2 * Copyright © 2009 Red Hat, Inc.
3 * Copyright © 2018 Ebrahim Byagowi
5 * This is part of HarfBuzz, a text shaping library.
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.
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
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.
25 * Red Hat Author(s): Behdad Esfahbod
31 #ifdef HAVE_SYS_MMAN_H
34 #endif /* HAVE_UNISTD_H */
36 #endif /* HAVE_SYS_MMAN_H */
42 * @short_description: Binary data containers
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.
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.
60 * Creates a new "blob" object wrapping @data. The @mode parameter is used
61 * to negotiate ownership and lifecycle of @data.
63 * Return value: New blob, or the empty blob if something failed or if @length is
64 * zero. Destroy with hb_blob_destroy().
69 hb_blob_create (const char *data
,
71 hb_memory_mode_t mode
,
73 hb_destroy_func_t destroy
)
79 return hb_blob_get_empty ();
82 hb_blob_t
*blob
= hb_blob_create_or_fail (data
, length
, mode
,
84 return likely (blob
) ? blob
: hb_blob_get_empty ();
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.
95 * Creates a new "blob" object wrapping @data. The @mode parameter is used
96 * to negotiate ownership and lifecycle of @data.
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.
102 * Return value: New blob, or `NULL` if failed. Destroy with hb_blob_destroy().
107 hb_blob_create_or_fail (const char *data
,
109 hb_memory_mode_t mode
,
111 hb_destroy_func_t destroy
)
115 if (length
>= 1u << 31 ||
116 !(blob
= hb_object_create
<hb_blob_t
> ()))
124 blob
->length
= length
;
127 blob
->user_data
= user_data
;
128 blob
->destroy
= destroy
;
130 if (blob
->mode
== HB_MEMORY_MODE_DUPLICATE
) {
131 blob
->mode
= HB_MEMORY_MODE_READONLY
;
132 if (!blob
->try_make_writable ())
134 hb_blob_destroy (blob
);
143 _hb_blob_destroy (void *data
)
145 hb_blob_destroy ((hb_blob_t
*) data
);
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.
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
160 * Makes @parent immutable.
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().
169 hb_blob_create_sub_blob (hb_blob_t
*parent
,
175 if (!length
|| !parent
|| offset
>= parent
->length
)
176 return hb_blob_get_empty ();
178 hb_blob_make_immutable (parent
);
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
),
190 * hb_blob_copy_writable_or_fail:
193 * Makes a writable copy of @blob.
195 * Return value: The new blob, or nullptr if allocation failed
200 hb_blob_copy_writable_or_fail (hb_blob_t
*blob
)
202 blob
= hb_blob_create (blob
->data
,
204 HB_MEMORY_MODE_DUPLICATE
,
208 if (unlikely (blob
== hb_blob_get_empty ()))
217 * Returns the singleton empty blob.
219 * See TODO:link object types for more information.
221 * Return value: (transfer full): The empty blob.
228 return const_cast<hb_blob_t
*> (&Null (hb_blob_t
));
232 * hb_blob_reference: (skip)
235 * Increases the reference count on @blob.
237 * See TODO:link object types for more information.
239 * Return value: @blob.
244 hb_blob_reference (hb_blob_t
*blob
)
246 return hb_object_reference (blob
);
250 * hb_blob_destroy: (skip)
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.
257 * See TODO:link object types for more information.
262 hb_blob_destroy (hb_blob_t
*blob
)
264 if (!hb_object_destroy (blob
)) return;
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
277 * Attaches a user-data key/data pair to the specified blob.
279 * Return value: `true` if success, `false` otherwise
284 hb_blob_set_user_data (hb_blob_t
*blob
,
285 hb_user_data_key_t
*key
,
287 hb_destroy_func_t destroy
,
290 return hb_object_set_user_data (blob
, key
, data
, destroy
, replace
);
294 * hb_blob_get_user_data: (skip)
296 * @key: The user-data key to query
298 * Fetches the user data associated with the specified key,
299 * attached to the specified font-functions structure.
301 * Return value: (transfer none): A pointer to the user data
306 hb_blob_get_user_data (const hb_blob_t
*blob
,
307 hb_user_data_key_t
*key
)
309 return hb_object_get_user_data (blob
, key
);
314 * hb_blob_make_immutable:
317 * Makes a blob immutable.
322 hb_blob_make_immutable (hb_blob_t
*blob
)
324 if (hb_object_is_immutable (blob
))
327 hb_object_make_immutable (blob
);
331 * hb_blob_is_immutable:
334 * Tests whether a blob is immutable.
336 * Return value: `true` if @blob is immutable, `false` otherwise
341 hb_blob_is_immutable (hb_blob_t
*blob
)
343 return hb_object_is_immutable (blob
);
348 * hb_blob_get_length:
351 * Fetches the length of a blob's data.
353 * Return value: the length of @blob data in bytes.
358 hb_blob_get_length (hb_blob_t
*blob
)
366 * @length: (out): The length in bytes of the data retrieved
368 * Fetches the data from a blob.
370 * Returns: (nullable) (transfer none) (array length=length): the byte data of @blob.
375 hb_blob_get_data (hb_blob_t
*blob
, unsigned int *length
)
378 *length
= blob
->length
;
384 * hb_blob_get_data_writable:
386 * @length: (out): output length of the writable data.
388 * Tries to make blob data writable (possibly copying it) and
389 * return pointer to data.
391 * Fails if blob has been made immutable, or if memory allocation
394 * Returns: (transfer none) (array length=length): Writable blob data,
395 * or `NULL` if failed.
400 hb_blob_get_data_writable (hb_blob_t
*blob
, unsigned int *length
)
402 if (hb_object_is_immutable (blob
) ||
403 !blob
->try_make_writable ())
405 if (length
) *length
= 0;
409 if (length
) *length
= blob
->length
;
410 return const_cast<char *> (blob
->data
);
415 hb_blob_t::try_make_writable_inplace_unix ()
417 #if defined(HAVE_SYS_MMAN_H) && defined(HAVE_MPROTECT)
418 uintptr_t pagesize
= -1, mask
, length
;
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 ();
429 if ((uintptr_t) -1L == pagesize
) {
430 DEBUG_MSG_FUNC (BLOB
, this, "failed to get pagesize: %s", strerror (errno
));
433 DEBUG_MSG_FUNC (BLOB
, this, "pagesize is %lu", (unsigned long) pagesize
);
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
));
446 this->mode
= HB_MEMORY_MODE_WRITABLE
;
448 DEBUG_MSG_FUNC (BLOB
, this,
449 "successfully made [%p..%p] (%lu bytes) writable\n",
450 addr
, addr
+length
, (unsigned long) length
);
458 hb_blob_t::try_make_writable_inplace ()
460 DEBUG_MSG_FUNC (BLOB
, this, "making writable inplace\n");
462 if (this->try_make_writable_inplace_unix ())
465 DEBUG_MSG_FUNC (BLOB
, this, "making writable -> FAILED\n");
467 /* Failed to make writable inplace, mark that */
468 this->mode
= HB_MEMORY_MODE_READONLY
;
473 hb_blob_t::try_make_writable ()
475 if (unlikely (!length
))
476 mode
= HB_MEMORY_MODE_WRITABLE
;
478 if (this->mode
== HB_MEMORY_MODE_WRITABLE
)
481 if (this->mode
== HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE
&& this->try_make_writable_inplace ())
484 if (this->mode
== HB_MEMORY_MODE_WRITABLE
)
488 DEBUG_MSG_FUNC (BLOB
, this, "current data is -> %p\n", this->data
);
492 new_data
= (char *) hb_malloc (this->length
);
493 if (unlikely (!new_data
))
496 DEBUG_MSG_FUNC (BLOB
, this, "dupped successfully -> %p\n", this->data
);
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
;
514 # if !defined(HB_NO_RESOURCE_FORK) && defined(__APPLE__)
515 # include <sys/paths.h>
517 # include <sys/types.h>
518 # include <sys/stat.h>
523 # include <windows.h>
530 #ifndef MAP_NORESERVE
531 # define MAP_NORESERVE 0
534 struct hb_mapped_file_t
537 unsigned long length
;
543 #if (defined(HAVE_MMAP) || defined(_WIN32)) && !defined(HB_NO_MMAP)
545 _hb_mapped_file_destroy (void *file_
)
547 hb_mapped_file_t
*file
= (hb_mapped_file_t
*) file_
;
549 munmap (file
->contents
, file
->length
);
550 #elif defined(_WIN32)
551 UnmapViewOfFile (file
->contents
);
552 CloseHandle (file
->mapping
);
554 assert (0); // If we don't have mmap we shouldn't reach here
561 #ifdef _PATH_RSRCFORKSPEC
563 _open_resource_fork (const char *file_name
, hb_mapped_file_t
*file
)
565 size_t name_len
= strlen (file_name
);
566 size_t len
= name_len
+ sizeof (_PATH_RSRCFORKSPEC
);
568 char *rsrc_name
= (char *) hb_malloc (len
);
569 if (unlikely (!rsrc_name
)) return -1;
571 strncpy (rsrc_name
, file_name
, name_len
);
572 strncpy (rsrc_name
+ name_len
, _PATH_RSRCFORKSPEC
,
573 sizeof (_PATH_RSRCFORKSPEC
));
575 int fd
= open (rsrc_name
, O_RDONLY
| O_BINARY
, 0);
581 if (fstat (fd
, &st
) != -1)
582 file
->length
= (unsigned long) st
.st_size
;
595 * hb_blob_create_from_file:
596 * @file_name: A font filename
598 * Creates a new blob containing the data from the
599 * specified binary font file.
601 * Returns: An #hb_blob_t pointer with the content of the file,
602 * or hb_blob_get_empty() if failed.
607 hb_blob_create_from_file (const char *file_name
)
609 hb_blob_t
*blob
= hb_blob_create_from_file_or_fail (file_name
);
610 return likely (blob
) ? blob
: hb_blob_get_empty ();
614 * hb_blob_create_from_file_or_fail:
615 * @file_name: A font filename
617 * Creates a new blob containing the data from the
618 * specified binary font file.
620 * Returns: An #hb_blob_t pointer with the content of the file,
621 * or `NULL` if failed.
626 hb_blob_create_from_file_or_fail (const char *file_name
)
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;
634 int fd
= open (file_name
, O_RDONLY
| O_BINARY
, 0);
635 if (unlikely (fd
== -1)) goto fail_without_close
;
638 if (unlikely (fstat (fd
, &st
) == -1)) goto fail
;
640 file
->length
= (unsigned long) st
.st_size
;
642 #ifdef _PATH_RSRCFORKSPEC
643 if (unlikely (file
->length
== 0))
645 int rfd
= _open_resource_fork (file_name
, file
);
654 file
->contents
= (char *) mmap (nullptr, file
->length
, PROT_READ
,
655 MAP_PRIVATE
| MAP_NORESERVE
, fd
, 0);
657 if (unlikely (file
->contents
== MAP_FAILED
)) goto fail
;
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
);
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;
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) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)
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
);
692 fd
= CreateFileW (wchar_file_name
, GENERIC_READ
, FILE_SHARE_READ
, nullptr,
693 OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
|FILE_FLAG_OVERLAPPED
,
696 hb_free (wchar_file_name
);
698 if (unlikely (fd
== INVALID_HANDLE_VALUE
)) goto fail_without_close
;
700 #if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)
702 LARGE_INTEGER length
;
703 GetFileSizeEx (fd
, &length
);
704 file
->length
= length
.LowPart
;
705 file
->mapping
= CreateFileMappingFromApp (fd
, nullptr, PAGE_READONLY
, length
.QuadPart
, nullptr);
708 file
->length
= (unsigned long) GetFileSize (fd
, nullptr);
709 file
->mapping
= CreateFileMapping (fd
, nullptr, PAGE_READONLY
, 0, 0, nullptr);
711 if (unlikely (!file
->mapping
)) goto fail
;
713 #if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)
714 file
->contents
= (char *) MapViewOfFileFromApp (file
->mapping
, FILE_MAP_READ
, 0, 0);
716 file
->contents
= (char *) MapViewOfFile (file
->mapping
, FILE_MAP_READ
, 0, 0, 0);
718 if (unlikely (!file
->contents
)) goto fail
;
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
);
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;
738 FILE *fp
= fopen (file_name
, "rb");
739 if (unlikely (!fp
)) goto fread_fail_without_close
;
743 if (allocated
- len
< BUFSIZ
)
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
;
754 unsigned long addition
= fread (data
+ len
, 1, allocated
- len
, fp
);
756 int err
= ferror (fp
);
757 #ifdef EINTR // armcc doesn't have it
758 if (unlikely (err
== EINTR
)) continue;
760 if (unlikely (err
)) goto fread_fail
;
766 return hb_blob_create_or_fail (data
, len
, HB_MEMORY_MODE_WRITABLE
, data
,
767 (hb_destroy_func_t
) hb_free
);
771 fread_fail_without_close
:
775 #endif /* !HB_NO_OPEN */