Home | History | Annotate | Download | only in src
      1 /*
      2  * Copyright  2009  Red Hat, Inc.
      3  *
      4  *  This is part of HarfBuzz, a text shaping library.
      5  *
      6  * Permission is hereby granted, without written agreement and without
      7  * license or royalty fees, to use, copy, modify, and distribute this
      8  * software and its documentation for any purpose, provided that the
      9  * above copyright notice and the following two paragraphs appear in
     10  * all copies of this software.
     11  *
     12  * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
     13  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
     14  * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
     15  * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
     16  * DAMAGE.
     17  *
     18  * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
     19  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
     20  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
     21  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
     22  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
     23  *
     24  * Red Hat Author(s): Behdad Esfahbod
     25  */
     26 
     27 /* http://www.oracle.com/technetwork/articles/servers-storage-dev/standardheaderfiles-453865.html */
     28 #ifndef _POSIX_C_SOURCE
     29 #define _POSIX_C_SOURCE 199309L
     30 #endif
     31 
     32 #include "hb-private.hh"
     33 #include "hb-debug.hh"
     34 
     35 #include "hb-object-private.hh"
     36 
     37 #ifdef HAVE_SYS_MMAN_H
     38 #ifdef HAVE_UNISTD_H
     39 #include <unistd.h>
     40 #endif /* HAVE_UNISTD_H */
     41 #include <sys/mman.h>
     42 #endif /* HAVE_SYS_MMAN_H */
     43 
     44 #include <stdio.h>
     45 #include <errno.h>
     46 
     47 
     48 struct hb_blob_t {
     49   hb_object_header_t header;
     50   ASSERT_POD ();
     51 
     52   bool immutable;
     53 
     54   const char *data;
     55   unsigned int length;
     56   hb_memory_mode_t mode;
     57 
     58   void *user_data;
     59   hb_destroy_func_t destroy;
     60 };
     61 
     62 
     63 static bool _try_writable (hb_blob_t *blob);
     64 
     65 static void
     66 _hb_blob_destroy_user_data (hb_blob_t *blob)
     67 {
     68   if (blob->destroy) {
     69     blob->destroy (blob->user_data);
     70     blob->user_data = nullptr;
     71     blob->destroy = nullptr;
     72   }
     73 }
     74 
     75 /**
     76  * hb_blob_create: (skip)
     77  * @data: Pointer to blob data.
     78  * @length: Length of @data in bytes.
     79  * @mode: Memory mode for @data.
     80  * @user_data: Data parameter to pass to @destroy.
     81  * @destroy: Callback to call when @data is not needed anymore.
     82  *
     83  * Creates a new "blob" object wrapping @data.  The @mode parameter is used
     84  * to negotiate ownership and lifecycle of @data.
     85  *
     86  * Return value: New blob, or the empty blob if something failed or if @length is
     87  * zero.  Destroy with hb_blob_destroy().
     88  *
     89  * Since: 0.9.2
     90  **/
     91 hb_blob_t *
     92 hb_blob_create (const char        *data,
     93 		unsigned int       length,
     94 		hb_memory_mode_t   mode,
     95 		void              *user_data,
     96 		hb_destroy_func_t  destroy)
     97 {
     98   hb_blob_t *blob;
     99 
    100   if (!length ||
    101       length >= 1u << 31 ||
    102       !(blob = hb_object_create<hb_blob_t> ())) {
    103     if (destroy)
    104       destroy (user_data);
    105     return hb_blob_get_empty ();
    106   }
    107 
    108   blob->data = data;
    109   blob->length = length;
    110   blob->mode = mode;
    111 
    112   blob->user_data = user_data;
    113   blob->destroy = destroy;
    114 
    115   if (blob->mode == HB_MEMORY_MODE_DUPLICATE) {
    116     blob->mode = HB_MEMORY_MODE_READONLY;
    117     if (!_try_writable (blob)) {
    118       hb_blob_destroy (blob);
    119       return hb_blob_get_empty ();
    120     }
    121   }
    122 
    123   return blob;
    124 }
    125 
    126 static void
    127 _hb_blob_destroy (void *data)
    128 {
    129   hb_blob_destroy ((hb_blob_t *) data);
    130 }
    131 
    132 /**
    133  * hb_blob_create_sub_blob:
    134  * @parent: Parent blob.
    135  * @offset: Start offset of sub-blob within @parent, in bytes.
    136  * @length: Length of sub-blob.
    137  *
    138  * Returns a blob that represents a range of bytes in @parent.  The new
    139  * blob is always created with %HB_MEMORY_MODE_READONLY, meaning that it
    140  * will never modify data in the parent blob.  The parent data is not
    141  * expected to be modified, and will result in undefined behavior if it
    142  * is.
    143  *
    144  * Makes @parent immutable.
    145  *
    146  * Return value: New blob, or the empty blob if something failed or if
    147  * @length is zero or @offset is beyond the end of @parent's data.  Destroy
    148  * with hb_blob_destroy().
    149  *
    150  * Since: 0.9.2
    151  **/
    152 hb_blob_t *
    153 hb_blob_create_sub_blob (hb_blob_t    *parent,
    154 			 unsigned int  offset,
    155 			 unsigned int  length)
    156 {
    157   hb_blob_t *blob;
    158 
    159   if (!length || offset >= parent->length)
    160     return hb_blob_get_empty ();
    161 
    162   hb_blob_make_immutable (parent);
    163 
    164   blob = hb_blob_create (parent->data + offset,
    165 			 MIN (length, parent->length - offset),
    166 			 HB_MEMORY_MODE_READONLY,
    167 			 hb_blob_reference (parent),
    168 			 _hb_blob_destroy);
    169 
    170   return blob;
    171 }
    172 
    173 /**
    174  * hb_blob_get_empty:
    175  *
    176  * Returns the singleton empty blob.
    177  *
    178  * See TODO:link object types for more information.
    179  *
    180  * Return value: (transfer full): the empty blob.
    181  *
    182  * Since: 0.9.2
    183  **/
    184 hb_blob_t *
    185 hb_blob_get_empty (void)
    186 {
    187   static const hb_blob_t _hb_blob_nil = {
    188     HB_OBJECT_HEADER_STATIC,
    189 
    190     true, /* immutable */
    191 
    192     nullptr, /* data */
    193     0, /* length */
    194     HB_MEMORY_MODE_READONLY, /* mode */
    195 
    196     nullptr, /* user_data */
    197     nullptr  /* destroy */
    198   };
    199 
    200   return const_cast<hb_blob_t *> (&_hb_blob_nil);
    201 }
    202 
    203 /**
    204  * hb_blob_reference: (skip)
    205  * @blob: a blob.
    206  *
    207  * Increases the reference count on @blob.
    208  *
    209  * See TODO:link object types for more information.
    210  *
    211  * Return value: @blob.
    212  *
    213  * Since: 0.9.2
    214  **/
    215 hb_blob_t *
    216 hb_blob_reference (hb_blob_t *blob)
    217 {
    218   return hb_object_reference (blob);
    219 }
    220 
    221 /**
    222  * hb_blob_destroy: (skip)
    223  * @blob: a blob.
    224  *
    225  * Descreases the reference count on @blob, and if it reaches zero, destroys
    226  * @blob, freeing all memory, possibly calling the destroy-callback the blob
    227  * was created for if it has not been called already.
    228  *
    229  * See TODO:link object types for more information.
    230  *
    231  * Since: 0.9.2
    232  **/
    233 void
    234 hb_blob_destroy (hb_blob_t *blob)
    235 {
    236   if (!hb_object_destroy (blob)) return;
    237 
    238   _hb_blob_destroy_user_data (blob);
    239 
    240   free (blob);
    241 }
    242 
    243 /**
    244  * hb_blob_set_user_data: (skip)
    245  * @blob: a blob.
    246  * @key: key for data to set.
    247  * @data: data to set.
    248  * @destroy: callback to call when @data is not needed anymore.
    249  * @replace: whether to replace an existing data with the same key.
    250  *
    251  * Return value:
    252  *
    253  * Since: 0.9.2
    254  **/
    255 hb_bool_t
    256 hb_blob_set_user_data (hb_blob_t          *blob,
    257 		       hb_user_data_key_t *key,
    258 		       void *              data,
    259 		       hb_destroy_func_t   destroy,
    260 		       hb_bool_t           replace)
    261 {
    262   return hb_object_set_user_data (blob, key, data, destroy, replace);
    263 }
    264 
    265 /**
    266  * hb_blob_get_user_data: (skip)
    267  * @blob: a blob.
    268  * @key: key for data to get.
    269  *
    270  *
    271  *
    272  * Return value: (transfer none):
    273  *
    274  * Since: 0.9.2
    275  **/
    276 void *
    277 hb_blob_get_user_data (hb_blob_t          *blob,
    278 		       hb_user_data_key_t *key)
    279 {
    280   return hb_object_get_user_data (blob, key);
    281 }
    282 
    283 
    284 /**
    285  * hb_blob_make_immutable:
    286  * @blob: a blob.
    287  *
    288  *
    289  *
    290  * Since: 0.9.2
    291  **/
    292 void
    293 hb_blob_make_immutable (hb_blob_t *blob)
    294 {
    295   if (hb_object_is_inert (blob))
    296     return;
    297 
    298   blob->immutable = true;
    299 }
    300 
    301 /**
    302  * hb_blob_is_immutable:
    303  * @blob: a blob.
    304  *
    305  *
    306  *
    307  * Return value: TODO
    308  *
    309  * Since: 0.9.2
    310  **/
    311 hb_bool_t
    312 hb_blob_is_immutable (hb_blob_t *blob)
    313 {
    314   return blob->immutable;
    315 }
    316 
    317 
    318 /**
    319  * hb_blob_get_length:
    320  * @blob: a blob.
    321  *
    322  *
    323  *
    324  * Return value: the length of blob data in bytes.
    325  *
    326  * Since: 0.9.2
    327  **/
    328 unsigned int
    329 hb_blob_get_length (hb_blob_t *blob)
    330 {
    331   return blob->length;
    332 }
    333 
    334 /**
    335  * hb_blob_get_data:
    336  * @blob: a blob.
    337  * @length: (out):
    338  *
    339  *
    340  *
    341  * Returns: (transfer none) (array length=length):
    342  *
    343  * Since: 0.9.2
    344  **/
    345 const char *
    346 hb_blob_get_data (hb_blob_t *blob, unsigned int *length)
    347 {
    348   if (length)
    349     *length = blob->length;
    350 
    351   return blob->data;
    352 }
    353 
    354 /**
    355  * hb_blob_get_data_writable:
    356  * @blob: a blob.
    357  * @length: (out): output length of the writable data.
    358  *
    359  * Tries to make blob data writable (possibly copying it) and
    360  * return pointer to data.
    361  *
    362  * Fails if blob has been made immutable, or if memory allocation
    363  * fails.
    364  *
    365  * Returns: (transfer none) (array length=length): Writable blob data,
    366  * or %NULL if failed.
    367  *
    368  * Since: 0.9.2
    369  **/
    370 char *
    371 hb_blob_get_data_writable (hb_blob_t *blob, unsigned int *length)
    372 {
    373   if (!_try_writable (blob)) {
    374     if (length)
    375       *length = 0;
    376 
    377     return nullptr;
    378   }
    379 
    380   if (length)
    381     *length = blob->length;
    382 
    383   return const_cast<char *> (blob->data);
    384 }
    385 
    386 
    387 static hb_bool_t
    388 _try_make_writable_inplace_unix (hb_blob_t *blob)
    389 {
    390 #if defined(HAVE_SYS_MMAN_H) && defined(HAVE_MPROTECT)
    391   uintptr_t pagesize = -1, mask, length;
    392   const char *addr;
    393 
    394 #if defined(HAVE_SYSCONF) && defined(_SC_PAGE_SIZE)
    395   pagesize = (uintptr_t) sysconf (_SC_PAGE_SIZE);
    396 #elif defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)
    397   pagesize = (uintptr_t) sysconf (_SC_PAGESIZE);
    398 #elif defined(HAVE_GETPAGESIZE)
    399   pagesize = (uintptr_t) getpagesize ();
    400 #endif
    401 
    402   if ((uintptr_t) -1L == pagesize) {
    403     DEBUG_MSG_FUNC (BLOB, blob, "failed to get pagesize: %s", strerror (errno));
    404     return false;
    405   }
    406   DEBUG_MSG_FUNC (BLOB, blob, "pagesize is %lu", (unsigned long) pagesize);
    407 
    408   mask = ~(pagesize-1);
    409   addr = (const char *) (((uintptr_t) blob->data) & mask);
    410   length = (const char *) (((uintptr_t) blob->data + blob->length + pagesize-1) & mask)  - addr;
    411   DEBUG_MSG_FUNC (BLOB, blob,
    412 		  "calling mprotect on [%p..%p] (%lu bytes)",
    413 		  addr, addr+length, (unsigned long) length);
    414   if (-1 == mprotect ((void *) addr, length, PROT_READ | PROT_WRITE)) {
    415     DEBUG_MSG_FUNC (BLOB, blob, "mprotect failed: %s", strerror (errno));
    416     return false;
    417   }
    418 
    419   blob->mode = HB_MEMORY_MODE_WRITABLE;
    420 
    421   DEBUG_MSG_FUNC (BLOB, blob,
    422 		  "successfully made [%p..%p] (%lu bytes) writable\n",
    423 		  addr, addr+length, (unsigned long) length);
    424   return true;
    425 #else
    426   return false;
    427 #endif
    428 }
    429 
    430 static bool
    431 _try_writable_inplace (hb_blob_t *blob)
    432 {
    433   DEBUG_MSG_FUNC (BLOB, blob, "making writable inplace\n");
    434 
    435   if (_try_make_writable_inplace_unix (blob))
    436     return true;
    437 
    438   DEBUG_MSG_FUNC (BLOB, blob, "making writable -> FAILED\n");
    439 
    440   /* Failed to make writable inplace, mark that */
    441   blob->mode = HB_MEMORY_MODE_READONLY;
    442   return false;
    443 }
    444 
    445 static bool
    446 _try_writable (hb_blob_t *blob)
    447 {
    448   if (blob->immutable)
    449     return false;
    450 
    451   if (blob->mode == HB_MEMORY_MODE_WRITABLE)
    452     return true;
    453 
    454   if (blob->mode == HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE && _try_writable_inplace (blob))
    455     return true;
    456 
    457   if (blob->mode == HB_MEMORY_MODE_WRITABLE)
    458     return true;
    459 
    460 
    461   DEBUG_MSG_FUNC (BLOB, blob, "current data is -> %p\n", blob->data);
    462 
    463   char *new_data;
    464 
    465   new_data = (char *) malloc (blob->length);
    466   if (unlikely (!new_data))
    467     return false;
    468 
    469   DEBUG_MSG_FUNC (BLOB, blob, "dupped successfully -> %p\n", blob->data);
    470 
    471   memcpy (new_data, blob->data, blob->length);
    472   _hb_blob_destroy_user_data (blob);
    473   blob->mode = HB_MEMORY_MODE_WRITABLE;
    474   blob->data = new_data;
    475   blob->user_data = new_data;
    476   blob->destroy = free;
    477 
    478   return true;
    479 }
    480