Coverage Report

Created: 2025-07-07 10:01

/work/workdir/UnpackedTarball/fontconfig/src/fcserialize.c
Line
Count
Source (jump to first uncovered line)
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
632
{
28
632
    intptr_t rem = size % sizeof (FcAlign);
29
632
    if (rem)
30
144
  size += sizeof (FcAlign) - rem;
31
632
    return size;
32
632
}
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
1
{
42
1
    FcSerialize *serialize;
43
44
1
    serialize = malloc (sizeof (FcSerialize));
45
1
    if (!serialize)
46
0
  return NULL;
47
1
    serialize->size = 0;
48
1
    serialize->linear = NULL;
49
1
    serialize->cs_freezer = NULL;
50
1
    serialize->buckets = NULL;
51
1
    serialize->buckets_count = 0;
52
1
    serialize->buckets_used = 0;
53
1
    serialize->buckets_used_max = 0;
54
1
    return serialize;
55
1
}
56
57
void
58
FcSerializeDestroy (FcSerialize *serialize)
59
1
{
60
1
    free (serialize->buckets);
61
1
    if (serialize->cs_freezer)
62
1
  FcCharSetFreezerDestroy (serialize->cs_freezer);
63
1
    free (serialize);
64
1
}
65
66
static size_t
67
FcSerializeNextBucketIndex (const FcSerialize *serialize, size_t index)
68
3.79k
{
69
3.79k
    if (index == 0)
70
26
  index = serialize->buckets_count;
71
3.79k
    --index;
72
3.79k
    return index;
73
3.79k
}
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
#elif ((SIZEOF_VOID_P) * (CHAR_BIT)) == 64
96
97
/*
98
 * Based on splittable64/splitmix64 from Mix13
99
 * https://zimbry.blogspot.com/2011/09/better-bit-mixing-improving-on.html
100
 * https://prng.di.unimi.it/splitmix64.c
101
 */
102
static uintptr_t
103
FcSerializeHashPtr (const void *object)
104
2.38k
{
105
2.38k
    uintptr_t x = (uintptr_t)object;
106
2.38k
    x ^= x >> 30;
107
2.38k
    x *= 0xbf58476d1ce4e5b9U;
108
2.38k
    x ^= x >> 27;
109
2.38k
    x *= 0x94d049bb133111ebU;
110
2.38k
    x ^= x >> 31;
111
2.38k
    return x ? x : 1; /* 0 reserved to mark empty, x starts out 0 */
112
2.38k
}
113
114
#endif
115
116
static FcSerializeBucket *
117
FcSerializeFind (const FcSerialize *serialize, const void *object)
118
1.75k
{
119
1.75k
    uintptr_t hash = FcSerializeHashPtr (object);
120
1.75k
    size_t    buckets_count = serialize->buckets_count;
121
1.75k
    size_t    index = hash & (buckets_count - 1);
122
3.89k
    for (size_t n = 0; n < buckets_count; ++n) {
123
3.89k
  FcSerializeBucket *bucket = &serialize->buckets[index];
124
3.89k
  if (bucket->hash == 0) {
125
630
      return NULL;
126
630
  }
127
3.26k
  if (object == bucket->object) {
128
1.12k
      return bucket;
129
1.12k
  }
130
2.14k
  index = FcSerializeNextBucketIndex (serialize, index);
131
2.14k
    }
132
1
    return NULL;
133
1.75k
}
134
135
static FcSerializeBucket *
136
FcSerializeUncheckedSet (FcSerialize *serialize, FcSerializeBucket *insert)
137
1.39k
{
138
1.39k
    const void *object = insert->object;
139
1.39k
    size_t      buckets_count = serialize->buckets_count;
140
1.39k
    size_t      index = insert->hash & (buckets_count - 1);
141
3.05k
    for (size_t n = 0; n < buckets_count; ++n) {
142
3.05k
  FcSerializeBucket *bucket = &serialize->buckets[index];
143
3.05k
  if (bucket->hash == 0) {
144
1.39k
      *bucket = *insert;
145
1.39k
      ++serialize->buckets_used;
146
1.39k
      return bucket;
147
1.39k
  }
148
1.65k
  if (object == bucket->object) {
149
      /* FcSerializeAlloc should not allow this to happen. */
150
0
      assert (0);
151
0
      *bucket = *insert;
152
0
      return bucket;
153
0
  }
154
1.65k
  index = FcSerializeNextBucketIndex (serialize, index);
155
1.65k
    }
156
0
    assert (0);
157
0
    return NULL;
158
0
}
159
160
static FcBool
161
FcSerializeResize (FcSerialize *serialize, size_t new_count)
162
9
{
163
9
    size_t             old_used = serialize->buckets_used;
164
9
    size_t             old_count = serialize->buckets_count;
165
9
    FcSerializeBucket *old_buckets = serialize->buckets;
166
9
    FcSerializeBucket *old_buckets_end = old_buckets ? old_buckets + old_count : NULL;
167
168
9
    FcSerializeBucket *new_buckets = malloc (new_count * sizeof (*old_buckets));
169
9
    if (!new_buckets)
170
0
  return FcFalse;
171
9
    FcSerializeBucket *new_buckets_end = new_buckets + new_count;
172
2.05k
    for (FcSerializeBucket *b = new_buckets; b < new_buckets_end; ++b)
173
2.04k
  b->hash = 0;
174
175
9
    serialize->buckets = new_buckets;
176
9
    serialize->buckets_count = new_count;
177
9
    serialize->buckets_used = 0;
178
1.02k
    for (FcSerializeBucket *b = old_buckets; b < old_buckets_end; ++b)
179
1.02k
  if (b->hash != 0 && !FcSerializeUncheckedSet (serialize, b)) {
180
0
      serialize->buckets = old_buckets;
181
0
      serialize->buckets_count = old_count;
182
0
      serialize->buckets_used = old_used;
183
0
      free (new_buckets);
184
0
      return FcFalse;
185
0
  }
186
9
    free (old_buckets);
187
9
    return FcTrue;
188
9
}
189
190
static FcSerializeBucket *
191
FcSerializeSet (FcSerialize *serialize, const void *object, intptr_t offset)
192
631
{
193
631
    if (serialize->buckets_used >= serialize->buckets_used_max) {
194
9
  size_t capacity = serialize->buckets_count;
195
9
  if (capacity == 0)
196
1
      capacity = 4;
197
8
  else if (capacity > SIZE_MAX / 2u)
198
0
      return NULL;
199
8
  else
200
8
      capacity *= 2;
201
202
9
  if (!FcSerializeResize (serialize, capacity))
203
0
      return NULL;
204
205
9
  serialize->buckets_used_max = capacity / 4u * 3u;
206
9
    }
207
208
631
    FcSerializeBucket bucket;
209
631
    bucket.object = object;
210
631
    bucket.offset = offset;
211
631
    bucket.hash = FcSerializeHashPtr (object);
212
631
    return FcSerializeUncheckedSet (serialize, &bucket);
213
631
}
214
215
/*
216
 * Allocate space for an object in the serialized array. Keep track
217
 * of where the object is placed and only allocate one copy of each object
218
 */
219
FcBool
220
FcSerializeAlloc (FcSerialize *serialize, const void *object, int size)
221
879
{
222
879
    FcSerializeBucket *bucket = FcSerializeFind (serialize, object);
223
879
    if (bucket)
224
248
  return FcTrue;
225
226
631
    if (!FcSerializeSet (serialize, object, serialize->size))
227
0
  return FcFalse;
228
229
631
    serialize->size += FcAlignSize (size);
230
631
    return FcTrue;
231
631
}
232
233
/*
234
 * Reserve space in the serialization array
235
 */
236
intptr_t
237
FcSerializeReserve (FcSerialize *serialize, int size)
238
1
{
239
1
    intptr_t offset = serialize->size;
240
1
    serialize->size += FcAlignSize (size);
241
1
    return offset;
242
1
}
243
244
/*
245
 * Given an object, return the offset in the serialized array where
246
 * the serialized copy of the object is stored
247
 */
248
intptr_t
249
FcSerializeOffset (FcSerialize *serialize, const void *object)
250
879
{
251
879
    FcSerializeBucket *bucket = FcSerializeFind (serialize, object);
252
879
    return bucket ? bucket->offset : 0;
253
879
}
254
255
/*
256
 * Given a cache and an object, return a pointer to where
257
 * the serialized copy of the object is stored
258
 */
259
void *
260
FcSerializePtr (FcSerialize *serialize, const void *object)
261
879
{
262
879
    intptr_t offset = FcSerializeOffset (serialize, object);
263
264
879
    if (!offset)
265
0
  return NULL;
266
879
    return (void *)((char *)serialize->linear + offset);
267
879
}
268
269
FcBool
270
FcStrSerializeAlloc (FcSerialize *serialize, const FcChar8 *str)
271
156
{
272
156
    return FcSerializeAlloc (serialize, str, strlen ((const char *)str) + 1);
273
156
}
274
275
FcChar8 *
276
FcStrSerialize (FcSerialize *serialize, const FcChar8 *str)
277
156
{
278
156
    FcChar8 *str_serialize = FcSerializePtr (serialize, str);
279
156
    if (!str_serialize)
280
0
  return NULL;
281
156
    strcpy ((char *)str_serialize, (const char *)str);
282
156
    return str_serialize;
283
156
}
284
#include "fcaliastail.h"
285
#undef __fcserialize__