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