hb-blob.cc revision 13240:8c09472c3de2
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 34#include "hb-object-private.hh" 35 36#ifdef HAVE_SYS_MMAN_H 37#ifdef HAVE_UNISTD_H 38#include <unistd.h> 39#endif /* HAVE_UNISTD_H */ 40#include <sys/mman.h> 41#endif /* HAVE_SYS_MMAN_H */ 42 43#include <stdio.h> 44#include <errno.h> 45 46 47 48#ifndef HB_DEBUG_BLOB 49#define HB_DEBUG_BLOB (HB_DEBUG+0) 50#endif 51 52 53struct hb_blob_t { 54 hb_object_header_t header; 55 ASSERT_POD (); 56 57 bool immutable; 58 59 const char *data; 60 unsigned int length; 61 hb_memory_mode_t mode; 62 63 void *user_data; 64 hb_destroy_func_t destroy; 65}; 66 67 68static bool _try_writable (hb_blob_t *blob); 69 70static void 71_hb_blob_destroy_user_data (hb_blob_t *blob) 72{ 73 if (blob->destroy) { 74 blob->destroy (blob->user_data); 75 blob->user_data = NULL; 76 blob->destroy = NULL; 77 } 78} 79 80/** 81 * hb_blob_create: (skip) 82 * @data: Pointer to blob data. 83 * @length: Length of @data in bytes. 84 * @mode: Memory mode for @data. 85 * @user_data: Data parameter to pass to @destroy. 86 * @destroy: Callback to call when @data is not needed anymore. 87 * 88 * Creates a new "blob" object wrapping @data. The @mode parameter is used 89 * to negotiate ownership and lifecycle of @data. 90 * 91 * Return value: New blob, or the empty blob if something failed or if @length is 92 * zero. Destroy with hb_blob_destroy(). 93 * 94 * Since: 0.9.2 95 **/ 96hb_blob_t * 97hb_blob_create (const char *data, 98 unsigned int length, 99 hb_memory_mode_t mode, 100 void *user_data, 101 hb_destroy_func_t destroy) 102{ 103 hb_blob_t *blob; 104 105 if (!length || 106 length >= 1u << 31 || 107 data + length < data /* overflows */ || 108 !(blob = hb_object_create<hb_blob_t> ())) { 109 if (destroy) 110 destroy (user_data); 111 return hb_blob_get_empty (); 112 } 113 114 blob->data = data; 115 blob->length = length; 116 blob->mode = mode; 117 118 blob->user_data = user_data; 119 blob->destroy = destroy; 120 121 if (blob->mode == HB_MEMORY_MODE_DUPLICATE) { 122 blob->mode = HB_MEMORY_MODE_READONLY; 123 if (!_try_writable (blob)) { 124 hb_blob_destroy (blob); 125 return hb_blob_get_empty (); 126 } 127 } 128 129 return blob; 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 **/ 152hb_blob_t * 153hb_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_destroy_func_t) 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 **/ 184hb_blob_t * 185hb_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 NULL, /* data */ 193 0, /* length */ 194 HB_MEMORY_MODE_READONLY, /* mode */ 195 196 NULL, /* user_data */ 197 NULL /* 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 **/ 215hb_blob_t * 216hb_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 **/ 233void 234hb_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 **/ 255hb_bool_t 256hb_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 **/ 276void * 277hb_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 **/ 292void 293hb_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 **/ 311hb_bool_t 312hb_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 **/ 328unsigned int 329hb_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 **/ 345const char * 346hb_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 **/ 370char * 371hb_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 NULL; 378 } 379 380 if (length) 381 *length = blob->length; 382 383 return const_cast<char *> (blob->data); 384} 385 386 387static 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 430static 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 445static 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