• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 *
hb_blob_create(const char * data,unsigned int length,hb_memory_mode_t mode,void * user_data,hb_destroy_func_t destroy)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 *
hb_blob_create_or_fail(const char * data,unsigned int length,hb_memory_mode_t mode,void * user_data,hb_destroy_func_t destroy)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
_hb_blob_destroy(void * data)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 *
hb_blob_create_sub_blob(hb_blob_t * parent,unsigned int offset,unsigned int length)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 *
hb_blob_copy_writable_or_fail(hb_blob_t * blob)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 *
hb_blob_get_empty()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 *
hb_blob_reference(hb_blob_t * blob)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
hb_blob_destroy(hb_blob_t * blob)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
hb_blob_set_user_data(hb_blob_t * blob,hb_user_data_key_t * key,void * data,hb_destroy_func_t destroy,hb_bool_t replace)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 *
hb_blob_get_user_data(const hb_blob_t * blob,hb_user_data_key_t * key)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
hb_blob_make_immutable(hb_blob_t * blob)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
hb_blob_is_immutable(hb_blob_t * blob)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
hb_blob_get_length(hb_blob_t * blob)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 *
hb_blob_get_data(hb_blob_t * blob,unsigned int * length)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 *
hb_blob_get_data_writable(hb_blob_t * blob,unsigned int * length)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
try_make_writable_inplace_unix()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
try_make_writable_inplace()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
try_make_writable()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
_hb_mapped_file_destroy(void * file_)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
_open_resource_fork(const char * file_name,hb_mapped_file_t * file)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  * Returns: An #hb_blob_t pointer with the content of the file,
602  * or hb_blob_get_empty() if failed.
603  *
604  * Since: 1.7.7
605  **/
606 hb_blob_t *
hb_blob_create_from_file(const char * file_name)607 hb_blob_create_from_file (const char *file_name)
608 {
609   hb_blob_t *blob = hb_blob_create_from_file_or_fail (file_name);
610   return likely (blob) ? blob : hb_blob_get_empty ();
611 }
612 
613 /**
614  * hb_blob_create_from_file_or_fail:
615  * @file_name: A font filename
616  *
617  * Creates a new blob containing the data from the
618  * specified binary font file.
619  *
620  * Returns: An #hb_blob_t pointer with the content of the file,
621  * or `NULL` if failed.
622  *
623  * Since: 2.8.2
624  **/
625 hb_blob_t *
hb_blob_create_from_file_or_fail(const char * file_name)626 hb_blob_create_from_file_or_fail (const char *file_name)
627 {
628   /* Adopted from glib's gmappedfile.c with Matthias Clasen and
629      Allison Lortie permission but changed a lot to suit our need. */
630 #if defined(HAVE_MMAP) && !defined(HB_NO_MMAP)
631   hb_mapped_file_t *file = (hb_mapped_file_t *) hb_calloc (1, sizeof (hb_mapped_file_t));
632   if (unlikely (!file)) return nullptr;
633 
634   int fd = open (file_name, O_RDONLY | O_BINARY, 0);
635   if (unlikely (fd == -1)) goto fail_without_close;
636 
637   struct stat st;
638   if (unlikely (fstat (fd, &st) == -1)) goto fail;
639 
640   file->length = (unsigned long) st.st_size;
641 
642 #ifdef _PATH_RSRCFORKSPEC
643   if (unlikely (file->length == 0))
644   {
645     int rfd = _open_resource_fork (file_name, file);
646     if (rfd != -1)
647     {
648       close (fd);
649       fd = rfd;
650     }
651   }
652 #endif
653 
654   file->contents = (char *) mmap (nullptr, file->length, PROT_READ,
655 				  MAP_PRIVATE | MAP_NORESERVE, fd, 0);
656 
657   if (unlikely (file->contents == MAP_FAILED)) goto fail;
658 
659   close (fd);
660 
661   return hb_blob_create_or_fail (file->contents, file->length,
662 				 HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE, (void *) file,
663 				 (hb_destroy_func_t) _hb_mapped_file_destroy);
664 
665 fail:
666   close (fd);
667 fail_without_close:
668   hb_free (file);
669 
670 #elif defined(_WIN32) && !defined(HB_NO_MMAP)
671   hb_mapped_file_t *file = (hb_mapped_file_t *) hb_calloc (1, sizeof (hb_mapped_file_t));
672   if (unlikely (!file)) return nullptr;
673 
674   HANDLE fd;
675   unsigned int size = strlen (file_name) + 1;
676   wchar_t * wchar_file_name = (wchar_t *) hb_malloc (sizeof (wchar_t) * size);
677   if (unlikely (!wchar_file_name)) goto fail_without_close;
678   mbstowcs (wchar_file_name, file_name, size);
679 #if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
680   {
681     CREATEFILE2_EXTENDED_PARAMETERS ceparams = { 0 };
682     ceparams.dwSize = sizeof(CREATEFILE2_EXTENDED_PARAMETERS);
683     ceparams.dwFileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED & 0xFFFF;
684     ceparams.dwFileFlags = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED & 0xFFF00000;
685     ceparams.dwSecurityQosFlags = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED & 0x000F0000;
686     ceparams.lpSecurityAttributes = nullptr;
687     ceparams.hTemplateFile = nullptr;
688     fd = CreateFile2 (wchar_file_name, GENERIC_READ, FILE_SHARE_READ,
689 		      OPEN_EXISTING, &ceparams);
690   }
691 #else
692   fd = CreateFileW (wchar_file_name, GENERIC_READ, FILE_SHARE_READ, nullptr,
693 		    OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,
694 		    nullptr);
695 #endif
696   hb_free (wchar_file_name);
697 
698   if (unlikely (fd == INVALID_HANDLE_VALUE)) goto fail_without_close;
699 
700 #if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
701   {
702     LARGE_INTEGER length;
703     GetFileSizeEx (fd, &length);
704     file->length = length.LowPart;
705     file->mapping = CreateFileMappingFromApp (fd, nullptr, PAGE_READONLY, length.QuadPart, nullptr);
706   }
707 #else
708   file->length = (unsigned long) GetFileSize (fd, nullptr);
709   file->mapping = CreateFileMapping (fd, nullptr, PAGE_READONLY, 0, 0, nullptr);
710 #endif
711   if (unlikely (!file->mapping)) goto fail;
712 
713 #if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
714   file->contents = (char *) MapViewOfFileFromApp (file->mapping, FILE_MAP_READ, 0, 0);
715 #else
716   file->contents = (char *) MapViewOfFile (file->mapping, FILE_MAP_READ, 0, 0, 0);
717 #endif
718   if (unlikely (!file->contents)) goto fail;
719 
720   CloseHandle (fd);
721   return hb_blob_create_or_fail (file->contents, file->length,
722 				 HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE, (void *) file,
723 				 (hb_destroy_func_t) _hb_mapped_file_destroy);
724 
725 fail:
726   CloseHandle (fd);
727 fail_without_close:
728   hb_free (file);
729 
730 #endif
731 
732   /* The following tries to read a file without knowing its size beforehand
733      It's used as a fallback for systems without mmap or to read from pipes */
734   unsigned long len = 0, allocated = BUFSIZ * 16;
735   char *data = (char *) hb_malloc (allocated);
736   if (unlikely (!data)) return nullptr;
737 
738   FILE *fp = fopen (file_name, "rb");
739   if (unlikely (!fp)) goto fread_fail_without_close;
740 
741   while (!feof (fp))
742   {
743     if (allocated - len < BUFSIZ)
744     {
745       allocated *= 2;
746       /* Don't allocate and go more than ~536MB, our mmap reader still
747 	 can cover files like that but lets limit our fallback reader */
748       if (unlikely (allocated > (2 << 28))) goto fread_fail;
749       char *new_data = (char *) hb_realloc (data, allocated);
750       if (unlikely (!new_data)) goto fread_fail;
751       data = new_data;
752     }
753 
754     unsigned long addition = fread (data + len, 1, allocated - len, fp);
755 
756     int err = ferror (fp);
757 #ifdef EINTR // armcc doesn't have it
758     if (unlikely (err == EINTR)) continue;
759 #endif
760     if (unlikely (err)) goto fread_fail;
761 
762     len += addition;
763   }
764 	fclose (fp);
765 
766   return hb_blob_create_or_fail (data, len, HB_MEMORY_MODE_WRITABLE, data,
767 				 (hb_destroy_func_t) hb_free);
768 
769 fread_fail:
770   fclose (fp);
771 fread_fail_without_close:
772   hb_free (data);
773   return nullptr;
774 }
775 #endif /* !HB_NO_OPEN */
776