/* * Copyright © 2006 Keith Packard * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that copyright * notice and this permission notice appear in supporting documentation, and * that the name of the copyright holders not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. The copyright holders make no representations * about the suitability of this software for any purpose. It is provided "as * is" without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE * OF THIS SOFTWARE. */ #include "fcint.h" intptr_t FcAlignSize (intptr_t size) { intptr_t rem = size % sizeof (FcAlign); if (rem) size += sizeof (FcAlign) - rem; return size; } /* * Serialization helper object -- allocate space in the * yet-to-be-created linear array for a serialized font set */ FcSerialize * FcSerializeCreate (void) { FcSerialize *serialize; serialize = malloc (sizeof (FcSerialize)); if (!serialize) return NULL; serialize->size = 0; serialize->linear = NULL; serialize->cs_freezer = NULL; serialize->buckets = NULL; serialize->buckets_count = 0; serialize->buckets_used = 0; serialize->buckets_used_max = 0; return serialize; } void FcSerializeDestroy (FcSerialize *serialize) { free (serialize->buckets); if (serialize->cs_freezer) FcCharSetFreezerDestroy (serialize->cs_freezer); free (serialize); } static size_t FcSerializeNextBucketIndex (const FcSerialize *serialize, size_t index) { if (index == 0) index = serialize->buckets_count; --index; return index; } #if ((SIZEOF_VOID_P) * (CHAR_BIT)) == 32 /* * Based on triple32 * https://github.com/skeeto/hash-prospector */ static uintptr_t FcSerializeHashPtr (const void *object) { uintptr_t x = (uintptr_t)object; x ^= x >> 17; x *= 0xed5ad4bbU; x ^= x >> 11; x *= 0xac4c1b51U; x ^= x >> 15; x *= 0x31848babU; x ^= x >> 14; return x ? x : 1; /* 0 reserved to mark empty, x starts out 0 */ } #elif ((SIZEOF_VOID_P) * (CHAR_BIT)) == 64 /* * Based on splittable64/splitmix64 from Mix13 * https://zimbry.blogspot.com/2011/09/better-bit-mixing-improving-on.html * https://prng.di.unimi.it/splitmix64.c */ static uintptr_t FcSerializeHashPtr (const void *object) { uintptr_t x = (uintptr_t)object; x ^= x >> 30; x *= 0xbf58476d1ce4e5b9U; x ^= x >> 27; x *= 0x94d049bb133111ebU; x ^= x >> 31; return x ? x : 1; /* 0 reserved to mark empty, x starts out 0 */ } #endif static FcSerializeBucket* FcSerializeFind (const FcSerialize *serialize, const void *object) { uintptr_t hash = FcSerializeHashPtr (object); size_t buckets_count = serialize->buckets_count; size_t index = hash & (buckets_count-1); for (size_t n = 0; n < buckets_count; ++n) { FcSerializeBucket* bucket = &serialize->buckets[index]; if (bucket->hash == 0) { return NULL; } if (object == bucket->object) { return bucket; } index = FcSerializeNextBucketIndex (serialize, index); } return NULL; } static FcSerializeBucket* FcSerializeUncheckedSet (FcSerialize *serialize, FcSerializeBucket* insert) { const void *object = insert->object; size_t buckets_count = serialize->buckets_count; size_t index = insert->hash & (buckets_count-1); for (size_t n = 0; n < buckets_count; ++n) { FcSerializeBucket* bucket = &serialize->buckets[index]; if (bucket->hash == 0) { *bucket = *insert; ++serialize->buckets_used; return bucket; } if (object == bucket->object) { /* FcSerializeAlloc should not allow this to happen. */ assert (0); *bucket = *insert; return bucket; } index = FcSerializeNextBucketIndex (serialize, index); } assert (0); return NULL; } static FcBool FcSerializeResize (FcSerialize *serialize, size_t new_count) { size_t old_used = serialize->buckets_used; size_t old_count = serialize->buckets_count; FcSerializeBucket *old_buckets = serialize->buckets; FcSerializeBucket *old_buckets_end = old_buckets + old_count; FcSerializeBucket *new_buckets = malloc (new_count * sizeof (*old_buckets)); if (!new_buckets) return FcFalse; FcSerializeBucket *new_buckets_end = new_buckets + new_count; for (FcSerializeBucket *b = new_buckets; b < new_buckets_end; ++b) b->hash = 0; serialize->buckets = new_buckets; serialize->buckets_count = new_count; serialize->buckets_used = 0; for (FcSerializeBucket *b = old_buckets; b < old_buckets_end; ++b) if (b->hash != 0 && !FcSerializeUncheckedSet (serialize, b)) { serialize->buckets = old_buckets; serialize->buckets_count = old_count; serialize->buckets_used = old_used; free (new_buckets); return FcFalse; } free (old_buckets); return FcTrue; } static FcSerializeBucket* FcSerializeSet (FcSerialize *serialize, const void *object, intptr_t offset) { if (serialize->buckets_used >= serialize->buckets_used_max) { size_t capacity = serialize->buckets_count; if (capacity == 0) capacity = 4; else if (capacity > SIZE_MAX / 2u) return NULL; else capacity *= 2; if (!FcSerializeResize (serialize, capacity)) return NULL; serialize->buckets_used_max = capacity / 4u * 3u; } FcSerializeBucket bucket; bucket.object = object; bucket.offset = offset; bucket.hash = FcSerializeHashPtr (object); return FcSerializeUncheckedSet (serialize, &bucket); } /* * Allocate space for an object in the serialized array. Keep track * of where the object is placed and only allocate one copy of each object */ FcBool FcSerializeAlloc (FcSerialize *serialize, const void *object, int size) { FcSerializeBucket *bucket = FcSerializeFind (serialize, object); if (bucket) return FcTrue; if (!FcSerializeSet (serialize, object, serialize->size)) return FcFalse; serialize->size += FcAlignSize (size); return FcTrue; } /* * Reserve space in the serialization array */ intptr_t FcSerializeReserve (FcSerialize *serialize, int size) { intptr_t offset = serialize->size; serialize->size += FcAlignSize (size); return offset; } /* * Given an object, return the offset in the serialized array where * the serialized copy of the object is stored */ intptr_t FcSerializeOffset (FcSerialize *serialize, const void *object) { FcSerializeBucket *bucket = FcSerializeFind (serialize, object); return bucket ? bucket->offset : 0; } /* * Given a cache and an object, return a pointer to where * the serialized copy of the object is stored */ void * FcSerializePtr (FcSerialize *serialize, const void *object) { intptr_t offset = FcSerializeOffset (serialize, object); if (!offset) return NULL; return (void *) ((char *) serialize->linear + offset); } FcBool FcStrSerializeAlloc (FcSerialize *serialize, const FcChar8 *str) { return FcSerializeAlloc (serialize, str, strlen ((const char *) str) + 1); } FcChar8 * FcStrSerialize (FcSerialize *serialize, const FcChar8 *str) { FcChar8 *str_serialize = FcSerializePtr (serialize, str); if (!str_serialize) return NULL; strcpy ((char *) str_serialize, (const char *) str); return str_serialize; } #include "fcaliastail.h" #undef __fcserialize__