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