Path: blob/master/src/java.desktop/share/native/libharfbuzz/hb-blob.cc
66644 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;264265blob->fini_shallow ();266267hb_free (blob);268}269270/**271* hb_blob_set_user_data: (skip)272* @blob: An #hb_blob_t273* @key: The user-data key to set274* @data: A pointer to the user data to set275* @destroy: (nullable): A callback to call when @data is not needed anymore276* @replace: Whether to replace an existing data with the same key277*278* Attaches a user-data key/data pair to the specified blob.279*280* Return value: %true if success, %false otherwise281*282* Since: 0.9.2283**/284hb_bool_t285hb_blob_set_user_data (hb_blob_t *blob,286hb_user_data_key_t *key,287void * data,288hb_destroy_func_t destroy,289hb_bool_t replace)290{291return hb_object_set_user_data (blob, key, data, destroy, replace);292}293294/**295* hb_blob_get_user_data: (skip)296* @blob: a blob297* @key: The user-data key to query298*299* Fetches the user data associated with the specified key,300* attached to the specified font-functions structure.301*302* Return value: (transfer none): A pointer to the user data303*304* Since: 0.9.2305**/306void *307hb_blob_get_user_data (hb_blob_t *blob,308hb_user_data_key_t *key)309{310return hb_object_get_user_data (blob, key);311}312313314/**315* hb_blob_make_immutable:316* @blob: a blob317*318* Makes a blob immutable.319*320* Since: 0.9.2321**/322void323hb_blob_make_immutable (hb_blob_t *blob)324{325if (hb_object_is_immutable (blob))326return;327328hb_object_make_immutable (blob);329}330331/**332* hb_blob_is_immutable:333* @blob: a blob.334*335* Tests whether a blob is immutable.336*337* Return value: %true if @blob is immutable, %false otherwise338*339* Since: 0.9.2340**/341hb_bool_t342hb_blob_is_immutable (hb_blob_t *blob)343{344return hb_object_is_immutable (blob);345}346347348/**349* hb_blob_get_length:350* @blob: a blob.351*352* Fetches the length of a blob's data.353*354* Return value: the length of @blob data in bytes.355*356* Since: 0.9.2357**/358unsigned int359hb_blob_get_length (hb_blob_t *blob)360{361return blob->length;362}363364/**365* hb_blob_get_data:366* @blob: a blob.367* @length: (out): The length in bytes of the data retrieved368*369* Fetches the data from a blob.370*371* Returns: (nullable) (transfer none) (array length=length): the byte data of @blob.372*373* Since: 0.9.2374**/375const char *376hb_blob_get_data (hb_blob_t *blob, unsigned int *length)377{378if (length)379*length = blob->length;380381return blob->data;382}383384/**385* hb_blob_get_data_writable:386* @blob: a blob.387* @length: (out): output length of the writable data.388*389* Tries to make blob data writable (possibly copying it) and390* return pointer to data.391*392* Fails if blob has been made immutable, or if memory allocation393* fails.394*395* Returns: (transfer none) (array length=length): Writable blob data,396* or %NULL if failed.397*398* Since: 0.9.2399**/400char *401hb_blob_get_data_writable (hb_blob_t *blob, unsigned int *length)402{403if (hb_object_is_immutable (blob) ||404!blob->try_make_writable ())405{406if (length) *length = 0;407return nullptr;408}409410if (length) *length = blob->length;411return const_cast<char *> (blob->data);412}413414415bool416hb_blob_t::try_make_writable_inplace_unix ()417{418#if defined(HAVE_SYS_MMAN_H) && defined(HAVE_MPROTECT)419uintptr_t pagesize = -1, mask, length;420const char *addr;421422#if defined(HAVE_SYSCONF) && defined(_SC_PAGE_SIZE)423pagesize = (uintptr_t) sysconf (_SC_PAGE_SIZE);424#elif defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)425pagesize = (uintptr_t) sysconf (_SC_PAGESIZE);426#elif defined(HAVE_GETPAGESIZE)427pagesize = (uintptr_t) getpagesize ();428#endif429430if ((uintptr_t) -1L == pagesize) {431DEBUG_MSG_FUNC (BLOB, this, "failed to get pagesize: %s", strerror (errno));432return false;433}434DEBUG_MSG_FUNC (BLOB, this, "pagesize is %lu", (unsigned long) pagesize);435436mask = ~(pagesize-1);437addr = (const char *) (((uintptr_t) this->data) & mask);438length = (const char *) (((uintptr_t) this->data + this->length + pagesize-1) & mask) - addr;439DEBUG_MSG_FUNC (BLOB, this,440"calling mprotect on [%p..%p] (%lu bytes)",441addr, addr+length, (unsigned long) length);442if (-1 == mprotect ((void *) addr, length, PROT_READ | PROT_WRITE)) {443DEBUG_MSG_FUNC (BLOB, this, "mprotect failed: %s", strerror (errno));444return false;445}446447this->mode = HB_MEMORY_MODE_WRITABLE;448449DEBUG_MSG_FUNC (BLOB, this,450"successfully made [%p..%p] (%lu bytes) writable\n",451addr, addr+length, (unsigned long) length);452return true;453#else454return false;455#endif456}457458bool459hb_blob_t::try_make_writable_inplace ()460{461DEBUG_MSG_FUNC (BLOB, this, "making writable inplace\n");462463if (this->try_make_writable_inplace_unix ())464return true;465466DEBUG_MSG_FUNC (BLOB, this, "making writable -> FAILED\n");467468/* Failed to make writable inplace, mark that */469this->mode = HB_MEMORY_MODE_READONLY;470return false;471}472473bool474hb_blob_t::try_make_writable ()475{476if (unlikely (!length))477mode = HB_MEMORY_MODE_WRITABLE;478479if (this->mode == HB_MEMORY_MODE_WRITABLE)480return true;481482if (this->mode == HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE && this->try_make_writable_inplace ())483return true;484485if (this->mode == HB_MEMORY_MODE_WRITABLE)486return true;487488489DEBUG_MSG_FUNC (BLOB, this, "current data is -> %p\n", this->data);490491char *new_data;492493new_data = (char *) hb_malloc (this->length);494if (unlikely (!new_data))495return false;496497DEBUG_MSG_FUNC (BLOB, this, "dupped successfully -> %p\n", this->data);498499memcpy (new_data, this->data, this->length);500this->destroy_user_data ();501this->mode = HB_MEMORY_MODE_WRITABLE;502this->data = new_data;503this->user_data = new_data;504this->destroy = hb_free;505506return true;507}508509/*510* Mmap511*/512513#ifndef HB_NO_OPEN514#ifdef HAVE_MMAP515# if !defined(HB_NO_RESOURCE_FORK) && defined(__APPLE__)516# include <sys/paths.h>517# endif518# include <sys/types.h>519# include <sys/stat.h>520# include <fcntl.h>521#endif522523#ifdef _WIN32524# include <windows.h>525#else526# ifndef O_BINARY527# define O_BINARY 0528# endif529#endif530531#ifndef MAP_NORESERVE532# define MAP_NORESERVE 0533#endif534535struct hb_mapped_file_t536{537char *contents;538unsigned long length;539#ifdef _WIN32540HANDLE mapping;541#endif542};543544#if (defined(HAVE_MMAP) || defined(_WIN32)) && !defined(HB_NO_MMAP)545static void546_hb_mapped_file_destroy (void *file_)547{548hb_mapped_file_t *file = (hb_mapped_file_t *) file_;549#ifdef HAVE_MMAP550munmap (file->contents, file->length);551#elif defined(_WIN32)552UnmapViewOfFile (file->contents);553CloseHandle (file->mapping);554#else555assert (0); // If we don't have mmap we shouldn't reach here556#endif557558hb_free (file);559}560#endif561562#ifdef _PATH_RSRCFORKSPEC563static int564_open_resource_fork (const char *file_name, hb_mapped_file_t *file)565{566size_t name_len = strlen (file_name);567size_t len = name_len + sizeof (_PATH_RSRCFORKSPEC);568569char *rsrc_name = (char *) hb_malloc (len);570if (unlikely (!rsrc_name)) return -1;571572strncpy (rsrc_name, file_name, name_len);573strncpy (rsrc_name + name_len, _PATH_RSRCFORKSPEC,574sizeof (_PATH_RSRCFORKSPEC));575576int fd = open (rsrc_name, O_RDONLY | O_BINARY, 0);577hb_free (rsrc_name);578579if (fd != -1)580{581struct stat st;582if (fstat (fd, &st) != -1)583file->length = (unsigned long) st.st_size;584else585{586close (fd);587fd = -1;588}589}590591return fd;592}593#endif594595/**596* hb_blob_create_from_file:597* @file_name: A font filename598*599* Creates a new blob containing the data from the600* specified binary font file.601*602* Returns: An #hb_blob_t pointer with the content of the file,603* or hb_blob_get_empty() if failed.604*605* Since: 1.7.7606**/607hb_blob_t *608hb_blob_create_from_file (const char *file_name)609{610hb_blob_t *blob = hb_blob_create_from_file_or_fail (file_name);611return likely (blob) ? blob : hb_blob_get_empty ();612}613614/**615* hb_blob_create_from_file_or_fail:616* @file_name: A font filename617*618* Creates a new blob containing the data from the619* specified binary font file.620*621* Returns: An #hb_blob_t pointer with the content of the file,622* or %NULL if failed.623*624* Since: 2.8.2625**/626hb_blob_t *627hb_blob_create_from_file_or_fail (const char *file_name)628{629/* Adopted from glib's gmappedfile.c with Matthias Clasen and630Allison Lortie permission but changed a lot to suit our need. */631#if defined(HAVE_MMAP) && !defined(HB_NO_MMAP)632hb_mapped_file_t *file = (hb_mapped_file_t *) hb_calloc (1, sizeof (hb_mapped_file_t));633if (unlikely (!file)) return nullptr;634635int fd = open (file_name, O_RDONLY | O_BINARY, 0);636if (unlikely (fd == -1)) goto fail_without_close;637638struct stat st;639if (unlikely (fstat (fd, &st) == -1)) goto fail;640641file->length = (unsigned long) st.st_size;642643#ifdef _PATH_RSRCFORKSPEC644if (unlikely (file->length == 0))645{646int rfd = _open_resource_fork (file_name, file);647if (rfd != -1)648{649close (fd);650fd = rfd;651}652}653#endif654655file->contents = (char *) mmap (nullptr, file->length, PROT_READ,656MAP_PRIVATE | MAP_NORESERVE, fd, 0);657658if (unlikely (file->contents == MAP_FAILED)) goto fail;659660close (fd);661662return hb_blob_create_or_fail (file->contents, file->length,663HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE, (void *) file,664(hb_destroy_func_t) _hb_mapped_file_destroy);665666fail:667close (fd);668fail_without_close:669hb_free (file);670671#elif defined(_WIN32) && !defined(HB_NO_MMAP)672hb_mapped_file_t *file = (hb_mapped_file_t *) hb_calloc (1, sizeof (hb_mapped_file_t));673if (unlikely (!file)) return nullptr;674675HANDLE fd;676unsigned int size = strlen (file_name) + 1;677wchar_t * wchar_file_name = (wchar_t *) hb_malloc (sizeof (wchar_t) * size);678if (unlikely (!wchar_file_name)) goto fail_without_close;679mbstowcs (wchar_file_name, file_name, size);680#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)681{682CREATEFILE2_EXTENDED_PARAMETERS ceparams = { 0 };683ceparams.dwSize = sizeof(CREATEFILE2_EXTENDED_PARAMETERS);684ceparams.dwFileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED & 0xFFFF;685ceparams.dwFileFlags = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED & 0xFFF00000;686ceparams.dwSecurityQosFlags = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED & 0x000F0000;687ceparams.lpSecurityAttributes = nullptr;688ceparams.hTemplateFile = nullptr;689fd = CreateFile2 (wchar_file_name, GENERIC_READ, FILE_SHARE_READ,690OPEN_EXISTING, &ceparams);691}692#else693fd = CreateFileW (wchar_file_name, GENERIC_READ, FILE_SHARE_READ, nullptr,694OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,695nullptr);696#endif697hb_free (wchar_file_name);698699if (unlikely (fd == INVALID_HANDLE_VALUE)) goto fail_without_close;700701#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)702{703LARGE_INTEGER length;704GetFileSizeEx (fd, &length);705file->length = length.LowPart;706file->mapping = CreateFileMappingFromApp (fd, nullptr, PAGE_READONLY, length.QuadPart, nullptr);707}708#else709file->length = (unsigned long) GetFileSize (fd, nullptr);710file->mapping = CreateFileMapping (fd, nullptr, PAGE_READONLY, 0, 0, nullptr);711#endif712if (unlikely (!file->mapping)) goto fail;713714#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)715file->contents = (char *) MapViewOfFileFromApp (file->mapping, FILE_MAP_READ, 0, 0);716#else717file->contents = (char *) MapViewOfFile (file->mapping, FILE_MAP_READ, 0, 0, 0);718#endif719if (unlikely (!file->contents)) goto fail;720721CloseHandle (fd);722return hb_blob_create_or_fail (file->contents, file->length,723HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE, (void *) file,724(hb_destroy_func_t) _hb_mapped_file_destroy);725726fail:727CloseHandle (fd);728fail_without_close:729hb_free (file);730731#endif732733/* The following tries to read a file without knowing its size beforehand734It's used as a fallback for systems without mmap or to read from pipes */735unsigned long len = 0, allocated = BUFSIZ * 16;736char *data = (char *) hb_malloc (allocated);737if (unlikely (!data)) return nullptr;738739FILE *fp = fopen (file_name, "rb");740if (unlikely (!fp)) goto fread_fail_without_close;741742while (!feof (fp))743{744if (allocated - len < BUFSIZ)745{746allocated *= 2;747/* Don't allocate and go more than ~536MB, our mmap reader still748can cover files like that but lets limit our fallback reader */749if (unlikely (allocated > (2 << 28))) goto fread_fail;750char *new_data = (char *) hb_realloc (data, allocated);751if (unlikely (!new_data)) goto fread_fail;752data = new_data;753}754755unsigned long addition = fread (data + len, 1, allocated - len, fp);756757int err = ferror (fp);758#ifdef EINTR // armcc doesn't have it759if (unlikely (err == EINTR)) continue;760#endif761if (unlikely (err)) goto fread_fail;762763len += addition;764}765fclose (fp);766767return hb_blob_create_or_fail (data, len, HB_MEMORY_MODE_WRITABLE, data,768(hb_destroy_func_t) hb_free);769770fread_fail:771fclose (fp);772fread_fail_without_close:773hb_free (data);774return nullptr;775}776#endif /* !HB_NO_OPEN */777778779