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 #define _POSIX_C_SOURCE 199309L
     29 
     30 #include "hb-private.hh"
     31 
     32 #include "hb-blob.h"
     33 #include "hb-object-private.hh"
     34 
     35 #ifdef HAVE_SYS_MMAN_H
     36 #ifdef HAVE_UNISTD_H
     37 #include <unistd.h>
     38 #endif /* HAVE_UNISTD_H */
     39 #include <sys/mman.h>
     40 #endif /* HAVE_SYS_MMAN_H */
     41 
     42 #include <stdio.h>
     43 #include <errno.h>
     44 
     45 
     46 
     47 #ifndef HB_DEBUG_BLOB
     48 #define HB_DEBUG_BLOB (HB_DEBUG+0)
     49 #endif
     50 
     51 
     52 struct hb_blob_t {
     53   hb_object_header_t header;
     54   ASSERT_POD ();
     55 
     56   bool immutable;
     57 
     58   const char *data;
     59   unsigned int length;
     60   hb_memory_mode_t mode;
     61 
     62   void *user_data;
     63   hb_destroy_func_t destroy;
     64 };
     65 
     66 
     67 static bool _try_writable (hb_blob_t *blob);
     68 
     69 static void
     70 _hb_blob_destroy_user_data (hb_blob_t *blob)
     71 {
     72   if (blob->destroy) {
     73     blob->destroy (blob->user_data);
     74     blob->user_data = NULL;
     75     blob->destroy = NULL;
     76   }
     77 }
     78 
     79 hb_blob_t *
     80 hb_blob_create (const char        *data,
     81 		unsigned int       length,
     82 		hb_memory_mode_t   mode,
     83 		void              *user_data,
     84 		hb_destroy_func_t  destroy)
     85 {
     86   hb_blob_t *blob;
     87 
     88   if (!length || !(blob = hb_object_create<hb_blob_t> ())) {
     89     if (destroy)
     90       destroy (user_data);
     91     return hb_blob_get_empty ();
     92   }
     93 
     94   blob->data = data;
     95   blob->length = length;
     96   blob->mode = mode;
     97 
     98   blob->user_data = user_data;
     99   blob->destroy = destroy;
    100 
    101   if (blob->mode == HB_MEMORY_MODE_DUPLICATE) {
    102     blob->mode = HB_MEMORY_MODE_READONLY;
    103     if (!_try_writable (blob)) {
    104       hb_blob_destroy (blob);
    105       return hb_blob_get_empty ();
    106     }
    107   }
    108 
    109   return blob;
    110 }
    111 
    112 hb_blob_t *
    113 hb_blob_create_sub_blob (hb_blob_t    *parent,
    114 			 unsigned int  offset,
    115 			 unsigned int  length)
    116 {
    117   hb_blob_t *blob;
    118 
    119   if (!length || offset >= parent->length)
    120     return hb_blob_get_empty ();
    121 
    122   hb_blob_make_immutable (parent);
    123 
    124   blob = hb_blob_create (parent->data + offset,
    125 			 MIN (length, parent->length - offset),
    126 			 HB_MEMORY_MODE_READONLY,
    127 			 hb_blob_reference (parent),
    128 			 (hb_destroy_func_t) hb_blob_destroy);
    129 
    130   return blob;
    131 }
    132 
    133 hb_blob_t *
    134 hb_blob_get_empty (void)
    135 {
    136   static const hb_blob_t _hb_blob_nil = {
    137     HB_OBJECT_HEADER_STATIC,
    138 
    139     true, /* immutable */
    140 
    141     NULL, /* data */
    142     0, /* length */
    143     HB_MEMORY_MODE_READONLY, /* mode */
    144 
    145     NULL, /* user_data */
    146     NULL  /* destroy */
    147   };
    148 
    149   return const_cast<hb_blob_t *> (&_hb_blob_nil);
    150 }
    151 
    152 hb_blob_t *
    153 hb_blob_reference (hb_blob_t *blob)
    154 {
    155   return hb_object_reference (blob);
    156 }
    157 
    158 void
    159 hb_blob_destroy (hb_blob_t *blob)
    160 {
    161   if (!hb_object_destroy (blob)) return;
    162 
    163   _hb_blob_destroy_user_data (blob);
    164 
    165   free (blob);
    166 }
    167 
    168 hb_bool_t
    169 hb_blob_set_user_data (hb_blob_t          *blob,
    170 		       hb_user_data_key_t *key,
    171 		       void *              data,
    172 		       hb_destroy_func_t   destroy,
    173 		       hb_bool_t           replace)
    174 {
    175   return hb_object_set_user_data (blob, key, data, destroy, replace);
    176 }
    177 
    178 void *
    179 hb_blob_get_user_data (hb_blob_t          *blob,
    180 		       hb_user_data_key_t *key)
    181 {
    182   return hb_object_get_user_data (blob, key);
    183 }
    184 
    185 
    186 void
    187 hb_blob_make_immutable (hb_blob_t *blob)
    188 {
    189   if (hb_object_is_inert (blob))
    190     return;
    191 
    192   blob->immutable = true;
    193 }
    194 
    195 hb_bool_t
    196 hb_blob_is_immutable (hb_blob_t *blob)
    197 {
    198   return blob->immutable;
    199 }
    200 
    201 
    202 unsigned int
    203 hb_blob_get_length (hb_blob_t *blob)
    204 {
    205   return blob->length;
    206 }
    207 
    208 const char *
    209 hb_blob_get_data (hb_blob_t *blob, unsigned int *length)
    210 {
    211   if (length)
    212     *length = blob->length;
    213 
    214   return blob->data;
    215 }
    216 
    217 char *
    218 hb_blob_get_data_writable (hb_blob_t *blob, unsigned int *length)
    219 {
    220   if (!_try_writable (blob)) {
    221     if (length)
    222       *length = 0;
    223 
    224     return NULL;
    225   }
    226 
    227   if (length)
    228     *length = blob->length;
    229 
    230   return const_cast<char *> (blob->data);
    231 }
    232 
    233 
    234 static hb_bool_t
    235 _try_make_writable_inplace_unix (hb_blob_t *blob)
    236 {
    237 #if defined(HAVE_SYS_MMAN_H) && defined(HAVE_MPROTECT)
    238   uintptr_t pagesize = -1, mask, length;
    239   const char *addr;
    240 
    241 #if defined(HAVE_SYSCONF) && defined(_SC_PAGE_SIZE)
    242   pagesize = (uintptr_t) sysconf (_SC_PAGE_SIZE);
    243 #elif defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)
    244   pagesize = (uintptr_t) sysconf (_SC_PAGESIZE);
    245 #elif defined(HAVE_GETPAGESIZE)
    246   pagesize = (uintptr_t) getpagesize ();
    247 #endif
    248 
    249   if ((uintptr_t) -1L == pagesize) {
    250     DEBUG_MSG_FUNC (BLOB, blob, "failed to get pagesize: %s", strerror (errno));
    251     return false;
    252   }
    253   DEBUG_MSG_FUNC (BLOB, blob, "pagesize is %lu", (unsigned long) pagesize);
    254 
    255   mask = ~(pagesize-1);
    256   addr = (const char *) (((uintptr_t) blob->data) & mask);
    257   length = (const char *) (((uintptr_t) blob->data + blob->length + pagesize-1) & mask)  - addr;
    258   DEBUG_MSG_FUNC (BLOB, blob,
    259 		  "calling mprotect on [%p..%p] (%lu bytes)",
    260 		  addr, addr+length, (unsigned long) length);
    261   if (-1 == mprotect ((void *) addr, length, PROT_READ | PROT_WRITE)) {
    262     DEBUG_MSG_FUNC (BLOB, blob, "mprotect failed: %s", strerror (errno));
    263     return false;
    264   }
    265 
    266   blob->mode = HB_MEMORY_MODE_WRITABLE;
    267 
    268   DEBUG_MSG_FUNC (BLOB, blob,
    269 		  "successfully made [%p..%p] (%lu bytes) writable\n",
    270 		  addr, addr+length, (unsigned long) length);
    271   return true;
    272 #else
    273   return false;
    274 #endif
    275 }
    276 
    277 static bool
    278 _try_writable_inplace (hb_blob_t *blob)
    279 {
    280   DEBUG_MSG_FUNC (BLOB, blob, "making writable inplace\n");
    281 
    282   if (_try_make_writable_inplace_unix (blob))
    283     return true;
    284 
    285   DEBUG_MSG_FUNC (BLOB, blob, "making writable -> FAILED\n");
    286 
    287   /* Failed to make writable inplace, mark that */
    288   blob->mode = HB_MEMORY_MODE_READONLY;
    289   return false;
    290 }
    291 
    292 static bool
    293 _try_writable (hb_blob_t *blob)
    294 {
    295   if (blob->immutable)
    296     return false;
    297 
    298   if (blob->mode == HB_MEMORY_MODE_WRITABLE)
    299     return true;
    300 
    301   if (blob->mode == HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE && _try_writable_inplace (blob))
    302     return true;
    303 
    304   if (blob->mode == HB_MEMORY_MODE_WRITABLE)
    305     return true;
    306 
    307 
    308   DEBUG_MSG_FUNC (BLOB, blob, "current data is -> %p\n", blob->data);
    309 
    310   char *new_data;
    311 
    312   new_data = (char *) malloc (blob->length);
    313   if (unlikely (!new_data))
    314     return false;
    315 
    316   DEBUG_MSG_FUNC (BLOB, blob, "dupped successfully -> %p\n", blob->data);
    317 
    318   memcpy (new_data, blob->data, blob->length);
    319   _hb_blob_destroy_user_data (blob);
    320   blob->mode = HB_MEMORY_MODE_WRITABLE;
    321   blob->data = new_data;
    322   blob->user_data = new_data;
    323   blob->destroy = free;
    324 
    325   return true;
    326 }
    327 
    328 
    329