tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 */