Path: blob/master/thirdparty/harfbuzz/src/hb-blob.cc
10278 views
/*1* Copyright © 2009 Red Hat, Inc.2* Copyright © 2018 Ebrahim Byagowi3*4* This is part of HarfBuzz, a text shaping library.5*6* Permission is hereby granted, without written agreement and without7* license or royalty fees, to use, copy, modify, and distribute this8* software and its documentation for any purpose, provided that the9* above copyright notice and the following two paragraphs appear in10* all copies of this software.11*12* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR13* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES14* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN15* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH16* DAMAGE.17*18* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,19* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND20* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS21* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO22* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.23*24* Red Hat Author(s): Behdad Esfahbod25*/2627#include "hb.hh"28#include "hb-blob.hh"2930#ifdef HAVE_SYS_MMAN_H31#ifdef HAVE_UNISTD_H32#include <unistd.h>33#endif /* HAVE_UNISTD_H */34#include <sys/mman.h>35#endif /* HAVE_SYS_MMAN_H */363738/**39* SECTION: hb-blob40* @title: hb-blob41* @short_description: Binary data containers42* @include: hb.h43*44* Blobs wrap a chunk of binary data to handle lifecycle management of data45* while it is passed between client and HarfBuzz. Blobs are primarily used46* to create font faces, but also to access font face tables, as well as47* pass around other binary data.48**/495051/**52* hb_blob_create: (skip)53* @data: Pointer to blob data.54* @length: Length of @data in bytes.55* @mode: Memory mode for @data.56* @user_data: Data parameter to pass to @destroy.57* @destroy: (nullable): Callback to call when @data is not needed anymore.58*59* Creates a new "blob" object wrapping @data. The @mode parameter is used60* to negotiate ownership and lifecycle of @data.61*62* Return value: New blob, or the empty blob if something failed or if @length is63* zero. Destroy with hb_blob_destroy().64*65* Since: 0.9.266**/67hb_blob_t *68hb_blob_create (const char *data,69unsigned int length,70hb_memory_mode_t mode,71void *user_data,72hb_destroy_func_t destroy)73{74if (!length)75{76if (destroy)77destroy (user_data);78return hb_blob_get_empty ();79}8081hb_blob_t *blob = hb_blob_create_or_fail (data, length, mode,82user_data, destroy);83return likely (blob) ? blob : hb_blob_get_empty ();84}8586/**87* hb_blob_create_or_fail: (skip)88* @data: Pointer to blob data.89* @length: Length of @data in bytes.90* @mode: Memory mode for @data.91* @user_data: Data parameter to pass to @destroy.92* @destroy: (nullable): Callback to call when @data is not needed anymore.93*94* Creates a new "blob" object wrapping @data. The @mode parameter is used95* to negotiate ownership and lifecycle of @data.96*97* Note that this function returns a freshly-allocated empty blob even if @length98* is zero. This is in contrast to hb_blob_create(), which returns the singleton99* empty blob (as returned by hb_blob_get_empty()) if @length is zero.100*101* Return value: New blob, or `NULL` if failed. Destroy with hb_blob_destroy().102*103* Since: 2.8.2104**/105hb_blob_t *106hb_blob_create_or_fail (const char *data,107unsigned int length,108hb_memory_mode_t mode,109void *user_data,110hb_destroy_func_t destroy)111{112hb_blob_t *blob;113114if (length >= 1u << 31 ||115!(blob = hb_object_create<hb_blob_t> ()))116{117if (destroy)118destroy (user_data);119return nullptr;120}121122blob->data = data;123blob->length = length;124blob->mode = mode;125126blob->user_data = user_data;127blob->destroy = destroy;128129if (blob->mode == HB_MEMORY_MODE_DUPLICATE) {130blob->mode = HB_MEMORY_MODE_READONLY;131if (!blob->try_make_writable ())132{133hb_blob_destroy (blob);134return nullptr;135}136}137138return blob;139}140141static void142_hb_blob_destroy (void *data)143{144hb_blob_destroy ((hb_blob_t *) data);145}146147/**148* hb_blob_create_sub_blob:149* @parent: Parent blob.150* @offset: Start offset of sub-blob within @parent, in bytes.151* @length: Length of sub-blob.152*153* Returns a blob that represents a range of bytes in @parent. The new154* blob is always created with #HB_MEMORY_MODE_READONLY, meaning that it155* will never modify data in the parent blob. The parent data is not156* expected to be modified, and will result in undefined behavior if it157* is.158*159* Makes @parent immutable.160*161* Return value: New blob, or the empty blob if something failed or if162* @length is zero or @offset is beyond the end of @parent's data. Destroy163* with hb_blob_destroy().164*165* Since: 0.9.2166**/167hb_blob_t *168hb_blob_create_sub_blob (hb_blob_t *parent,169unsigned int offset,170unsigned int length)171{172hb_blob_t *blob;173174if (!length || !parent || offset >= parent->length)175return hb_blob_get_empty ();176177hb_blob_make_immutable (parent);178179blob = hb_blob_create (parent->data + offset,180hb_min (length, parent->length - offset),181HB_MEMORY_MODE_READONLY,182hb_blob_reference (parent),183_hb_blob_destroy);184185return blob;186}187188/**189* hb_blob_copy_writable_or_fail:190* @blob: A blob.191*192* Makes a writable copy of @blob.193*194* Return value: The new blob, or nullptr if allocation failed195*196* Since: 1.8.0197**/198hb_blob_t *199hb_blob_copy_writable_or_fail (hb_blob_t *blob)200{201blob = hb_blob_create (blob->data,202blob->length,203HB_MEMORY_MODE_DUPLICATE,204nullptr,205nullptr);206207if (unlikely (blob == hb_blob_get_empty ()))208blob = nullptr;209210return blob;211}212213/**214* hb_blob_get_empty:215*216* Returns the singleton empty blob.217*218* See TODO:link object types for more information.219*220* Return value: (transfer full): The empty blob.221*222* Since: 0.9.2223**/224hb_blob_t *225hb_blob_get_empty ()226{227return const_cast<hb_blob_t *> (&Null (hb_blob_t));228}229230/**231* hb_blob_reference: (skip)232* @blob: a blob.233*234* Increases the reference count on @blob.235*236* See TODO:link object types for more information.237*238* Return value: @blob.239*240* Since: 0.9.2241**/242hb_blob_t *243hb_blob_reference (hb_blob_t *blob)244{245return hb_object_reference (blob);246}247248/**249* hb_blob_destroy: (skip)250* @blob: a blob.251*252* Decreases the reference count on @blob, and if it reaches zero, destroys253* @blob, freeing all memory, possibly calling the destroy-callback the blob254* was created for if it has not been called already.255*256* See TODO:link object types for more information.257*258* Since: 0.9.2259**/260void261hb_blob_destroy (hb_blob_t *blob)262{263if (!hb_object_destroy (blob)) return;264265hb_free (blob);266}267268/**269* hb_blob_set_user_data: (skip)270* @blob: An #hb_blob_t271* @key: The user-data key to set272* @data: A pointer to the user data to set273* @destroy: (nullable): A callback to call when @data is not needed anymore274* @replace: Whether to replace an existing data with the same key275*276* Attaches a user-data key/data pair to the specified blob.277*278* Return value: `true` if success, `false` otherwise279*280* Since: 0.9.2281**/282hb_bool_t283hb_blob_set_user_data (hb_blob_t *blob,284hb_user_data_key_t *key,285void * data,286hb_destroy_func_t destroy,287hb_bool_t replace)288{289return hb_object_set_user_data (blob, key, data, destroy, replace);290}291292/**293* hb_blob_get_user_data: (skip)294* @blob: a blob295* @key: The user-data key to query296*297* Fetches the user data associated with the specified key,298* attached to the specified font-functions structure.299*300* Return value: (transfer none): A pointer to the user data301*302* Since: 0.9.2303**/304void *305hb_blob_get_user_data (const hb_blob_t *blob,306hb_user_data_key_t *key)307{308return hb_object_get_user_data (blob, key);309}310311312/**313* hb_blob_make_immutable:314* @blob: a blob315*316* Makes a blob immutable.317*318* Since: 0.9.2319**/320void321hb_blob_make_immutable (hb_blob_t *blob)322{323if (hb_object_is_immutable (blob))324return;325326hb_object_make_immutable (blob);327}328329/**330* hb_blob_is_immutable:331* @blob: a blob.332*333* Tests whether a blob is immutable.334*335* Return value: `true` if @blob is immutable, `false` otherwise336*337* Since: 0.9.2338**/339hb_bool_t340hb_blob_is_immutable (hb_blob_t *blob)341{342return hb_object_is_immutable (blob);343}344345346/**347* hb_blob_get_length:348* @blob: a blob.349*350* Fetches the length of a blob's data.351*352* Return value: the length of @blob data in bytes.353*354* Since: 0.9.2355**/356unsigned int357hb_blob_get_length (hb_blob_t *blob)358{359return blob->length;360}361362/**363* hb_blob_get_data:364* @blob: a blob.365* @length: (out): The length in bytes of the data retrieved366*367* Fetches the data from a blob.368*369* Returns: (nullable) (transfer none) (array length=length): the byte data of @blob.370*371* Since: 0.9.2372**/373const char *374hb_blob_get_data (hb_blob_t *blob, unsigned int *length)375{376if (length)377*length = blob->length;378379return blob->data;380}381382/**383* hb_blob_get_data_writable:384* @blob: a blob.385* @length: (out): output length of the writable data.386*387* Tries to make blob data writable (possibly copying it) and388* return pointer to data.389*390* Fails if blob has been made immutable, or if memory allocation391* fails.392*393* Returns: (transfer none) (array length=length): Writable blob data,394* or `NULL` if failed.395*396* Since: 0.9.2397**/398char *399hb_blob_get_data_writable (hb_blob_t *blob, unsigned int *length)400{401if (hb_object_is_immutable (blob) ||402!blob->try_make_writable ())403{404if (length) *length = 0;405return nullptr;406}407408if (length) *length = blob->length;409return const_cast<char *> (blob->data);410}411412413bool414hb_blob_t::try_make_writable_inplace_unix ()415{416#if defined(HAVE_SYS_MMAN_H) && defined(HAVE_MPROTECT)417uintptr_t pagesize = -1, mask, length;418const char *addr;419420#if defined(HAVE_SYSCONF) && defined(_SC_PAGE_SIZE)421pagesize = (uintptr_t) sysconf (_SC_PAGE_SIZE);422#elif defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)423pagesize = (uintptr_t) sysconf (_SC_PAGESIZE);424#elif defined(HAVE_GETPAGESIZE)425pagesize = (uintptr_t) getpagesize ();426#endif427428if ((uintptr_t) -1L == pagesize) {429DEBUG_MSG_FUNC (BLOB, this, "failed to get pagesize: %s", strerror (errno));430return false;431}432DEBUG_MSG_FUNC (BLOB, this, "pagesize is %lu", (unsigned long) pagesize);433434mask = ~(pagesize-1);435addr = (const char *) (((uintptr_t) this->data) & mask);436length = (const char *) (((uintptr_t) this->data + this->length + pagesize-1) & mask) - addr;437DEBUG_MSG_FUNC (BLOB, this,438"calling mprotect on [%p..%p] (%lu bytes)",439addr, addr+length, (unsigned long) length);440if (-1 == mprotect ((void *) addr, length, PROT_READ | PROT_WRITE)) {441DEBUG_MSG_FUNC (BLOB, this, "mprotect failed: %s", strerror (errno));442return false;443}444445this->mode = HB_MEMORY_MODE_WRITABLE;446447DEBUG_MSG_FUNC (BLOB, this,448"successfully made [%p..%p] (%lu bytes) writable\n",449addr, addr+length, (unsigned long) length);450return true;451#else452return false;453#endif454}455456bool457hb_blob_t::try_make_writable_inplace ()458{459DEBUG_MSG_FUNC (BLOB, this, "making writable inplace\n");460461if (this->try_make_writable_inplace_unix ())462return true;463464DEBUG_MSG_FUNC (BLOB, this, "making writable -> FAILED\n");465466/* Failed to make writable inplace, mark that */467this->mode = HB_MEMORY_MODE_READONLY;468return false;469}470471bool472hb_blob_t::try_make_writable ()473{474if (unlikely (!length))475mode = HB_MEMORY_MODE_WRITABLE;476477if (this->mode == HB_MEMORY_MODE_WRITABLE)478return true;479480if (this->mode == HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE && this->try_make_writable_inplace ())481return true;482483if (this->mode == HB_MEMORY_MODE_WRITABLE)484return true;485486487DEBUG_MSG_FUNC (BLOB, this, "current data is -> %p\n", this->data);488489char *new_data;490491new_data = (char *) hb_malloc (this->length);492if (unlikely (!new_data))493return false;494495DEBUG_MSG_FUNC (BLOB, this, "dupped successfully -> %p\n", this->data);496497hb_memcpy (new_data, this->data, this->length);498this->destroy_user_data ();499this->mode = HB_MEMORY_MODE_WRITABLE;500this->data = new_data;501this->user_data = new_data;502this->destroy = hb_free;503504return true;505}506507/*508* Mmap509*/510511#ifndef HB_NO_OPEN512#ifdef HAVE_MMAP513# if !defined(HB_NO_RESOURCE_FORK) && defined(__APPLE__)514# include <sys/paths.h>515# endif516# include <sys/types.h>517# include <sys/stat.h>518# include <fcntl.h>519#endif520521#ifdef _WIN32522# include <windows.h>523#else524# ifndef O_BINARY525# define O_BINARY 0526# endif527#endif528529#ifndef MAP_NORESERVE530# define MAP_NORESERVE 0531#endif532533struct hb_mapped_file_t534{535char *contents;536unsigned long length;537#ifdef _WIN32538HANDLE mapping;539#endif540};541542#if (defined(HAVE_MMAP) || defined(_WIN32)) && !defined(HB_NO_MMAP)543static void544_hb_mapped_file_destroy (void *file_)545{546hb_mapped_file_t *file = (hb_mapped_file_t *) file_;547#ifdef HAVE_MMAP548munmap (file->contents, file->length);549#elif defined(_WIN32)550UnmapViewOfFile (file->contents);551CloseHandle (file->mapping);552#else553assert (0); // If we don't have mmap we shouldn't reach here554#endif555556hb_free (file);557}558#endif559560#ifdef _PATH_RSRCFORKSPEC561static int562_open_resource_fork (const char *file_name, hb_mapped_file_t *file)563{564size_t name_len = strlen (file_name);565size_t len = name_len + sizeof (_PATH_RSRCFORKSPEC);566567char *rsrc_name = (char *) hb_malloc (len);568if (unlikely (!rsrc_name)) return -1;569570strncpy (rsrc_name, file_name, name_len);571strncpy (rsrc_name + name_len, _PATH_RSRCFORKSPEC,572sizeof (_PATH_RSRCFORKSPEC));573574int fd = open (rsrc_name, O_RDONLY | O_BINARY, 0);575hb_free (rsrc_name);576577if (fd != -1)578{579struct stat st;580if (fstat (fd, &st) != -1)581file->length = (unsigned long) st.st_size;582else583{584close (fd);585fd = -1;586}587}588589return fd;590}591#endif592593/**594* hb_blob_create_from_file:595* @file_name: A font filename596*597* Creates a new blob containing the data from the598* specified binary font file.599*600* The filename is passed directly to the system on all platforms,601* except on Windows, where the filename is interpreted as UTF-8.602* Only if the filename is not valid UTF-8, it will be interpreted603* according to the system codepage.604*605* Returns: An #hb_blob_t pointer with the content of the file,606* or hb_blob_get_empty() if failed.607*608* Since: 1.7.7609**/610hb_blob_t *611hb_blob_create_from_file (const char *file_name)612{613hb_blob_t *blob = hb_blob_create_from_file_or_fail (file_name);614return likely (blob) ? blob : hb_blob_get_empty ();615}616617/**618* hb_blob_create_from_file_or_fail:619* @file_name: A filename620*621* Creates a new blob containing the data from the specified file.622*623* The filename is passed directly to the system on all platforms,624* except on Windows, where the filename is interpreted as UTF-8.625* Only if the filename is not valid UTF-8, it will be interpreted626* according to the system codepage.627*628* Returns: An #hb_blob_t pointer with the content of the file,629* or `NULL` if failed.630*631* Since: 2.8.2632**/633hb_blob_t *634hb_blob_create_from_file_or_fail (const char *file_name)635{636/* Adopted from glib's gmappedfile.c with Matthias Clasen and637Allison Lortie permission but changed a lot to suit our need. */638#if defined(HAVE_MMAP) && !defined(HB_NO_MMAP)639hb_mapped_file_t *file = (hb_mapped_file_t *) hb_calloc (1, sizeof (hb_mapped_file_t));640if (unlikely (!file)) return nullptr;641642int fd = open (file_name, O_RDONLY | O_BINARY, 0);643if (unlikely (fd == -1)) goto fail_without_close;644645struct stat st;646if (unlikely (fstat (fd, &st) == -1)) goto fail;647648file->length = (unsigned long) st.st_size;649650#ifdef _PATH_RSRCFORKSPEC651if (unlikely (file->length == 0))652{653int rfd = _open_resource_fork (file_name, file);654if (rfd != -1)655{656close (fd);657fd = rfd;658}659}660#endif661662file->contents = (char *) mmap (nullptr, file->length, PROT_READ,663MAP_PRIVATE | MAP_NORESERVE, fd, 0);664665if (unlikely (file->contents == MAP_FAILED)) goto fail;666667close (fd);668669return hb_blob_create_or_fail (file->contents, file->length,670HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE, (void *) file,671(hb_destroy_func_t) _hb_mapped_file_destroy);672673fail:674close (fd);675fail_without_close:676hb_free (file);677678#elif defined(_WIN32) && !defined(HB_NO_MMAP)679hb_mapped_file_t *file = (hb_mapped_file_t *) hb_calloc (1, sizeof (hb_mapped_file_t));680if (unlikely (!file)) return nullptr;681682HANDLE fd;683int conversion;684unsigned int size = strlen (file_name) + 1;685wchar_t * wchar_file_name = (wchar_t *) hb_malloc (sizeof (wchar_t) * size);686if (unlikely (!wchar_file_name)) goto fail_without_close;687688/* Assume file name is given in UTF-8 encoding */689conversion = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, file_name, -1, wchar_file_name, size);690if (conversion <= 0)691{692/* Conversion failed due to invalid UTF-8 characters,693Repeat conversion based on system code page */694mbstowcs(wchar_file_name, file_name, size);695}696#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)697{698CREATEFILE2_EXTENDED_PARAMETERS ceparams = { 0 };699ceparams.dwSize = sizeof(CREATEFILE2_EXTENDED_PARAMETERS);700ceparams.dwFileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED & 0xFFFF;701ceparams.dwFileFlags = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED & 0xFFF00000;702ceparams.dwSecurityQosFlags = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED & 0x000F0000;703ceparams.lpSecurityAttributes = nullptr;704ceparams.hTemplateFile = nullptr;705fd = CreateFile2 (wchar_file_name, GENERIC_READ, FILE_SHARE_READ,706OPEN_EXISTING, &ceparams);707}708#else709fd = CreateFileW (wchar_file_name, GENERIC_READ, FILE_SHARE_READ, nullptr,710OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,711nullptr);712#endif713hb_free (wchar_file_name);714715if (unlikely (fd == INVALID_HANDLE_VALUE)) goto fail_without_close;716717#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)718{719LARGE_INTEGER length;720GetFileSizeEx (fd, &length);721file->length = length.LowPart;722file->mapping = CreateFileMappingFromApp (fd, nullptr, PAGE_READONLY, length.QuadPart, nullptr);723}724#else725file->length = (unsigned long) GetFileSize (fd, nullptr);726file->mapping = CreateFileMapping (fd, nullptr, PAGE_READONLY, 0, 0, nullptr);727#endif728if (unlikely (!file->mapping)) goto fail;729730#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)731file->contents = (char *) MapViewOfFileFromApp (file->mapping, FILE_MAP_READ, 0, 0);732#else733file->contents = (char *) MapViewOfFile (file->mapping, FILE_MAP_READ, 0, 0, 0);734#endif735if (unlikely (!file->contents)) goto fail;736737CloseHandle (fd);738return hb_blob_create_or_fail (file->contents, file->length,739HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE, (void *) file,740(hb_destroy_func_t) _hb_mapped_file_destroy);741742fail:743CloseHandle (fd);744fail_without_close:745hb_free (file);746747#endif748749/* The following tries to read a file without knowing its size beforehand750It's used as a fallback for systems without mmap or to read from pipes */751unsigned long len = 0, allocated = BUFSIZ * 16;752char *data = (char *) hb_malloc (allocated);753if (unlikely (!data)) return nullptr;754755FILE *fp = fopen (file_name, "rb");756if (unlikely (!fp)) goto fread_fail_without_close;757758while (!feof (fp))759{760if (allocated - len < BUFSIZ)761{762allocated *= 2;763/* Don't allocate and go more than ~536MB, our mmap reader still764can cover files like that but lets limit our fallback reader */765if (unlikely (allocated > (2 << 28))) goto fread_fail;766char *new_data = (char *) hb_realloc (data, allocated);767if (unlikely (!new_data)) goto fread_fail;768data = new_data;769}770771unsigned long addition = fread (data + len, 1, allocated - len, fp);772773int err = ferror (fp);774#ifdef EINTR // armcc doesn't have it775if (unlikely (err == EINTR)) continue;776#endif777if (unlikely (err)) goto fread_fail;778779len += addition;780}781fclose (fp);782783return hb_blob_create_or_fail (data, len, HB_MEMORY_MODE_WRITABLE, data,784(hb_destroy_func_t) hb_free);785786fread_fail:787fclose (fp);788fread_fail_without_close:789hb_free (data);790return nullptr;791}792#endif /* !HB_NO_OPEN */793794795