• 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   hb_blob_t *blob;
76 
77   if (!length ||
78       length >= 1u << 31 ||
79       !(blob = hb_object_create<hb_blob_t> ())) {
80     if (destroy)
81       destroy (user_data);
82     return hb_blob_get_empty ();
83   }
84 
85   blob->data = data;
86   blob->length = length;
87   blob->mode = mode;
88 
89   blob->user_data = user_data;
90   blob->destroy = destroy;
91 
92   if (blob->mode == HB_MEMORY_MODE_DUPLICATE) {
93     blob->mode = HB_MEMORY_MODE_READONLY;
94     if (!blob->try_make_writable ()) {
95       hb_blob_destroy (blob);
96       return hb_blob_get_empty ();
97     }
98   }
99 
100   return blob;
101 }
102 
103 static void
_hb_blob_destroy(void * data)104 _hb_blob_destroy (void *data)
105 {
106   hb_blob_destroy ((hb_blob_t *) data);
107 }
108 
109 /**
110  * hb_blob_create_sub_blob:
111  * @parent: Parent blob.
112  * @offset: Start offset of sub-blob within @parent, in bytes.
113  * @length: Length of sub-blob.
114  *
115  * Returns a blob that represents a range of bytes in @parent.  The new
116  * blob is always created with #HB_MEMORY_MODE_READONLY, meaning that it
117  * will never modify data in the parent blob.  The parent data is not
118  * expected to be modified, and will result in undefined behavior if it
119  * is.
120  *
121  * Makes @parent immutable.
122  *
123  * Return value: New blob, or the empty blob if something failed or if
124  * @length is zero or @offset is beyond the end of @parent's data.  Destroy
125  * with hb_blob_destroy().
126  *
127  * Since: 0.9.2
128  **/
129 hb_blob_t *
hb_blob_create_sub_blob(hb_blob_t * parent,unsigned int offset,unsigned int length)130 hb_blob_create_sub_blob (hb_blob_t    *parent,
131 			 unsigned int  offset,
132 			 unsigned int  length)
133 {
134   hb_blob_t *blob;
135 
136   if (!length || !parent || offset >= parent->length)
137     return hb_blob_get_empty ();
138 
139   hb_blob_make_immutable (parent);
140 
141   blob = hb_blob_create (parent->data + offset,
142 			 hb_min (length, parent->length - offset),
143 			 HB_MEMORY_MODE_READONLY,
144 			 hb_blob_reference (parent),
145 			 _hb_blob_destroy);
146 
147   return blob;
148 }
149 
150 /**
151  * hb_blob_copy_writable_or_fail:
152  * @blob: A blob.
153  *
154  * Makes a writable copy of @blob.
155  *
156  * Return value: The new blob, or nullptr if allocation failed
157  *
158  * Since: 1.8.0
159  **/
160 hb_blob_t *
hb_blob_copy_writable_or_fail(hb_blob_t * blob)161 hb_blob_copy_writable_or_fail (hb_blob_t *blob)
162 {
163   blob = hb_blob_create (blob->data,
164 			 blob->length,
165 			 HB_MEMORY_MODE_DUPLICATE,
166 			 nullptr,
167 			 nullptr);
168 
169   if (unlikely (blob == hb_blob_get_empty ()))
170     blob = nullptr;
171 
172   return blob;
173 }
174 
175 /**
176  * hb_blob_get_empty:
177  *
178  * Returns the singleton empty blob.
179  *
180  * See TODO:link object types for more information.
181  *
182  * Return value: (transfer full): The empty blob.
183  *
184  * Since: 0.9.2
185  **/
186 hb_blob_t *
hb_blob_get_empty()187 hb_blob_get_empty ()
188 {
189   return const_cast<hb_blob_t *> (&Null (hb_blob_t));
190 }
191 
192 /**
193  * hb_blob_reference: (skip)
194  * @blob: a blob.
195  *
196  * Increases the reference count on @blob.
197  *
198  * See TODO:link object types for more information.
199  *
200  * Return value: @blob.
201  *
202  * Since: 0.9.2
203  **/
204 hb_blob_t *
hb_blob_reference(hb_blob_t * blob)205 hb_blob_reference (hb_blob_t *blob)
206 {
207   return hb_object_reference (blob);
208 }
209 
210 /**
211  * hb_blob_destroy: (skip)
212  * @blob: a blob.
213  *
214  * Decreases the reference count on @blob, and if it reaches zero, destroys
215  * @blob, freeing all memory, possibly calling the destroy-callback the blob
216  * was created for if it has not been called already.
217  *
218  * See TODO:link object types for more information.
219  *
220  * Since: 0.9.2
221  **/
222 void
hb_blob_destroy(hb_blob_t * blob)223 hb_blob_destroy (hb_blob_t *blob)
224 {
225   if (!hb_object_destroy (blob)) return;
226 
227   blob->fini_shallow ();
228 
229   free (blob);
230 }
231 
232 /**
233  * hb_blob_set_user_data: (skip)
234  * @blob: An #hb_blob_t
235  * @key: The user-data key to set
236  * @data: A pointer to the user data to set
237  * @destroy: (nullable): A callback to call when @data is not needed anymore
238  * @replace: Whether to replace an existing data with the same key
239  *
240  * Attaches a user-data key/data pair to the specified blob.
241  *
242  * Return value: %true if success, %false otherwise
243  *
244  * Since: 0.9.2
245  **/
246 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)247 hb_blob_set_user_data (hb_blob_t          *blob,
248 		       hb_user_data_key_t *key,
249 		       void *              data,
250 		       hb_destroy_func_t   destroy,
251 		       hb_bool_t           replace)
252 {
253   return hb_object_set_user_data (blob, key, data, destroy, replace);
254 }
255 
256 /**
257  * hb_blob_get_user_data: (skip)
258  * @blob: a blob
259  * @key: The user-data key to query
260  *
261  * Fetches the user data associated with the specified key,
262  * attached to the specified font-functions structure.
263  *
264  * Return value: (transfer none): A pointer to the user data
265  *
266  * Since: 0.9.2
267  **/
268 void *
hb_blob_get_user_data(hb_blob_t * blob,hb_user_data_key_t * key)269 hb_blob_get_user_data (hb_blob_t          *blob,
270 		       hb_user_data_key_t *key)
271 {
272   return hb_object_get_user_data (blob, key);
273 }
274 
275 
276 /**
277  * hb_blob_make_immutable:
278  * @blob: a blob
279  *
280  * Makes a blob immutable.
281  *
282  * Since: 0.9.2
283  **/
284 void
hb_blob_make_immutable(hb_blob_t * blob)285 hb_blob_make_immutable (hb_blob_t *blob)
286 {
287   if (hb_object_is_immutable (blob))
288     return;
289 
290   hb_object_make_immutable (blob);
291 }
292 
293 /**
294  * hb_blob_is_immutable:
295  * @blob: a blob.
296  *
297  * Tests whether a blob is immutable.
298  *
299  * Return value: %true if @blob is immutable, %false otherwise
300  *
301  * Since: 0.9.2
302  **/
303 hb_bool_t
hb_blob_is_immutable(hb_blob_t * blob)304 hb_blob_is_immutable (hb_blob_t *blob)
305 {
306   return hb_object_is_immutable (blob);
307 }
308 
309 
310 /**
311  * hb_blob_get_length:
312  * @blob: a blob.
313  *
314  * Fetches the length of a blob's data.
315  *
316  * Return value: the length of @blob data in bytes.
317  *
318  * Since: 0.9.2
319  **/
320 unsigned int
hb_blob_get_length(hb_blob_t * blob)321 hb_blob_get_length (hb_blob_t *blob)
322 {
323   return blob->length;
324 }
325 
326 /**
327  * hb_blob_get_data:
328  * @blob: a blob.
329  * @length: (out): The length in bytes of the data retrieved
330  *
331  * Fetches the data from a blob.
332  *
333  * Returns: (transfer none) (array length=length): the byte data of @blob.
334  *
335  * Since: 0.9.2
336  **/
337 const char *
hb_blob_get_data(hb_blob_t * blob,unsigned int * length)338 hb_blob_get_data (hb_blob_t *blob, unsigned int *length)
339 {
340   if (length)
341     *length = blob->length;
342 
343   return blob->data;
344 }
345 
346 /**
347  * hb_blob_get_data_writable:
348  * @blob: a blob.
349  * @length: (out): output length of the writable data.
350  *
351  * Tries to make blob data writable (possibly copying it) and
352  * return pointer to data.
353  *
354  * Fails if blob has been made immutable, or if memory allocation
355  * fails.
356  *
357  * Returns: (transfer none) (array length=length): Writable blob data,
358  * or %NULL if failed.
359  *
360  * Since: 0.9.2
361  **/
362 char *
hb_blob_get_data_writable(hb_blob_t * blob,unsigned int * length)363 hb_blob_get_data_writable (hb_blob_t *blob, unsigned int *length)
364 {
365   if (hb_object_is_immutable (blob) ||
366      !blob->try_make_writable ())
367   {
368     if (length) *length = 0;
369     return nullptr;
370   }
371 
372   if (length) *length = blob->length;
373   return const_cast<char *> (blob->data);
374 }
375 
376 
377 bool
try_make_writable_inplace_unix()378 hb_blob_t::try_make_writable_inplace_unix ()
379 {
380 #if defined(HAVE_SYS_MMAN_H) && defined(HAVE_MPROTECT)
381   uintptr_t pagesize = -1, mask, length;
382   const char *addr;
383 
384 #if defined(HAVE_SYSCONF) && defined(_SC_PAGE_SIZE)
385   pagesize = (uintptr_t) sysconf (_SC_PAGE_SIZE);
386 #elif defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)
387   pagesize = (uintptr_t) sysconf (_SC_PAGESIZE);
388 #elif defined(HAVE_GETPAGESIZE)
389   pagesize = (uintptr_t) getpagesize ();
390 #endif
391 
392   if ((uintptr_t) -1L == pagesize) {
393     DEBUG_MSG_FUNC (BLOB, this, "failed to get pagesize: %s", strerror (errno));
394     return false;
395   }
396   DEBUG_MSG_FUNC (BLOB, this, "pagesize is %lu", (unsigned long) pagesize);
397 
398   mask = ~(pagesize-1);
399   addr = (const char *) (((uintptr_t) this->data) & mask);
400   length = (const char *) (((uintptr_t) this->data + this->length + pagesize-1) & mask)  - addr;
401   DEBUG_MSG_FUNC (BLOB, this,
402 		  "calling mprotect on [%p..%p] (%lu bytes)",
403 		  addr, addr+length, (unsigned long) length);
404   if (-1 == mprotect ((void *) addr, length, PROT_READ | PROT_WRITE)) {
405     DEBUG_MSG_FUNC (BLOB, this, "mprotect failed: %s", strerror (errno));
406     return false;
407   }
408 
409   this->mode = HB_MEMORY_MODE_WRITABLE;
410 
411   DEBUG_MSG_FUNC (BLOB, this,
412 		  "successfully made [%p..%p] (%lu bytes) writable\n",
413 		  addr, addr+length, (unsigned long) length);
414   return true;
415 #else
416   return false;
417 #endif
418 }
419 
420 bool
try_make_writable_inplace()421 hb_blob_t::try_make_writable_inplace ()
422 {
423   DEBUG_MSG_FUNC (BLOB, this, "making writable inplace\n");
424 
425   if (this->try_make_writable_inplace_unix ())
426     return true;
427 
428   DEBUG_MSG_FUNC (BLOB, this, "making writable -> FAILED\n");
429 
430   /* Failed to make writable inplace, mark that */
431   this->mode = HB_MEMORY_MODE_READONLY;
432   return false;
433 }
434 
435 bool
try_make_writable()436 hb_blob_t::try_make_writable ()
437 {
438   if (unlikely (!length))
439     mode = HB_MEMORY_MODE_WRITABLE;
440 
441   if (this->mode == HB_MEMORY_MODE_WRITABLE)
442     return true;
443 
444   if (this->mode == HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE && this->try_make_writable_inplace ())
445     return true;
446 
447   if (this->mode == HB_MEMORY_MODE_WRITABLE)
448     return true;
449 
450 
451   DEBUG_MSG_FUNC (BLOB, this, "current data is -> %p\n", this->data);
452 
453   char *new_data;
454 
455   new_data = (char *) malloc (this->length);
456   if (unlikely (!new_data))
457     return false;
458 
459   DEBUG_MSG_FUNC (BLOB, this, "dupped successfully -> %p\n", this->data);
460 
461   memcpy (new_data, this->data, this->length);
462   this->destroy_user_data ();
463   this->mode = HB_MEMORY_MODE_WRITABLE;
464   this->data = new_data;
465   this->user_data = new_data;
466   this->destroy = free;
467 
468   return true;
469 }
470 
471 /*
472  * Mmap
473  */
474 
475 #ifndef HB_NO_OPEN
476 #ifdef HAVE_MMAP
477 # if !defined(HB_NO_RESOURCE_FORK) && defined(__APPLE__)
478 #  include <sys/paths.h>
479 # endif
480 # include <sys/types.h>
481 # include <sys/stat.h>
482 # include <fcntl.h>
483 #endif
484 
485 #ifdef _WIN32
486 # include <windows.h>
487 #else
488 # ifndef O_BINARY
489 #  define O_BINARY 0
490 # endif
491 #endif
492 
493 #ifndef MAP_NORESERVE
494 # define MAP_NORESERVE 0
495 #endif
496 
497 struct hb_mapped_file_t
498 {
499   char *contents;
500   unsigned long length;
501 #ifdef _WIN32
502   HANDLE mapping;
503 #endif
504 };
505 
506 #if (defined(HAVE_MMAP) || defined(_WIN32)) && !defined(HB_NO_MMAP)
507 static void
_hb_mapped_file_destroy(void * file_)508 _hb_mapped_file_destroy (void *file_)
509 {
510   hb_mapped_file_t *file = (hb_mapped_file_t *) file_;
511 #ifdef HAVE_MMAP
512   munmap (file->contents, file->length);
513 #elif defined(_WIN32)
514   UnmapViewOfFile (file->contents);
515   CloseHandle (file->mapping);
516 #else
517   assert (0); // If we don't have mmap we shouldn't reach here
518 #endif
519 
520   free (file);
521 }
522 #endif
523 
524 #ifdef _PATH_RSRCFORKSPEC
525 static int
_open_resource_fork(const char * file_name,hb_mapped_file_t * file)526 _open_resource_fork (const char *file_name, hb_mapped_file_t *file)
527 {
528   size_t name_len = strlen (file_name);
529   size_t len = name_len + sizeof (_PATH_RSRCFORKSPEC);
530 
531   char *rsrc_name = (char *) malloc (len);
532   if (unlikely (!rsrc_name)) return -1;
533 
534   strncpy (rsrc_name, file_name, name_len);
535   strncpy (rsrc_name + name_len, _PATH_RSRCFORKSPEC,
536 	   sizeof (_PATH_RSRCFORKSPEC) - 1);
537 
538   int fd = open (rsrc_name, O_RDONLY | O_BINARY, 0);
539   free (rsrc_name);
540 
541   if (fd != -1)
542   {
543     struct stat st;
544     if (fstat (fd, &st) != -1)
545       file->length = (unsigned long) st.st_size;
546     else
547     {
548       close (fd);
549       fd = -1;
550     }
551   }
552 
553   return fd;
554 }
555 #endif
556 
557 /**
558  * hb_blob_create_from_file:
559  * @file_name: A font filename
560  *
561  * Creates a new blob containing the data from the
562  * specified binary font file.
563  *
564  * Returns: An #hb_blob_t pointer with the content of the file
565  *
566  * Since: 1.7.7
567  **/
568 hb_blob_t *
hb_blob_create_from_file(const char * file_name)569 hb_blob_create_from_file (const char *file_name)
570 {
571   /* Adopted from glib's gmappedfile.c with Matthias Clasen and
572      Allison Lortie permission but changed a lot to suit our need. */
573 #if defined(HAVE_MMAP) && !defined(HB_NO_MMAP)
574   hb_mapped_file_t *file = (hb_mapped_file_t *) calloc (1, sizeof (hb_mapped_file_t));
575   if (unlikely (!file)) return hb_blob_get_empty ();
576 
577   int fd = open (file_name, O_RDONLY | O_BINARY, 0);
578   if (unlikely (fd == -1)) goto fail_without_close;
579 
580   struct stat st;
581   if (unlikely (fstat (fd, &st) == -1)) goto fail;
582 
583   file->length = (unsigned long) st.st_size;
584 
585 #ifdef _PATH_RSRCFORKSPEC
586   if (unlikely (file->length == 0))
587   {
588     int rfd = _open_resource_fork (file_name, file);
589     if (rfd != -1)
590     {
591       close (fd);
592       fd = rfd;
593     }
594   }
595 #endif
596 
597   file->contents = (char *) mmap (nullptr, file->length, PROT_READ,
598 				  MAP_PRIVATE | MAP_NORESERVE, fd, 0);
599 
600   if (unlikely (file->contents == MAP_FAILED)) goto fail;
601 
602   close (fd);
603 
604   return hb_blob_create (file->contents, file->length,
605 			 HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE, (void *) file,
606 			 (hb_destroy_func_t) _hb_mapped_file_destroy);
607 
608 fail:
609   close (fd);
610 fail_without_close:
611   free (file);
612 
613 #elif defined(_WIN32) && !defined(HB_NO_MMAP)
614   hb_mapped_file_t *file = (hb_mapped_file_t *) calloc (1, sizeof (hb_mapped_file_t));
615   if (unlikely (!file)) return hb_blob_get_empty ();
616 
617   HANDLE fd;
618   unsigned int size = strlen (file_name) + 1;
619   wchar_t * wchar_file_name = (wchar_t *) malloc (sizeof (wchar_t) * size);
620   if (unlikely (!wchar_file_name)) goto fail_without_close;
621   mbstowcs (wchar_file_name, file_name, size);
622 #if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
623   {
624     CREATEFILE2_EXTENDED_PARAMETERS ceparams = { 0 };
625     ceparams.dwSize = sizeof(CREATEFILE2_EXTENDED_PARAMETERS);
626     ceparams.dwFileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED & 0xFFFF;
627     ceparams.dwFileFlags = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED & 0xFFF00000;
628     ceparams.dwSecurityQosFlags = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED & 0x000F0000;
629     ceparams.lpSecurityAttributes = nullptr;
630     ceparams.hTemplateFile = nullptr;
631     fd = CreateFile2 (wchar_file_name, GENERIC_READ, FILE_SHARE_READ,
632 		      OPEN_EXISTING, &ceparams);
633   }
634 #else
635   fd = CreateFileW (wchar_file_name, GENERIC_READ, FILE_SHARE_READ, nullptr,
636 		    OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,
637 		    nullptr);
638 #endif
639   free (wchar_file_name);
640 
641   if (unlikely (fd == INVALID_HANDLE_VALUE)) goto fail_without_close;
642 
643 #if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
644   {
645     LARGE_INTEGER length;
646     GetFileSizeEx (fd, &length);
647     file->length = length.LowPart;
648     file->mapping = CreateFileMappingFromApp (fd, nullptr, PAGE_READONLY, length.QuadPart, nullptr);
649   }
650 #else
651   file->length = (unsigned long) GetFileSize (fd, nullptr);
652   file->mapping = CreateFileMapping (fd, nullptr, PAGE_READONLY, 0, 0, nullptr);
653 #endif
654   if (unlikely (!file->mapping)) goto fail;
655 
656 #if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
657   file->contents = (char *) MapViewOfFileFromApp (file->mapping, FILE_MAP_READ, 0, 0);
658 #else
659   file->contents = (char *) MapViewOfFile (file->mapping, FILE_MAP_READ, 0, 0, 0);
660 #endif
661   if (unlikely (!file->contents)) goto fail;
662 
663   CloseHandle (fd);
664   return hb_blob_create (file->contents, file->length,
665 			 HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE, (void *) file,
666 			 (hb_destroy_func_t) _hb_mapped_file_destroy);
667 
668 fail:
669   CloseHandle (fd);
670 fail_without_close:
671   free (file);
672 
673 #endif
674 
675   /* The following tries to read a file without knowing its size beforehand
676      It's used as a fallback for systems without mmap or to read from pipes */
677   unsigned long len = 0, allocated = BUFSIZ * 16;
678   char *data = (char *) malloc (allocated);
679   if (unlikely (!data)) return hb_blob_get_empty ();
680 
681   FILE *fp = fopen (file_name, "rb");
682   if (unlikely (!fp)) goto fread_fail_without_close;
683 
684   while (!feof (fp))
685   {
686     if (allocated - len < BUFSIZ)
687     {
688       allocated *= 2;
689       /* Don't allocate and go more than ~536MB, our mmap reader still
690 	 can cover files like that but lets limit our fallback reader */
691       if (unlikely (allocated > (2 << 28))) goto fread_fail;
692       char *new_data = (char *) realloc (data, allocated);
693       if (unlikely (!new_data)) goto fread_fail;
694       data = new_data;
695     }
696 
697     unsigned long addition = fread (data + len, 1, allocated - len, fp);
698 
699     int err = ferror (fp);
700 #ifdef EINTR // armcc doesn't have it
701     if (unlikely (err == EINTR)) continue;
702 #endif
703     if (unlikely (err)) goto fread_fail;
704 
705     len += addition;
706   }
707 	fclose (fp);
708 
709   return hb_blob_create (data, len, HB_MEMORY_MODE_WRITABLE, data,
710 			 (hb_destroy_func_t) free);
711 
712 fread_fail:
713   fclose (fp);
714 fread_fail_without_close:
715   free (data);
716   return hb_blob_get_empty ();
717 }
718 #endif /* !HB_NO_OPEN */
719