Home | History | Annotate | Line # | Download | only in src
      1 /*
      2  * Copyright  2006 Keith Packard
      3  *
      4  * Permission to use, copy, modify, distribute, and sell this software and its
      5  * documentation for any purpose is hereby granted without fee, provided that
      6  * the above copyright notice appear in all copies and that both that copyright
      7  * notice and this permission notice appear in supporting documentation, and
      8  * that the name of the copyright holders not be used in advertising or
      9  * publicity pertaining to distribution of the software without specific,
     10  * written prior permission.  The copyright holders make no representations
     11  * about the suitability of this software for any purpose.  It is provided "as
     12  * is" without express or implied warranty.
     13  *
     14  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
     15  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
     16  * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
     17  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
     18  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
     19  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
     20  * OF THIS SOFTWARE.
     21  */
     22 
     23 #include "fcint.h"
     24 
     25 intptr_t
     26 FcAlignSize (intptr_t size)
     27 {
     28     intptr_t	rem = size % sizeof (FcAlign);
     29     if (rem)
     30 	size += sizeof (FcAlign) - rem;
     31     return size;
     32 }
     33 
     34 /*
     35  * Serialization helper object -- allocate space in the
     36  * yet-to-be-created linear array for a serialized font set
     37  */
     38 
     39 FcSerialize *
     40 FcSerializeCreate (void)
     41 {
     42     FcSerialize	*serialize;
     43 
     44     serialize = malloc (sizeof (FcSerialize));
     45     if (!serialize)
     46 	return NULL;
     47     serialize->size = 0;
     48     serialize->linear = NULL;
     49     serialize->cs_freezer = NULL;
     50     serialize->buckets = NULL;
     51     serialize->buckets_count = 0;
     52     serialize->buckets_used = 0;
     53     serialize->buckets_used_max = 0;
     54     return serialize;
     55 }
     56 
     57 void
     58 FcSerializeDestroy (FcSerialize *serialize)
     59 {
     60     free (serialize->buckets);
     61     if (serialize->cs_freezer)
     62 	FcCharSetFreezerDestroy (serialize->cs_freezer);
     63     free (serialize);
     64 }
     65 
     66 static size_t
     67 FcSerializeNextBucketIndex (const FcSerialize *serialize, size_t index)
     68 {
     69     if (index == 0)
     70 	index = serialize->buckets_count;
     71     --index;
     72     return index;
     73 }
     74 
     75 #if ((SIZEOF_VOID_P) * (CHAR_BIT)) == 32
     76 
     77 /*
     78  * Based on triple32
     79  * https://github.com/skeeto/hash-prospector
     80  */
     81 static uintptr_t
     82 FcSerializeHashPtr (const void *object)
     83 {
     84     uintptr_t x = (uintptr_t)object;
     85     x ^= x >> 17;
     86     x *= 0xed5ad4bbU;
     87     x ^= x >> 11;
     88     x *= 0xac4c1b51U;
     89     x ^= x >> 15;
     90     x *= 0x31848babU;
     91     x ^= x >> 14;
     92     return x ? x : 1; /* 0 reserved to mark empty, x starts out 0 */
     93 }
     94 
     95 
     96 #elif ((SIZEOF_VOID_P) * (CHAR_BIT)) == 64
     97 
     98 /*
     99  * Based on splittable64/splitmix64 from Mix13
    100  * https://zimbry.blogspot.com/2011/09/better-bit-mixing-improving-on.html
    101  * https://prng.di.unimi.it/splitmix64.c
    102  */
    103 static uintptr_t
    104 FcSerializeHashPtr (const void *object)
    105 {
    106     uintptr_t x = (uintptr_t)object;
    107     x ^= x >> 30;
    108     x *= 0xbf58476d1ce4e5b9U;
    109     x ^= x >> 27;
    110     x *= 0x94d049bb133111ebU;
    111     x ^= x >> 31;
    112     return x ? x : 1; /* 0 reserved to mark empty, x starts out 0 */
    113 }
    114 
    115 #endif
    116 
    117 static FcSerializeBucket*
    118 FcSerializeFind (const FcSerialize *serialize, const void *object)
    119 {
    120     uintptr_t hash = FcSerializeHashPtr (object);
    121     size_t buckets_count = serialize->buckets_count;
    122     size_t index = hash & (buckets_count-1);
    123     for (size_t n = 0; n < buckets_count; ++n) {
    124 	FcSerializeBucket* bucket = &serialize->buckets[index];
    125 	if (bucket->hash == 0) {
    126 	    return NULL;
    127 	}
    128 	if (object == bucket->object) {
    129 	    return bucket;
    130 	}
    131 	index = FcSerializeNextBucketIndex (serialize, index);
    132     }
    133     return NULL;
    134 }
    135 
    136 static FcSerializeBucket*
    137 FcSerializeUncheckedSet (FcSerialize *serialize, FcSerializeBucket* insert) {
    138     const void *object = insert->object;
    139     size_t buckets_count = serialize->buckets_count;
    140     size_t index = insert->hash & (buckets_count-1);
    141     for (size_t n = 0; n < buckets_count; ++n) {
    142 	FcSerializeBucket* bucket = &serialize->buckets[index];
    143 	if (bucket->hash == 0) {
    144 	    *bucket = *insert;
    145 	    ++serialize->buckets_used;
    146 	    return bucket;
    147 	}
    148 	if (object == bucket->object) {
    149 	    /* FcSerializeAlloc should not allow this to happen. */
    150 	    assert (0);
    151 	    *bucket = *insert;
    152 	    return bucket;
    153 	}
    154 	index = FcSerializeNextBucketIndex (serialize, index);
    155     }
    156     assert (0);
    157     return NULL;
    158 }
    159 
    160 static FcBool
    161 FcSerializeResize (FcSerialize *serialize, size_t new_count)
    162 {
    163     size_t old_used = serialize->buckets_used;
    164     size_t old_count = serialize->buckets_count;
    165     FcSerializeBucket *old_buckets = serialize->buckets;
    166     FcSerializeBucket *old_buckets_end = old_buckets + old_count;
    167 
    168     FcSerializeBucket *new_buckets = malloc (new_count * sizeof (*old_buckets));
    169     if (!new_buckets)
    170 	return FcFalse;
    171     FcSerializeBucket *new_buckets_end = new_buckets + new_count;
    172     for (FcSerializeBucket *b = new_buckets; b < new_buckets_end; ++b)
    173 	b->hash = 0;
    174 
    175     serialize->buckets = new_buckets;
    176     serialize->buckets_count = new_count;
    177     serialize->buckets_used = 0;
    178     for (FcSerializeBucket *b = old_buckets; b < old_buckets_end; ++b)
    179 	if (b->hash != 0 && !FcSerializeUncheckedSet (serialize, b))
    180 	{
    181 	    serialize->buckets = old_buckets;
    182 	    serialize->buckets_count = old_count;
    183 	    serialize->buckets_used = old_used;
    184 	    free (new_buckets);
    185 	    return FcFalse;
    186 	}
    187     free (old_buckets);
    188     return FcTrue;
    189 }
    190 
    191 static FcSerializeBucket*
    192 FcSerializeSet (FcSerialize *serialize, const void *object, intptr_t offset)
    193 {
    194     if (serialize->buckets_used >= serialize->buckets_used_max)
    195     {
    196 	size_t capacity = serialize->buckets_count;
    197 	if (capacity == 0)
    198 	    capacity = 4;
    199 	else if (capacity > SIZE_MAX / 2u)
    200 	    return NULL;
    201 	else
    202 	    capacity *= 2;
    203 
    204 	if (!FcSerializeResize (serialize, capacity))
    205 	    return NULL;
    206 
    207 	serialize->buckets_used_max = capacity / 4u * 3u;
    208     }
    209 
    210     FcSerializeBucket bucket;
    211     bucket.object = object;
    212     bucket.offset = offset;
    213     bucket.hash = FcSerializeHashPtr (object);
    214     return FcSerializeUncheckedSet (serialize, &bucket);
    215 }
    216 
    217 /*
    218  * Allocate space for an object in the serialized array. Keep track
    219  * of where the object is placed and only allocate one copy of each object
    220  */
    221 FcBool
    222 FcSerializeAlloc (FcSerialize *serialize, const void *object, int size)
    223 {
    224     FcSerializeBucket *bucket = FcSerializeFind (serialize, object);
    225     if (bucket)
    226 	return FcTrue;
    227 
    228     if (!FcSerializeSet (serialize, object, serialize->size))
    229 	return FcFalse;
    230 
    231     serialize->size += FcAlignSize (size);
    232     return FcTrue;
    233 }
    234 
    235 /*
    236  * Reserve space in the serialization array
    237  */
    238 intptr_t
    239 FcSerializeReserve (FcSerialize *serialize, int size)
    240 {
    241     intptr_t	offset = serialize->size;
    242     serialize->size += FcAlignSize (size);
    243     return offset;
    244 }
    245 
    246 /*
    247  * Given an object, return the offset in the serialized array where
    248  * the serialized copy of the object is stored
    249  */
    250 intptr_t
    251 FcSerializeOffset (FcSerialize *serialize, const void *object)
    252 {
    253     FcSerializeBucket *bucket = FcSerializeFind (serialize, object);
    254     return bucket ? bucket->offset : 0;
    255 }
    256 
    257 /*
    258  * Given a cache and an object, return a pointer to where
    259  * the serialized copy of the object is stored
    260  */
    261 void *
    262 FcSerializePtr (FcSerialize *serialize, const void *object)
    263 {
    264     intptr_t	offset = FcSerializeOffset (serialize, object);
    265 
    266     if (!offset)
    267 	return NULL;
    268     return (void *) ((char *) serialize->linear + offset);
    269 }
    270 
    271 FcBool
    272 FcStrSerializeAlloc (FcSerialize *serialize, const FcChar8 *str)
    273 {
    274     return FcSerializeAlloc (serialize, str, strlen ((const char *) str) + 1);
    275 }
    276 
    277 FcChar8 *
    278 FcStrSerialize (FcSerialize *serialize, const FcChar8 *str)
    279 {
    280     FcChar8 *str_serialize = FcSerializePtr (serialize, str);
    281     if (!str_serialize)
    282 	return NULL;
    283     strcpy ((char *) str_serialize, (const char *) str);
    284     return str_serialize;
    285 }
    286 #include "fcaliastail.h"
    287 #undef __fcserialize__
    288