hb-blob.cc (20476B)
1 /* 2 * Copyright © 2009 Red Hat, Inc. 3 * Copyright © 2018 Ebrahim Byagowi 4 * 5 * This is part of HarfBuzz, a text shaping library. 6 * 7 * Permission is hereby granted, without written agreement and without 8 * license or royalty fees, to use, copy, modify, and distribute this 9 * software and its documentation for any purpose, provided that the 10 * above copyright notice and the following two paragraphs appear in 11 * all copies of this software. 12 * 13 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR 14 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 15 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN 16 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 17 * DAMAGE. 18 * 19 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, 20 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 21 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS 22 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO 23 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 24 * 25 * Red Hat Author(s): Behdad Esfahbod 26 */ 27 28 #include "hb.hh" 29 #include "hb-blob.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 39 /** 40 * SECTION: hb-blob 41 * @title: hb-blob 42 * @short_description: Binary data containers 43 * @include: hb.h 44 * 45 * Blobs wrap a chunk of binary data to handle lifecycle management of data 46 * while it is passed between client and HarfBuzz. Blobs are primarily used 47 * to create font faces, but also to access font face tables, as well as 48 * pass around other binary data. 49 **/ 50 51 52 /** 53 * hb_blob_create: (skip) 54 * @data: Pointer to blob data. 55 * @length: Length of @data in bytes. 56 * @mode: Memory mode for @data. 57 * @user_data: Data parameter to pass to @destroy. 58 * @destroy: (nullable): Callback to call when @data is not needed anymore. 59 * 60 * Creates a new "blob" object wrapping @data. The @mode parameter is used 61 * to negotiate ownership and lifecycle of @data. 62 * 63 * Return value: New blob, or the empty blob if something failed or if @length is 64 * zero. Destroy with hb_blob_destroy(). 65 * 66 * Since: 0.9.2 67 **/ 68 hb_blob_t * 69 hb_blob_create (const char *data, 70 unsigned int length, 71 hb_memory_mode_t mode, 72 void *user_data, 73 hb_destroy_func_t destroy) 74 { 75 if (!length) 76 { 77 if (destroy) 78 destroy (user_data); 79 return hb_blob_get_empty (); 80 } 81 82 hb_blob_t *blob = hb_blob_create_or_fail (data, length, mode, 83 user_data, destroy); 84 return likely (blob) ? blob : hb_blob_get_empty (); 85 } 86 87 /** 88 * hb_blob_create_or_fail: (skip) 89 * @data: Pointer to blob data. 90 * @length: Length of @data in bytes. 91 * @mode: Memory mode for @data. 92 * @user_data: Data parameter to pass to @destroy. 93 * @destroy: (nullable): Callback to call when @data is not needed anymore. 94 * 95 * Creates a new "blob" object wrapping @data. The @mode parameter is used 96 * to negotiate ownership and lifecycle of @data. 97 * 98 * Note that this function returns a freshly-allocated empty blob even if @length 99 * is zero. This is in contrast to hb_blob_create(), which returns the singleton 100 * empty blob (as returned by hb_blob_get_empty()) if @length is zero. 101 * 102 * Return value: New blob, or `NULL` if failed. Destroy with hb_blob_destroy(). 103 * 104 * Since: 2.8.2 105 **/ 106 hb_blob_t * 107 hb_blob_create_or_fail (const char *data, 108 unsigned int length, 109 hb_memory_mode_t mode, 110 void *user_data, 111 hb_destroy_func_t destroy) 112 { 113 hb_blob_t *blob; 114 115 if (length >= 1u << 31 || 116 !(blob = hb_object_create<hb_blob_t> ())) 117 { 118 if (destroy) 119 destroy (user_data); 120 return nullptr; 121 } 122 123 blob->data = data; 124 blob->length = length; 125 blob->mode = mode; 126 127 blob->user_data = user_data; 128 blob->destroy = destroy; 129 130 if (blob->mode == HB_MEMORY_MODE_DUPLICATE) { 131 blob->mode = HB_MEMORY_MODE_READONLY; 132 if (!blob->try_make_writable ()) 133 { 134 hb_blob_destroy (blob); 135 return nullptr; 136 } 137 } 138 139 return blob; 140 } 141 142 static void 143 _hb_blob_destroy (void *data) 144 { 145 hb_blob_destroy ((hb_blob_t *) data); 146 } 147 148 /** 149 * hb_blob_create_sub_blob: 150 * @parent: Parent blob. 151 * @offset: Start offset of sub-blob within @parent, in bytes. 152 * @length: Length of sub-blob. 153 * 154 * Returns a blob that represents a range of bytes in @parent. The new 155 * blob is always created with #HB_MEMORY_MODE_READONLY, meaning that it 156 * will never modify data in the parent blob. The parent data is not 157 * expected to be modified, and will result in undefined behavior if it 158 * is. 159 * 160 * Makes @parent immutable. 161 * 162 * Return value: New blob, or the empty blob if something failed or if 163 * @length is zero or @offset is beyond the end of @parent's data. Destroy 164 * with hb_blob_destroy(). 165 * 166 * Since: 0.9.2 167 **/ 168 hb_blob_t * 169 hb_blob_create_sub_blob (hb_blob_t *parent, 170 unsigned int offset, 171 unsigned int length) 172 { 173 hb_blob_t *blob; 174 175 if (!length || !parent || offset >= parent->length) 176 return hb_blob_get_empty (); 177 178 hb_blob_make_immutable (parent); 179 180 blob = hb_blob_create (parent->data + offset, 181 hb_min (length, parent->length - offset), 182 HB_MEMORY_MODE_READONLY, 183 hb_blob_reference (parent), 184 _hb_blob_destroy); 185 186 return blob; 187 } 188 189 /** 190 * hb_blob_copy_writable_or_fail: 191 * @blob: A blob. 192 * 193 * Makes a writable copy of @blob. 194 * 195 * Return value: The new blob, or nullptr if allocation failed 196 * 197 * Since: 1.8.0 198 **/ 199 hb_blob_t * 200 hb_blob_copy_writable_or_fail (hb_blob_t *blob) 201 { 202 blob = hb_blob_create (blob->data, 203 blob->length, 204 HB_MEMORY_MODE_DUPLICATE, 205 nullptr, 206 nullptr); 207 208 if (unlikely (blob == hb_blob_get_empty ())) 209 blob = nullptr; 210 211 return blob; 212 } 213 214 /** 215 * hb_blob_get_empty: 216 * 217 * Returns the singleton empty blob. 218 * 219 * See TODO:link object types for more information. 220 * 221 * Return value: (transfer full): The empty blob. 222 * 223 * Since: 0.9.2 224 **/ 225 hb_blob_t * 226 hb_blob_get_empty () 227 { 228 return const_cast<hb_blob_t *> (&Null (hb_blob_t)); 229 } 230 231 /** 232 * hb_blob_reference: (skip) 233 * @blob: a blob. 234 * 235 * Increases the reference count on @blob. 236 * 237 * See TODO:link object types for more information. 238 * 239 * Return value: @blob. 240 * 241 * Since: 0.9.2 242 **/ 243 hb_blob_t * 244 hb_blob_reference (hb_blob_t *blob) 245 { 246 return hb_object_reference (blob); 247 } 248 249 /** 250 * hb_blob_destroy: (skip) 251 * @blob: a blob. 252 * 253 * Decreases the reference count on @blob, and if it reaches zero, destroys 254 * @blob, freeing all memory, possibly calling the destroy-callback the blob 255 * was created for if it has not been called already. 256 * 257 * See TODO:link object types for more information. 258 * 259 * Since: 0.9.2 260 **/ 261 void 262 hb_blob_destroy (hb_blob_t *blob) 263 { 264 if (!hb_object_destroy (blob)) return; 265 266 hb_free (blob); 267 } 268 269 /** 270 * hb_blob_set_user_data: (skip) 271 * @blob: An #hb_blob_t 272 * @key: The user-data key to set 273 * @data: A pointer to the user data to set 274 * @destroy: (nullable): A callback to call when @data is not needed anymore 275 * @replace: Whether to replace an existing data with the same key 276 * 277 * Attaches a user-data key/data pair to the specified blob. 278 * 279 * Return value: `true` if success, `false` otherwise 280 * 281 * Since: 0.9.2 282 **/ 283 hb_bool_t 284 hb_blob_set_user_data (hb_blob_t *blob, 285 hb_user_data_key_t *key, 286 void * data, 287 hb_destroy_func_t destroy, 288 hb_bool_t replace) 289 { 290 return hb_object_set_user_data (blob, key, data, destroy, replace); 291 } 292 293 /** 294 * hb_blob_get_user_data: (skip) 295 * @blob: a blob 296 * @key: The user-data key to query 297 * 298 * Fetches the user data associated with the specified key, 299 * attached to the specified font-functions structure. 300 * 301 * Return value: (transfer none): A pointer to the user data 302 * 303 * Since: 0.9.2 304 **/ 305 void * 306 hb_blob_get_user_data (const hb_blob_t *blob, 307 hb_user_data_key_t *key) 308 { 309 return hb_object_get_user_data (blob, key); 310 } 311 312 313 /** 314 * hb_blob_make_immutable: 315 * @blob: a blob 316 * 317 * Makes a blob immutable. 318 * 319 * Since: 0.9.2 320 **/ 321 void 322 hb_blob_make_immutable (hb_blob_t *blob) 323 { 324 if (hb_object_is_immutable (blob)) 325 return; 326 327 hb_object_make_immutable (blob); 328 } 329 330 /** 331 * hb_blob_is_immutable: 332 * @blob: a blob. 333 * 334 * Tests whether a blob is immutable. 335 * 336 * Return value: `true` if @blob is immutable, `false` otherwise 337 * 338 * Since: 0.9.2 339 **/ 340 hb_bool_t 341 hb_blob_is_immutable (hb_blob_t *blob) 342 { 343 return hb_object_is_immutable (blob); 344 } 345 346 347 /** 348 * hb_blob_get_length: 349 * @blob: a blob. 350 * 351 * Fetches the length of a blob's data. 352 * 353 * Return value: the length of @blob data in bytes. 354 * 355 * Since: 0.9.2 356 **/ 357 unsigned int 358 hb_blob_get_length (hb_blob_t *blob) 359 { 360 return blob->length; 361 } 362 363 /** 364 * hb_blob_get_data: 365 * @blob: a blob. 366 * @length: (out): The length in bytes of the data retrieved 367 * 368 * Fetches the data from a blob. 369 * 370 * Returns: (nullable) (transfer none) (array length=length): the byte data of @blob. 371 * 372 * Since: 0.9.2 373 **/ 374 const char * 375 hb_blob_get_data (hb_blob_t *blob, unsigned int *length) 376 { 377 if (length) 378 *length = blob->length; 379 380 return blob->data; 381 } 382 383 /** 384 * hb_blob_get_data_writable: 385 * @blob: a blob. 386 * @length: (out): output length of the writable data. 387 * 388 * Tries to make blob data writable (possibly copying it) and 389 * return pointer to data. 390 * 391 * Fails if blob has been made immutable, or if memory allocation 392 * fails. 393 * 394 * Returns: (transfer none) (array length=length): Writable blob data, 395 * or `NULL` if failed. 396 * 397 * Since: 0.9.2 398 **/ 399 char * 400 hb_blob_get_data_writable (hb_blob_t *blob, unsigned int *length) 401 { 402 if (hb_object_is_immutable (blob) || 403 !blob->try_make_writable ()) 404 { 405 if (length) *length = 0; 406 return nullptr; 407 } 408 409 if (length) *length = blob->length; 410 return const_cast<char *> (blob->data); 411 } 412 413 414 bool 415 hb_blob_t::try_make_writable_inplace_unix () 416 { 417 #if defined(HAVE_SYS_MMAN_H) && defined(HAVE_MPROTECT) 418 uintptr_t pagesize = -1, mask, length; 419 const char *addr; 420 421 #if defined(HAVE_SYSCONF) && defined(_SC_PAGE_SIZE) 422 pagesize = (uintptr_t) sysconf (_SC_PAGE_SIZE); 423 #elif defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE) 424 pagesize = (uintptr_t) sysconf (_SC_PAGESIZE); 425 #elif defined(HAVE_GETPAGESIZE) 426 pagesize = (uintptr_t) getpagesize (); 427 #endif 428 429 if ((uintptr_t) -1L == pagesize) { 430 DEBUG_MSG_FUNC (BLOB, this, "failed to get pagesize: %s", strerror (errno)); 431 return false; 432 } 433 DEBUG_MSG_FUNC (BLOB, this, "pagesize is %lu", (unsigned long) pagesize); 434 435 mask = ~(pagesize-1); 436 addr = (const char *) (((uintptr_t) this->data) & mask); 437 length = (const char *) (((uintptr_t) this->data + this->length + pagesize-1) & mask) - addr; 438 DEBUG_MSG_FUNC (BLOB, this, 439 "calling mprotect on [%p..%p] (%lu bytes)", 440 addr, addr+length, (unsigned long) length); 441 if (-1 == mprotect ((void *) addr, length, PROT_READ | PROT_WRITE)) { 442 DEBUG_MSG_FUNC (BLOB, this, "mprotect failed: %s", strerror (errno)); 443 return false; 444 } 445 446 this->mode = HB_MEMORY_MODE_WRITABLE; 447 448 DEBUG_MSG_FUNC (BLOB, this, 449 "successfully made [%p..%p] (%lu bytes) writable\n", 450 addr, addr+length, (unsigned long) length); 451 return true; 452 #else 453 return false; 454 #endif 455 } 456 457 bool 458 hb_blob_t::try_make_writable_inplace () 459 { 460 DEBUG_MSG_FUNC (BLOB, this, "making writable inplace\n"); 461 462 if (this->try_make_writable_inplace_unix ()) 463 return true; 464 465 DEBUG_MSG_FUNC (BLOB, this, "making writable -> FAILED\n"); 466 467 /* Failed to make writable inplace, mark that */ 468 this->mode = HB_MEMORY_MODE_READONLY; 469 return false; 470 } 471 472 bool 473 hb_blob_t::try_make_writable () 474 { 475 if (unlikely (!length)) 476 mode = HB_MEMORY_MODE_WRITABLE; 477 478 if (this->mode == HB_MEMORY_MODE_WRITABLE) 479 return true; 480 481 if (this->mode == HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE && this->try_make_writable_inplace ()) 482 return true; 483 484 if (this->mode == HB_MEMORY_MODE_WRITABLE) 485 return true; 486 487 488 DEBUG_MSG_FUNC (BLOB, this, "current data is -> %p\n", this->data); 489 490 char *new_data; 491 492 new_data = (char *) hb_malloc (this->length); 493 if (unlikely (!new_data)) 494 return false; 495 496 DEBUG_MSG_FUNC (BLOB, this, "dupped successfully -> %p\n", this->data); 497 498 hb_memcpy (new_data, this->data, this->length); 499 this->destroy_user_data (); 500 this->mode = HB_MEMORY_MODE_WRITABLE; 501 this->data = new_data; 502 this->user_data = new_data; 503 this->destroy = hb_free; 504 505 return true; 506 } 507 508 /* 509 * Mmap 510 */ 511 512 #ifndef HB_NO_OPEN 513 #ifdef HAVE_MMAP 514 # if !defined(HB_NO_RESOURCE_FORK) && defined(__APPLE__) 515 # include <sys/paths.h> 516 # endif 517 # include <sys/types.h> 518 # include <sys/stat.h> 519 # include <fcntl.h> 520 #endif 521 522 #ifdef _WIN32 523 # include <windows.h> 524 #else 525 # ifndef O_BINARY 526 # define O_BINARY 0 527 # endif 528 #endif 529 530 #ifndef MAP_NORESERVE 531 # define MAP_NORESERVE 0 532 #endif 533 534 struct hb_mapped_file_t 535 { 536 char *contents; 537 unsigned long length; 538 #ifdef _WIN32 539 HANDLE mapping; 540 #endif 541 }; 542 543 #if (defined(HAVE_MMAP) || defined(_WIN32)) && !defined(HB_NO_MMAP) 544 static void 545 _hb_mapped_file_destroy (void *file_) 546 { 547 hb_mapped_file_t *file = (hb_mapped_file_t *) file_; 548 #ifdef HAVE_MMAP 549 munmap (file->contents, file->length); 550 #elif defined(_WIN32) 551 UnmapViewOfFile (file->contents); 552 CloseHandle (file->mapping); 553 #else 554 assert (0); // If we don't have mmap we shouldn't reach here 555 #endif 556 557 hb_free (file); 558 } 559 #endif 560 561 #ifdef _PATH_RSRCFORKSPEC 562 static int 563 _open_resource_fork (const char *file_name, hb_mapped_file_t *file) 564 { 565 size_t name_len = strlen (file_name); 566 size_t len = name_len + sizeof (_PATH_RSRCFORKSPEC); 567 568 char *rsrc_name = (char *) hb_malloc (len); 569 if (unlikely (!rsrc_name)) return -1; 570 571 strncpy (rsrc_name, file_name, name_len); 572 strncpy (rsrc_name + name_len, _PATH_RSRCFORKSPEC, 573 sizeof (_PATH_RSRCFORKSPEC)); 574 575 int fd = open (rsrc_name, O_RDONLY | O_BINARY, 0); 576 hb_free (rsrc_name); 577 578 if (fd != -1) 579 { 580 struct stat st; 581 if (fstat (fd, &st) != -1) 582 file->length = (unsigned long) st.st_size; 583 else 584 { 585 close (fd); 586 fd = -1; 587 } 588 } 589 590 return fd; 591 } 592 #endif 593 594 /** 595 * hb_blob_create_from_file: 596 * @file_name: A font filename 597 * 598 * Creates a new blob containing the data from the 599 * specified binary font file. 600 * 601 * The filename is passed directly to the system on all platforms, 602 * except on Windows, where the filename is interpreted as UTF-8. 603 * Only if the filename is not valid UTF-8, it will be interpreted 604 * according to the system codepage. 605 * 606 * Returns: An #hb_blob_t pointer with the content of the file, 607 * or hb_blob_get_empty() if failed. 608 * 609 * Since: 1.7.7 610 **/ 611 hb_blob_t * 612 hb_blob_create_from_file (const char *file_name) 613 { 614 hb_blob_t *blob = hb_blob_create_from_file_or_fail (file_name); 615 return likely (blob) ? blob : hb_blob_get_empty (); 616 } 617 618 /** 619 * hb_blob_create_from_file_or_fail: 620 * @file_name: A filename 621 * 622 * Creates a new blob containing the data from the specified file. 623 * 624 * The filename is passed directly to the system on all platforms, 625 * except on Windows, where the filename is interpreted as UTF-8. 626 * Only if the filename is not valid UTF-8, it will be interpreted 627 * according to the system codepage. 628 * 629 * Returns: An #hb_blob_t pointer with the content of the file, 630 * or `NULL` if failed. 631 * 632 * Since: 2.8.2 633 **/ 634 hb_blob_t * 635 hb_blob_create_from_file_or_fail (const char *file_name) 636 { 637 /* Adopted from glib's gmappedfile.c with Matthias Clasen and 638 Allison Lortie permission but changed a lot to suit our need. */ 639 #if defined(HAVE_MMAP) && !defined(HB_NO_MMAP) 640 hb_mapped_file_t *file = (hb_mapped_file_t *) hb_calloc (1, sizeof (hb_mapped_file_t)); 641 if (unlikely (!file)) return nullptr; 642 643 int fd = open (file_name, O_RDONLY | O_BINARY, 0); 644 if (unlikely (fd == -1)) goto fail_without_close; 645 646 struct stat st; 647 if (unlikely (fstat (fd, &st) == -1)) goto fail; 648 649 file->length = (unsigned long) st.st_size; 650 651 #ifdef _PATH_RSRCFORKSPEC 652 if (unlikely (file->length == 0)) 653 { 654 int rfd = _open_resource_fork (file_name, file); 655 if (rfd != -1) 656 { 657 close (fd); 658 fd = rfd; 659 } 660 } 661 #endif 662 663 file->contents = (char *) mmap (nullptr, file->length, PROT_READ, 664 MAP_PRIVATE | MAP_NORESERVE, fd, 0); 665 666 if (unlikely (file->contents == MAP_FAILED)) goto fail; 667 668 close (fd); 669 670 return hb_blob_create_or_fail (file->contents, file->length, 671 HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE, (void *) file, 672 (hb_destroy_func_t) _hb_mapped_file_destroy); 673 674 fail: 675 close (fd); 676 fail_without_close: 677 hb_free (file); 678 679 #elif defined(_WIN32) && !defined(HB_NO_MMAP) 680 hb_mapped_file_t *file = (hb_mapped_file_t *) hb_calloc (1, sizeof (hb_mapped_file_t)); 681 if (unlikely (!file)) return nullptr; 682 683 HANDLE fd; 684 int conversion; 685 unsigned int size = strlen (file_name) + 1; 686 wchar_t * wchar_file_name = (wchar_t *) hb_malloc (sizeof (wchar_t) * size); 687 if (unlikely (!wchar_file_name)) goto fail_without_close; 688 689 /* Assume file name is given in UTF-8 encoding */ 690 conversion = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, file_name, -1, wchar_file_name, size); 691 if (conversion <= 0) 692 { 693 /* Conversion failed due to invalid UTF-8 characters, 694 Repeat conversion based on system code page */ 695 mbstowcs(wchar_file_name, file_name, size); 696 } 697 #if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) 698 { 699 CREATEFILE2_EXTENDED_PARAMETERS ceparams = { 0 }; 700 ceparams.dwSize = sizeof(CREATEFILE2_EXTENDED_PARAMETERS); 701 ceparams.dwFileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED & 0xFFFF; 702 ceparams.dwFileFlags = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED & 0xFFF00000; 703 ceparams.dwSecurityQosFlags = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED & 0x000F0000; 704 ceparams.lpSecurityAttributes = nullptr; 705 ceparams.hTemplateFile = nullptr; 706 fd = CreateFile2 (wchar_file_name, GENERIC_READ, FILE_SHARE_READ, 707 OPEN_EXISTING, &ceparams); 708 } 709 #else 710 fd = CreateFileW (wchar_file_name, GENERIC_READ, FILE_SHARE_READ, nullptr, 711 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED, 712 nullptr); 713 #endif 714 hb_free (wchar_file_name); 715 716 if (unlikely (fd == INVALID_HANDLE_VALUE)) goto fail_without_close; 717 718 #if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) 719 { 720 LARGE_INTEGER length; 721 GetFileSizeEx (fd, &length); 722 file->length = length.LowPart; 723 file->mapping = CreateFileMappingFromApp (fd, nullptr, PAGE_READONLY, length.QuadPart, nullptr); 724 } 725 #else 726 file->length = (unsigned long) GetFileSize (fd, nullptr); 727 file->mapping = CreateFileMapping (fd, nullptr, PAGE_READONLY, 0, 0, nullptr); 728 #endif 729 if (unlikely (!file->mapping)) goto fail; 730 731 #if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) 732 file->contents = (char *) MapViewOfFileFromApp (file->mapping, FILE_MAP_READ, 0, 0); 733 #else 734 file->contents = (char *) MapViewOfFile (file->mapping, FILE_MAP_READ, 0, 0, 0); 735 #endif 736 if (unlikely (!file->contents)) goto fail; 737 738 CloseHandle (fd); 739 return hb_blob_create_or_fail (file->contents, file->length, 740 HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE, (void *) file, 741 (hb_destroy_func_t) _hb_mapped_file_destroy); 742 743 fail: 744 CloseHandle (fd); 745 fail_without_close: 746 hb_free (file); 747 748 #endif 749 750 /* The following tries to read a file without knowing its size beforehand 751 It's used as a fallback for systems without mmap or to read from pipes */ 752 unsigned long len = 0, allocated = BUFSIZ * 16; 753 char *data = (char *) hb_malloc (allocated); 754 if (unlikely (!data)) return nullptr; 755 756 FILE *fp = fopen (file_name, "rb"); 757 if (unlikely (!fp)) goto fread_fail_without_close; 758 759 while (!feof (fp)) 760 { 761 if (allocated - len < BUFSIZ) 762 { 763 allocated *= 2; 764 /* Don't allocate and go more than ~536MB, our mmap reader still 765 can cover files like that but lets limit our fallback reader */ 766 if (unlikely (allocated > (2 << 28))) goto fread_fail; 767 char *new_data = (char *) hb_realloc (data, allocated); 768 if (unlikely (!new_data)) goto fread_fail; 769 data = new_data; 770 } 771 772 unsigned long addition = fread (data + len, 1, allocated - len, fp); 773 774 int err = ferror (fp); 775 #ifdef EINTR // armcc doesn't have it 776 if (unlikely (err == EINTR)) continue; 777 #endif 778 if (unlikely (err)) goto fread_fail; 779 780 len += addition; 781 } 782 fclose (fp); 783 784 return hb_blob_create_or_fail (data, len, HB_MEMORY_MODE_WRITABLE, data, 785 (hb_destroy_func_t) hb_free); 786 787 fread_fail: 788 fclose (fp); 789 fread_fail_without_close: 790 hb_free (data); 791 return nullptr; 792 } 793 #endif /* !HB_NO_OPEN */