]> git.wh0rd.org - fontconfig.git/blob - src/fccharset.c
cd9b158aca6081c47a89a780f453a6b938513665
[fontconfig.git] / src / fccharset.c
1 /*
2 * fontconfig/src/fccharset.c
3 *
4 * Copyright © 2001 Keith Packard
5 *
6 * Permission to use, copy, modify, distribute, and sell this software and its
7 * documentation for any purpose is hereby granted without fee, provided that
8 * the above copyright notice appear in all copies and that both that
9 * copyright notice and this permission notice appear in supporting
10 * documentation, and that the name of Keith Packard not be used in
11 * advertising or publicity pertaining to distribution of the software without
12 * specific, written prior permission. Keith Packard makes no
13 * representations about the suitability of this software for any purpose. It
14 * is provided "as is" without express or implied warranty.
15 *
16 * THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
18 * EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR
19 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
20 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
21 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
22 * PERFORMANCE OF THIS SOFTWARE.
23 */
24
25 #include "fcint.h"
26 #include <stdlib.h>
27
28 /* #define CHECK */
29
30 FcCharSet *
31 FcCharSetCreate (void)
32 {
33 FcCharSet *fcs;
34
35 fcs = (FcCharSet *) malloc (sizeof (FcCharSet));
36 if (!fcs)
37 return 0;
38 FcMemAlloc (FC_MEM_CHARSET, sizeof (FcCharSet));
39 fcs->ref = 1;
40 fcs->num = 0;
41 fcs->leaves_offset = 0;
42 fcs->numbers_offset = 0;
43 return fcs;
44 }
45
46 FcCharSet *
47 FcCharSetNew (void)
48 {
49 return FcCharSetCreate ();
50 }
51
52 void
53 FcCharSetDestroy (FcCharSet *fcs)
54 {
55 int i;
56
57 if (fcs->ref == FC_REF_CONSTANT)
58 {
59 FcCacheObjectDereference (fcs);
60 return;
61 }
62 if (--fcs->ref > 0)
63 return;
64 for (i = 0; i < fcs->num; i++)
65 {
66 FcMemFree (FC_MEM_CHARLEAF, sizeof (FcCharLeaf));
67 free (FcCharSetLeaf (fcs, i));
68 }
69 if (fcs->num)
70 {
71 /* the numbers here are estimates */
72 FcMemFree (FC_MEM_CHARSET, fcs->num * sizeof (intptr_t));
73 free (FcCharSetLeaves (fcs));
74 FcMemFree (FC_MEM_CHARSET, fcs->num * sizeof (FcChar16));
75 free (FcCharSetNumbers (fcs));
76 }
77 FcMemFree (FC_MEM_CHARSET, sizeof (FcCharSet));
78 free (fcs);
79 }
80
81 /*
82 * Search for the leaf containing with the specified num.
83 * Return its index if it exists, otherwise return negative of
84 * the (position + 1) where it should be inserted
85 */
86
87
88 static int
89 FcCharSetFindLeafForward (const FcCharSet *fcs, int start, FcChar16 num)
90 {
91 FcChar16 *numbers = FcCharSetNumbers(fcs);
92 FcChar16 page;
93 int low = start;
94 int high = fcs->num - 1;
95
96 if (!numbers)
97 return -1;
98 while (low <= high)
99 {
100 int mid = (low + high) >> 1;
101 page = numbers[mid];
102 if (page == num)
103 return mid;
104 if (page < num)
105 low = mid + 1;
106 else
107 high = mid - 1;
108 }
109 if (high < 0 || (high < fcs->num && numbers[high] < num))
110 high++;
111 return -(high + 1);
112 }
113
114 /*
115 * Locate the leaf containing the specified char, return
116 * its index if it exists, otherwise return negative of
117 * the (position + 1) where it should be inserted
118 */
119
120 static int
121 FcCharSetFindLeafPos (const FcCharSet *fcs, FcChar32 ucs4)
122 {
123 return FcCharSetFindLeafForward (fcs, 0, ucs4 >> 8);
124 }
125
126 static FcCharLeaf *
127 FcCharSetFindLeaf (const FcCharSet *fcs, FcChar32 ucs4)
128 {
129 int pos = FcCharSetFindLeafPos (fcs, ucs4);
130 if (pos >= 0)
131 return FcCharSetLeaf(fcs, pos);
132 return 0;
133 }
134
135 #define FC_IS_ZERO_OR_POWER_OF_TWO(x) (!((x) & ((x)-1)))
136
137 static FcBool
138 FcCharSetPutLeaf (FcCharSet *fcs,
139 FcChar32 ucs4,
140 FcCharLeaf *leaf,
141 int pos)
142 {
143 intptr_t *leaves = FcCharSetLeaves (fcs);
144 FcChar16 *numbers = FcCharSetNumbers (fcs);
145
146 ucs4 >>= 8;
147 /* XXX We can't handle Unicode values in Plane 16 */
148 if (ucs4 >= 0x10000)
149 return FcFalse;
150
151 if (FC_IS_ZERO_OR_POWER_OF_TWO (fcs->num))
152 {
153 if (!fcs->num)
154 {
155 unsigned int alloced = 8;
156 leaves = malloc (alloced * sizeof (*leaves));
157 numbers = malloc (alloced * sizeof (*numbers));
158 FcMemAlloc (FC_MEM_CHARSET, alloced * sizeof (*leaves));
159 FcMemAlloc (FC_MEM_CHARSET, alloced * sizeof (*numbers));
160 }
161 else
162 {
163 unsigned int alloced = fcs->num;
164 intptr_t *new_leaves, distance;
165
166 FcMemFree (FC_MEM_CHARSET, alloced * sizeof (*leaves));
167 FcMemFree (FC_MEM_CHARSET, alloced * sizeof (*numbers));
168
169 alloced *= 2;
170 new_leaves = realloc (leaves, alloced * sizeof (*leaves));
171 numbers = realloc (numbers, alloced * sizeof (*numbers));
172
173 FcMemAlloc (FC_MEM_CHARSET, alloced * sizeof (*leaves));
174 FcMemAlloc (FC_MEM_CHARSET, alloced * sizeof (*numbers));
175
176 distance = (intptr_t) new_leaves - (intptr_t) leaves;
177 if (new_leaves && distance)
178 {
179 int i;
180 for (i = 0; i < fcs->num; i++)
181 new_leaves[i] -= distance;
182 }
183 leaves = new_leaves;
184 }
185
186 if (!leaves || !numbers)
187 return FcFalse;
188
189 fcs->leaves_offset = FcPtrToOffset (fcs, leaves);
190 fcs->numbers_offset = FcPtrToOffset (fcs, numbers);
191 }
192
193 memmove (leaves + pos + 1, leaves + pos,
194 (fcs->num - pos) * sizeof (*leaves));
195 memmove (numbers + pos + 1, numbers + pos,
196 (fcs->num - pos) * sizeof (*numbers));
197 numbers[pos] = (FcChar16) ucs4;
198 leaves[pos] = FcPtrToOffset (leaves, leaf);
199 fcs->num++;
200 return FcTrue;
201 }
202
203 /*
204 * Locate the leaf containing the specified char, creating it
205 * if desired
206 */
207
208 FcCharLeaf *
209 FcCharSetFindLeafCreate (FcCharSet *fcs, FcChar32 ucs4)
210 {
211 int pos;
212 FcCharLeaf *leaf;
213
214 pos = FcCharSetFindLeafPos (fcs, ucs4);
215 if (pos >= 0)
216 return FcCharSetLeaf(fcs, pos);
217
218 leaf = calloc (1, sizeof (FcCharLeaf));
219 if (!leaf)
220 return 0;
221
222 pos = -pos - 1;
223 if (!FcCharSetPutLeaf (fcs, ucs4, leaf, pos))
224 {
225 free (leaf);
226 return 0;
227 }
228 FcMemAlloc (FC_MEM_CHARLEAF, sizeof (FcCharLeaf));
229 return leaf;
230 }
231
232 static FcBool
233 FcCharSetInsertLeaf (FcCharSet *fcs, FcChar32 ucs4, FcCharLeaf *leaf)
234 {
235 int pos;
236
237 pos = FcCharSetFindLeafPos (fcs, ucs4);
238 if (pos >= 0)
239 {
240 FcMemFree (FC_MEM_CHARLEAF, sizeof (FcCharLeaf));
241 free (FcCharSetLeaf (fcs, pos));
242 FcCharSetLeaves(fcs)[pos] = FcPtrToOffset (FcCharSetLeaves(fcs),
243 leaf);
244 return FcTrue;
245 }
246 pos = -pos - 1;
247 return FcCharSetPutLeaf (fcs, ucs4, leaf, pos);
248 }
249
250 FcBool
251 FcCharSetAddChar (FcCharSet *fcs, FcChar32 ucs4)
252 {
253 FcCharLeaf *leaf;
254 FcChar32 *b;
255
256 if (fcs->ref == FC_REF_CONSTANT)
257 return FcFalse;
258 leaf = FcCharSetFindLeafCreate (fcs, ucs4);
259 if (!leaf)
260 return FcFalse;
261 b = &leaf->map[(ucs4 & 0xff) >> 5];
262 *b |= (1 << (ucs4 & 0x1f));
263 return FcTrue;
264 }
265
266 /*
267 * An iterator for the leaves of a charset
268 */
269
270 typedef struct _fcCharSetIter {
271 FcCharLeaf *leaf;
272 FcChar32 ucs4;
273 int pos;
274 } FcCharSetIter;
275
276 /*
277 * Set iter->leaf to the leaf containing iter->ucs4 or higher
278 */
279
280 static void
281 FcCharSetIterSet (const FcCharSet *fcs, FcCharSetIter *iter)
282 {
283 int pos = FcCharSetFindLeafPos (fcs, iter->ucs4);
284
285 if (pos < 0)
286 {
287 pos = -pos - 1;
288 if (pos == fcs->num)
289 {
290 iter->ucs4 = ~0;
291 iter->leaf = 0;
292 return;
293 }
294 iter->ucs4 = (FcChar32) FcCharSetNumbers(fcs)[pos] << 8;
295 }
296 iter->leaf = FcCharSetLeaf(fcs, pos);
297 iter->pos = pos;
298 }
299
300 static void
301 FcCharSetIterNext (const FcCharSet *fcs, FcCharSetIter *iter)
302 {
303 int pos = iter->pos + 1;
304 if (pos >= fcs->num)
305 {
306 iter->ucs4 = ~0;
307 iter->leaf = 0;
308 }
309 else
310 {
311 iter->ucs4 = (FcChar32) FcCharSetNumbers(fcs)[pos] << 8;
312 iter->leaf = FcCharSetLeaf(fcs, pos);
313 iter->pos = pos;
314 }
315 }
316
317
318 static void
319 FcCharSetIterStart (const FcCharSet *fcs, FcCharSetIter *iter)
320 {
321 iter->ucs4 = 0;
322 iter->pos = 0;
323 FcCharSetIterSet (fcs, iter);
324 }
325
326 FcCharSet *
327 FcCharSetCopy (FcCharSet *src)
328 {
329 if (src->ref != FC_REF_CONSTANT)
330 src->ref++;
331 else
332 FcCacheObjectReference (src);
333 return src;
334 }
335
336 FcBool
337 FcCharSetEqual (const FcCharSet *a, const FcCharSet *b)
338 {
339 FcCharSetIter ai, bi;
340 int i;
341
342 if (a == b)
343 return FcTrue;
344 for (FcCharSetIterStart (a, &ai), FcCharSetIterStart (b, &bi);
345 ai.leaf && bi.leaf;
346 FcCharSetIterNext (a, &ai), FcCharSetIterNext (b, &bi))
347 {
348 if (ai.ucs4 != bi.ucs4)
349 return FcFalse;
350 for (i = 0; i < 256/32; i++)
351 if (ai.leaf->map[i] != bi.leaf->map[i])
352 return FcFalse;
353 }
354 return ai.leaf == bi.leaf;
355 }
356
357 static FcBool
358 FcCharSetAddLeaf (FcCharSet *fcs,
359 FcChar32 ucs4,
360 FcCharLeaf *leaf)
361 {
362 FcCharLeaf *new = FcCharSetFindLeafCreate (fcs, ucs4);
363 if (!new)
364 return FcFalse;
365 *new = *leaf;
366 return FcTrue;
367 }
368
369 static FcCharSet *
370 FcCharSetOperate (const FcCharSet *a,
371 const FcCharSet *b,
372 FcBool (*overlap) (FcCharLeaf *result,
373 const FcCharLeaf *al,
374 const FcCharLeaf *bl),
375 FcBool aonly,
376 FcBool bonly)
377 {
378 FcCharSet *fcs;
379 FcCharSetIter ai, bi;
380
381 fcs = FcCharSetCreate ();
382 if (!fcs)
383 goto bail0;
384 FcCharSetIterStart (a, &ai);
385 FcCharSetIterStart (b, &bi);
386 while ((ai.leaf || (bonly && bi.leaf)) && (bi.leaf || (aonly && ai.leaf)))
387 {
388 if (ai.ucs4 < bi.ucs4)
389 {
390 if (aonly)
391 {
392 if (!FcCharSetAddLeaf (fcs, ai.ucs4, ai.leaf))
393 goto bail1;
394 FcCharSetIterNext (a, &ai);
395 }
396 else
397 {
398 ai.ucs4 = bi.ucs4;
399 FcCharSetIterSet (a, &ai);
400 }
401 }
402 else if (bi.ucs4 < ai.ucs4 )
403 {
404 if (bonly)
405 {
406 if (!FcCharSetAddLeaf (fcs, bi.ucs4, bi.leaf))
407 goto bail1;
408 FcCharSetIterNext (b, &bi);
409 }
410 else
411 {
412 bi.ucs4 = ai.ucs4;
413 FcCharSetIterSet (b, &bi);
414 }
415 }
416 else
417 {
418 FcCharLeaf leaf;
419
420 if ((*overlap) (&leaf, ai.leaf, bi.leaf))
421 {
422 if (!FcCharSetAddLeaf (fcs, ai.ucs4, &leaf))
423 goto bail1;
424 }
425 FcCharSetIterNext (a, &ai);
426 FcCharSetIterNext (b, &bi);
427 }
428 }
429 return fcs;
430 bail1:
431 FcCharSetDestroy (fcs);
432 bail0:
433 return 0;
434 }
435
436 static FcBool
437 FcCharSetIntersectLeaf (FcCharLeaf *result,
438 const FcCharLeaf *al,
439 const FcCharLeaf *bl)
440 {
441 int i;
442 FcBool nonempty = FcFalse;
443
444 for (i = 0; i < 256/32; i++)
445 if ((result->map[i] = al->map[i] & bl->map[i]))
446 nonempty = FcTrue;
447 return nonempty;
448 }
449
450 FcCharSet *
451 FcCharSetIntersect (const FcCharSet *a, const FcCharSet *b)
452 {
453 return FcCharSetOperate (a, b, FcCharSetIntersectLeaf, FcFalse, FcFalse);
454 }
455
456 static FcBool
457 FcCharSetUnionLeaf (FcCharLeaf *result,
458 const FcCharLeaf *al,
459 const FcCharLeaf *bl)
460 {
461 int i;
462
463 for (i = 0; i < 256/32; i++)
464 result->map[i] = al->map[i] | bl->map[i];
465 return FcTrue;
466 }
467
468 FcCharSet *
469 FcCharSetUnion (const FcCharSet *a, const FcCharSet *b)
470 {
471 return FcCharSetOperate (a, b, FcCharSetUnionLeaf, FcTrue, FcTrue);
472 }
473
474 FcBool
475 FcCharSetMerge (FcCharSet *a, const FcCharSet *b, FcBool *changed)
476 {
477 int ai = 0, bi = 0;
478 FcChar16 an, bn;
479
480 if (a->ref == FC_REF_CONSTANT) {
481 if (changed)
482 *changed = FcFalse;
483 return FcFalse;
484 }
485
486 if (changed) {
487 *changed = !FcCharSetIsSubset(b, a);
488 if (!*changed)
489 return FcTrue;
490 }
491
492 while (bi < b->num)
493 {
494 an = ai < a->num ? FcCharSetNumbers(a)[ai] : ~0;
495 bn = FcCharSetNumbers(b)[bi];
496
497 if (an < bn)
498 {
499 ai = FcCharSetFindLeafForward (a, ai + 1, bn);
500 if (ai < 0)
501 ai = -ai - 1;
502 }
503 else
504 {
505 FcCharLeaf *bl = FcCharSetLeaf(b, bi);
506 if (bn < an)
507 {
508 if (!FcCharSetAddLeaf (a, bn << 8, bl))
509 return FcFalse;
510 }
511 else
512 {
513 FcCharLeaf *al = FcCharSetLeaf(a, ai);
514 FcCharSetUnionLeaf (al, al, bl);
515 }
516
517 ai++;
518 bi++;
519 }
520 }
521
522 return FcTrue;
523 }
524
525 static FcBool
526 FcCharSetSubtractLeaf (FcCharLeaf *result,
527 const FcCharLeaf *al,
528 const FcCharLeaf *bl)
529 {
530 int i;
531 FcBool nonempty = FcFalse;
532
533 for (i = 0; i < 256/32; i++)
534 if ((result->map[i] = al->map[i] & ~bl->map[i]))
535 nonempty = FcTrue;
536 return nonempty;
537 }
538
539 FcCharSet *
540 FcCharSetSubtract (const FcCharSet *a, const FcCharSet *b)
541 {
542 return FcCharSetOperate (a, b, FcCharSetSubtractLeaf, FcTrue, FcFalse);
543 }
544
545 FcBool
546 FcCharSetHasChar (const FcCharSet *fcs, FcChar32 ucs4)
547 {
548 FcCharLeaf *leaf = FcCharSetFindLeaf (fcs, ucs4);
549 if (!leaf)
550 return FcFalse;
551 return (leaf->map[(ucs4 & 0xff) >> 5] & (1 << (ucs4 & 0x1f))) != 0;
552 }
553
554 static FcChar32
555 FcCharSetPopCount (FcChar32 c1)
556 {
557 #if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
558 return __builtin_popcount (c1);
559 #else
560 /* hackmem 169 */
561 FcChar32 c2 = (c1 >> 1) & 033333333333;
562 c2 = c1 - c2 - ((c2 >> 1) & 033333333333);
563 return (((c2 + (c2 >> 3)) & 030707070707) % 077);
564 #endif
565 }
566
567 FcChar32
568 FcCharSetIntersectCount (const FcCharSet *a, const FcCharSet *b)
569 {
570 FcCharSetIter ai, bi;
571 FcChar32 count = 0;
572
573 FcCharSetIterStart (a, &ai);
574 FcCharSetIterStart (b, &bi);
575 while (ai.leaf && bi.leaf)
576 {
577 if (ai.ucs4 == bi.ucs4)
578 {
579 FcChar32 *am = ai.leaf->map;
580 FcChar32 *bm = bi.leaf->map;
581 int i = 256/32;
582 while (i--)
583 count += FcCharSetPopCount (*am++ & *bm++);
584 FcCharSetIterNext (a, &ai);
585 }
586 else if (ai.ucs4 < bi.ucs4)
587 {
588 ai.ucs4 = bi.ucs4;
589 FcCharSetIterSet (a, &ai);
590 }
591 if (bi.ucs4 < ai.ucs4)
592 {
593 bi.ucs4 = ai.ucs4;
594 FcCharSetIterSet (b, &bi);
595 }
596 }
597 return count;
598 }
599
600 FcChar32
601 FcCharSetCount (const FcCharSet *a)
602 {
603 FcCharSetIter ai;
604 FcChar32 count = 0;
605
606 for (FcCharSetIterStart (a, &ai); ai.leaf; FcCharSetIterNext (a, &ai))
607 {
608 int i = 256/32;
609 FcChar32 *am = ai.leaf->map;
610
611 while (i--)
612 count += FcCharSetPopCount (*am++);
613 }
614 return count;
615 }
616
617 FcChar32
618 FcCharSetSubtractCount (const FcCharSet *a, const FcCharSet *b)
619 {
620 FcCharSetIter ai, bi;
621 FcChar32 count = 0;
622
623 FcCharSetIterStart (a, &ai);
624 FcCharSetIterStart (b, &bi);
625 while (ai.leaf)
626 {
627 if (ai.ucs4 <= bi.ucs4)
628 {
629 FcChar32 *am = ai.leaf->map;
630 int i = 256/32;
631 if (ai.ucs4 == bi.ucs4)
632 {
633 FcChar32 *bm = bi.leaf->map;
634 while (i--)
635 count += FcCharSetPopCount (*am++ & ~*bm++);
636 }
637 else
638 {
639 while (i--)
640 count += FcCharSetPopCount (*am++);
641 }
642 FcCharSetIterNext (a, &ai);
643 }
644 else if (bi.leaf)
645 {
646 bi.ucs4 = ai.ucs4;
647 FcCharSetIterSet (b, &bi);
648 }
649 }
650 return count;
651 }
652
653 /*
654 * return FcTrue iff a is a subset of b
655 */
656 FcBool
657 FcCharSetIsSubset (const FcCharSet *a, const FcCharSet *b)
658 {
659 int ai, bi;
660 FcChar16 an, bn;
661
662 if (a == b) return FcTrue;
663 bi = 0;
664 ai = 0;
665 while (ai < a->num && bi < b->num)
666 {
667 an = FcCharSetNumbers(a)[ai];
668 bn = FcCharSetNumbers(b)[bi];
669 /*
670 * Check matching pages
671 */
672 if (an == bn)
673 {
674 FcChar32 *am = FcCharSetLeaf(a, ai)->map;
675 FcChar32 *bm = FcCharSetLeaf(b, bi)->map;
676
677 if (am != bm)
678 {
679 int i = 256/32;
680 /*
681 * Does am have any bits not in bm?
682 */
683 while (i--)
684 if (*am++ & ~*bm++)
685 return FcFalse;
686 }
687 ai++;
688 bi++;
689 }
690 /*
691 * Does a have any pages not in b?
692 */
693 else if (an < bn)
694 return FcFalse;
695 else
696 {
697 bi = FcCharSetFindLeafForward (b, bi + 1, an);
698 if (bi < 0)
699 bi = -bi - 1;
700 }
701 }
702 /*
703 * did we look at every page?
704 */
705 return ai >= a->num;
706 }
707
708 /*
709 * These two functions efficiently walk the entire charmap for
710 * other software (like pango) that want their own copy
711 */
712
713 FcChar32
714 FcCharSetNextPage (const FcCharSet *a,
715 FcChar32 map[FC_CHARSET_MAP_SIZE],
716 FcChar32 *next)
717 {
718 FcCharSetIter ai;
719 FcChar32 page;
720
721 ai.ucs4 = *next;
722 FcCharSetIterSet (a, &ai);
723 if (!ai.leaf)
724 return FC_CHARSET_DONE;
725
726 /*
727 * Save current information
728 */
729 page = ai.ucs4;
730 memcpy (map, ai.leaf->map, sizeof (ai.leaf->map));
731 /*
732 * Step to next page
733 */
734 FcCharSetIterNext (a, &ai);
735 *next = ai.ucs4;
736
737 return page;
738 }
739
740 FcChar32
741 FcCharSetFirstPage (const FcCharSet *a,
742 FcChar32 map[FC_CHARSET_MAP_SIZE],
743 FcChar32 *next)
744 {
745 *next = 0;
746 return FcCharSetNextPage (a, map, next);
747 }
748
749 /*
750 * old coverage API, rather hard to use correctly
751 */
752
753 FcChar32
754 FcCharSetCoverage (const FcCharSet *a, FcChar32 page, FcChar32 *result)
755 {
756 FcCharSetIter ai;
757
758 ai.ucs4 = page;
759 FcCharSetIterSet (a, &ai);
760 if (!ai.leaf)
761 {
762 memset (result, '\0', 256 / 8);
763 page = 0;
764 }
765 else
766 {
767 memcpy (result, ai.leaf->map, sizeof (ai.leaf->map));
768 FcCharSetIterNext (a, &ai);
769 page = ai.ucs4;
770 }
771 return page;
772 }
773
774 /*
775 * ASCII representation of charsets.
776 *
777 * Each leaf is represented as 9 32-bit values, the code of the first character followed
778 * by 8 32 bit values for the leaf itself. Each value is encoded as 5 ASCII characters,
779 * only 85 different values are used to avoid control characters as well as the other
780 * characters used to encode font names. 85**5 > 2^32 so things work out, but
781 * it's not exactly human readable output. As a special case, 0 is encoded as a space
782 */
783
784 static const unsigned char charToValue[256] = {
785 /* "" */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
786 /* "\b" */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
787 /* "\020" */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
788 /* "\030" */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
789 /* " " */ 0xff, 0x00, 0xff, 0x01, 0x02, 0x03, 0x04, 0xff,
790 /* "(" */ 0x05, 0x06, 0x07, 0x08, 0xff, 0xff, 0x09, 0x0a,
791 /* "0" */ 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12,
792 /* "8" */ 0x13, 0x14, 0xff, 0x15, 0x16, 0xff, 0x17, 0x18,
793 /* "@" */ 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
794 /* "H" */ 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
795 /* "P" */ 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
796 /* "X" */ 0x31, 0x32, 0x33, 0x34, 0xff, 0x35, 0x36, 0xff,
797 /* "`" */ 0xff, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d,
798 /* "h" */ 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45,
799 /* "p" */ 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d,
800 /* "x" */ 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0xff,
801 /* "\200" */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
802 /* "\210" */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
803 /* "\220" */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
804 /* "\230" */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
805 /* "\240" */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
806 /* "\250" */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
807 /* "\260" */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
808 /* "\270" */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
809 /* "\300" */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
810 /* "\310" */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
811 /* "\320" */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
812 /* "\330" */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
813 /* "\340" */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
814 /* "\350" */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
815 /* "\360" */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
816 /* "\370" */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
817 };
818
819 static const FcChar8 valueToChar[0x55] = {
820 /* 0x00 */ '!', '#', '$', '%', '&', '(', ')', '*',
821 /* 0x08 */ '+', '.', '/', '0', '1', '2', '3', '4',
822 /* 0x10 */ '5', '6', '7', '8', '9', ';', '<', '>',
823 /* 0x18 */ '?', '@', 'A', 'B', 'C', 'D', 'E', 'F',
824 /* 0x20 */ 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
825 /* 0x28 */ 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
826 /* 0x30 */ 'W', 'X', 'Y', 'Z', '[', ']', '^', 'a',
827 /* 0x38 */ 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
828 /* 0x40 */ 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
829 /* 0x48 */ 'r', 's', 't', 'u', 'v', 'w', 'x', 'y',
830 /* 0x50 */ 'z', '{', '|', '}', '~',
831 };
832
833 static FcChar8 *
834 FcCharSetParseValue (FcChar8 *string, FcChar32 *value)
835 {
836 int i;
837 FcChar32 v;
838 FcChar32 c;
839
840 if (*string == ' ')
841 {
842 v = 0;
843 string++;
844 }
845 else
846 {
847 v = 0;
848 for (i = 0; i < 5; i++)
849 {
850 if (!(c = (FcChar32) (unsigned char) *string++))
851 return 0;
852 c = charToValue[c];
853 if (c == 0xff)
854 return 0;
855 v = v * 85 + c;
856 }
857 }
858 *value = v;
859 return string;
860 }
861
862 static FcBool
863 FcCharSetUnparseValue (FcStrBuf *buf, FcChar32 value)
864 {
865 int i;
866 if (value == 0)
867 {
868 return FcStrBufChar (buf, ' ');
869 }
870 else
871 {
872 FcChar8 string[6];
873 FcChar8 *s = string + 5;
874 string[5] = '\0';
875 for (i = 0; i < 5; i++)
876 {
877 *--s = valueToChar[value % 85];
878 value /= 85;
879 }
880 for (i = 0; i < 5; i++)
881 if (!FcStrBufChar (buf, *s++))
882 return FcFalse;
883 }
884 return FcTrue;
885 }
886
887 FcCharSet *
888 FcNameParseCharSet (FcChar8 *string)
889 {
890 FcCharSet *c;
891 FcChar32 ucs4;
892 FcCharLeaf *leaf;
893 FcCharLeaf temp;
894 FcChar32 bits;
895 int i;
896
897 c = FcCharSetCreate ();
898 if (!c)
899 goto bail0;
900 while (*string)
901 {
902 string = FcCharSetParseValue (string, &ucs4);
903 if (!string)
904 goto bail1;
905 bits = 0;
906 for (i = 0; i < 256/32; i++)
907 {
908 string = FcCharSetParseValue (string, &temp.map[i]);
909 if (!string)
910 goto bail1;
911 bits |= temp.map[i];
912 }
913 if (bits)
914 {
915 leaf = malloc (sizeof (FcCharLeaf));
916 if (!leaf)
917 goto bail1;
918 *leaf = temp;
919 if (!FcCharSetInsertLeaf (c, ucs4, leaf))
920 goto bail1;
921 }
922 }
923 return c;
924 bail1:
925 if (c->num)
926 {
927 FcMemFree (FC_MEM_CHARSET, c->num * sizeof (FcCharLeaf *));
928 free (FcCharSetLeaves (c));
929 }
930 if (c->num)
931 {
932 FcMemFree (FC_MEM_CHARSET, c->num * sizeof (FcChar16));
933 free (FcCharSetNumbers (c));
934 }
935 FcMemFree (FC_MEM_CHARSET, sizeof (FcCharSet));
936 free (c);
937 bail0:
938 return NULL;
939 }
940
941 FcBool
942 FcNameUnparseCharSet (FcStrBuf *buf, const FcCharSet *c)
943 {
944 FcCharSetIter ci;
945 int i;
946 #ifdef CHECK
947 int len = buf->len;
948 #endif
949
950 for (FcCharSetIterStart (c, &ci);
951 ci.leaf;
952 FcCharSetIterNext (c, &ci))
953 {
954 if (!FcCharSetUnparseValue (buf, ci.ucs4))
955 return FcFalse;
956 for (i = 0; i < 256/32; i++)
957 if (!FcCharSetUnparseValue (buf, ci.leaf->map[i]))
958 return FcFalse;
959 }
960 #ifdef CHECK
961 {
962 FcCharSet *check;
963 FcChar32 missing;
964 FcCharSetIter ci, checki;
965
966 /* null terminate for parser */
967 FcStrBufChar (buf, '\0');
968 /* step back over null for life after test */
969 buf->len--;
970 check = FcNameParseCharSet (buf->buf + len);
971 FcCharSetIterStart (c, &ci);
972 FcCharSetIterStart (check, &checki);
973 while (ci.leaf || checki.leaf)
974 {
975 if (ci.ucs4 < checki.ucs4)
976 {
977 printf ("Missing leaf node at 0x%x\n", ci.ucs4);
978 FcCharSetIterNext (c, &ci);
979 }
980 else if (checki.ucs4 < ci.ucs4)
981 {
982 printf ("Extra leaf node at 0x%x\n", checki.ucs4);
983 FcCharSetIterNext (check, &checki);
984 }
985 else
986 {
987 int i = 256/32;
988 FcChar32 *cm = ci.leaf->map;
989 FcChar32 *checkm = checki.leaf->map;
990
991 for (i = 0; i < 256; i += 32)
992 {
993 if (*cm != *checkm)
994 printf ("Mismatching sets at 0x%08x: 0x%08x != 0x%08x\n",
995 ci.ucs4 + i, *cm, *checkm);
996 cm++;
997 checkm++;
998 }
999 FcCharSetIterNext (c, &ci);
1000 FcCharSetIterNext (check, &checki);
1001 }
1002 }
1003 if ((missing = FcCharSetSubtractCount (c, check)))
1004 printf ("%d missing in reparsed result\n", missing);
1005 if ((missing = FcCharSetSubtractCount (check, c)))
1006 printf ("%d extra in reparsed result\n", missing);
1007 FcCharSetDestroy (check);
1008 }
1009 #endif
1010
1011 return FcTrue;
1012 }
1013
1014 typedef struct _FcCharLeafEnt FcCharLeafEnt;
1015
1016 struct _FcCharLeafEnt {
1017 FcCharLeafEnt *next;
1018 FcChar32 hash;
1019 FcCharLeaf leaf;
1020 };
1021
1022 #define FC_CHAR_LEAF_BLOCK (4096 / sizeof (FcCharLeafEnt))
1023 #define FC_CHAR_LEAF_HASH_SIZE 257
1024
1025 typedef struct _FcCharSetEnt FcCharSetEnt;
1026
1027 struct _FcCharSetEnt {
1028 FcCharSetEnt *next;
1029 FcChar32 hash;
1030 FcCharSet set;
1031 };
1032
1033 typedef struct _FcCharSetOrigEnt FcCharSetOrigEnt;
1034
1035 struct _FcCharSetOrigEnt {
1036 FcCharSetOrigEnt *next;
1037 const FcCharSet *orig;
1038 const FcCharSet *frozen;
1039 };
1040
1041 #define FC_CHAR_SET_HASH_SIZE 67
1042
1043 struct _FcCharSetFreezer {
1044 FcCharLeafEnt *leaf_hash_table[FC_CHAR_LEAF_HASH_SIZE];
1045 FcCharLeafEnt **leaf_blocks;
1046 int leaf_block_count;
1047 FcCharSetEnt *set_hash_table[FC_CHAR_SET_HASH_SIZE];
1048 FcCharSetOrigEnt *orig_hash_table[FC_CHAR_SET_HASH_SIZE];
1049 FcCharLeafEnt *current_block;
1050 int leaf_remain;
1051 int leaves_seen;
1052 int charsets_seen;
1053 int leaves_allocated;
1054 int charsets_allocated;
1055 };
1056
1057 static FcCharLeafEnt *
1058 FcCharLeafEntCreate (FcCharSetFreezer *freezer)
1059 {
1060 if (!freezer->leaf_remain)
1061 {
1062 FcCharLeafEnt **newBlocks;
1063
1064 freezer->leaf_block_count++;
1065 newBlocks = realloc (freezer->leaf_blocks, freezer->leaf_block_count * sizeof (FcCharLeafEnt *));
1066 if (!newBlocks)
1067 return 0;
1068 freezer->leaf_blocks = newBlocks;
1069 freezer->current_block = freezer->leaf_blocks[freezer->leaf_block_count-1] = malloc (FC_CHAR_LEAF_BLOCK * sizeof (FcCharLeafEnt));
1070 if (!freezer->current_block)
1071 return 0;
1072 FcMemAlloc (FC_MEM_CHARLEAF, FC_CHAR_LEAF_BLOCK * sizeof (FcCharLeafEnt));
1073 freezer->leaf_remain = FC_CHAR_LEAF_BLOCK;
1074 }
1075 freezer->leaf_remain--;
1076 freezer->leaves_allocated++;
1077 return freezer->current_block++;
1078 }
1079
1080 static FcChar32
1081 FcCharLeafHash (FcCharLeaf *leaf)
1082 {
1083 FcChar32 hash = 0;
1084 int i;
1085
1086 for (i = 0; i < 256/32; i++)
1087 hash = ((hash << 1) | (hash >> 31)) ^ leaf->map[i];
1088 return hash;
1089 }
1090
1091 static FcCharLeaf *
1092 FcCharSetFreezeLeaf (FcCharSetFreezer *freezer, FcCharLeaf *leaf)
1093 {
1094 FcChar32 hash = FcCharLeafHash (leaf);
1095 FcCharLeafEnt **bucket = &freezer->leaf_hash_table[hash % FC_CHAR_LEAF_HASH_SIZE];
1096 FcCharLeafEnt *ent;
1097
1098 for (ent = *bucket; ent; ent = ent->next)
1099 {
1100 if (ent->hash == hash && !memcmp (&ent->leaf, leaf, sizeof (FcCharLeaf)))
1101 return &ent->leaf;
1102 }
1103
1104 ent = FcCharLeafEntCreate(freezer);
1105 if (!ent)
1106 return 0;
1107 ent->leaf = *leaf;
1108 ent->hash = hash;
1109 ent->next = *bucket;
1110 *bucket = ent;
1111 return &ent->leaf;
1112 }
1113
1114 static FcChar32
1115 FcCharSetHash (FcCharSet *fcs)
1116 {
1117 FcChar32 hash = 0;
1118 int i;
1119
1120 /* hash in leaves */
1121 for (i = 0; i < fcs->num; i++)
1122 hash = ((hash << 1) | (hash >> 31)) ^ FcCharLeafHash (FcCharSetLeaf(fcs,i));
1123 /* hash in numbers */
1124 for (i = 0; i < fcs->num; i++)
1125 hash = ((hash << 1) | (hash >> 31)) ^ *FcCharSetNumbers(fcs);
1126 return hash;
1127 }
1128
1129 static FcBool
1130 FcCharSetFreezeOrig (FcCharSetFreezer *freezer, const FcCharSet *orig, const FcCharSet *frozen)
1131 {
1132 FcCharSetOrigEnt **bucket = &freezer->orig_hash_table[((uintptr_t) orig) & FC_CHAR_SET_HASH_SIZE];
1133 FcCharSetOrigEnt *ent;
1134
1135 ent = malloc (sizeof (FcCharSetOrigEnt));
1136 if (!ent)
1137 return FcFalse;
1138 ent->orig = orig;
1139 ent->frozen = frozen;
1140 ent->next = *bucket;
1141 *bucket = ent;
1142 return FcTrue;
1143 }
1144
1145 static FcCharSet *
1146 FcCharSetFreezeBase (FcCharSetFreezer *freezer, FcCharSet *fcs, const FcCharSet *orig)
1147 {
1148 FcChar32 hash = FcCharSetHash (fcs);
1149 FcCharSetEnt **bucket = &freezer->set_hash_table[hash % FC_CHAR_SET_HASH_SIZE];
1150 FcCharSetEnt *ent;
1151 int size;
1152 int i;
1153
1154 for (ent = *bucket; ent; ent = ent->next)
1155 {
1156 if (ent->hash == hash &&
1157 ent->set.num == fcs->num &&
1158 !memcmp (FcCharSetNumbers(&ent->set),
1159 FcCharSetNumbers(fcs),
1160 fcs->num * sizeof (FcChar16)))
1161 {
1162 FcBool ok = FcTrue;
1163 int i;
1164
1165 for (i = 0; i < fcs->num; i++)
1166 if (FcCharSetLeaf(&ent->set, i) != FcCharSetLeaf(fcs, i))
1167 ok = FcFalse;
1168 if (ok)
1169 return &ent->set;
1170 }
1171 }
1172
1173 size = (sizeof (FcCharSetEnt) +
1174 fcs->num * sizeof (FcCharLeaf *) +
1175 fcs->num * sizeof (FcChar16));
1176 ent = malloc (size);
1177 if (!ent)
1178 return 0;
1179 FcMemAlloc (FC_MEM_CHARSET, size);
1180
1181 freezer->charsets_allocated++;
1182
1183 ent->set.ref = FC_REF_CONSTANT;
1184 ent->set.num = fcs->num;
1185 if (fcs->num)
1186 {
1187 intptr_t *ent_leaves;
1188
1189 ent->set.leaves_offset = sizeof (ent->set);
1190 ent->set.numbers_offset = (ent->set.leaves_offset +
1191 fcs->num * sizeof (intptr_t));
1192
1193 ent_leaves = FcCharSetLeaves (&ent->set);
1194 for (i = 0; i < fcs->num; i++)
1195 ent_leaves[i] = FcPtrToOffset (ent_leaves,
1196 FcCharSetLeaf (fcs, i));
1197 memcpy (FcCharSetNumbers (&ent->set),
1198 FcCharSetNumbers (fcs),
1199 fcs->num * sizeof (FcChar16));
1200 }
1201 else
1202 {
1203 ent->set.leaves_offset = 0;
1204 ent->set.numbers_offset = 0;
1205 }
1206
1207 ent->hash = hash;
1208 ent->next = *bucket;
1209 *bucket = ent;
1210
1211 return &ent->set;
1212 }
1213
1214 static const FcCharSet *
1215 FcCharSetFindFrozen (FcCharSetFreezer *freezer, const FcCharSet *orig)
1216 {
1217 FcCharSetOrigEnt **bucket = &freezer->orig_hash_table[((uintptr_t) orig) & FC_CHAR_SET_HASH_SIZE];
1218 FcCharSetOrigEnt *ent;
1219
1220 for (ent = *bucket; ent; ent = ent->next)
1221 if (ent->orig == orig)
1222 return ent->frozen;
1223 return NULL;
1224 }
1225
1226 const FcCharSet *
1227 FcCharSetFreeze (FcCharSetFreezer *freezer, const FcCharSet *fcs)
1228 {
1229 FcCharSet *b;
1230 const FcCharSet *n = 0;
1231 FcCharLeaf *l;
1232 int i;
1233
1234 b = FcCharSetCreate ();
1235 if (!b)
1236 goto bail0;
1237 for (i = 0; i < fcs->num; i++)
1238 {
1239 l = FcCharSetFreezeLeaf (freezer, FcCharSetLeaf(fcs, i));
1240 if (!l)
1241 goto bail1;
1242 if (!FcCharSetInsertLeaf (b, FcCharSetNumbers(fcs)[i] << 8, l))
1243 goto bail1;
1244 }
1245 n = FcCharSetFreezeBase (freezer, b, fcs);
1246 if (!FcCharSetFreezeOrig (freezer, fcs, n))
1247 {
1248 n = NULL;
1249 goto bail1;
1250 }
1251 freezer->charsets_seen++;
1252 freezer->leaves_seen += fcs->num;
1253 bail1:
1254 if (b->num)
1255 {
1256 FcMemFree (FC_MEM_CHARSET, b->num * sizeof (FcCharLeaf *));
1257 free (FcCharSetLeaves (b));
1258 }
1259 if (b->num)
1260 {
1261 FcMemFree (FC_MEM_CHARSET, b->num * sizeof (FcChar16));
1262 free (FcCharSetNumbers (b));
1263 }
1264 FcMemFree (FC_MEM_CHARSET, sizeof (FcCharSet));
1265 free (b);
1266 bail0:
1267 return n;
1268 }
1269
1270 FcCharSetFreezer *
1271 FcCharSetFreezerCreate (void)
1272 {
1273 FcCharSetFreezer *freezer;
1274
1275 freezer = calloc (1, sizeof (FcCharSetFreezer));
1276 return freezer;
1277 }
1278
1279 void
1280 FcCharSetFreezerDestroy (FcCharSetFreezer *freezer)
1281 {
1282 int i;
1283
1284 if (FcDebug() & FC_DBG_CACHE)
1285 {
1286 printf ("\ncharsets %d -> %d leaves %d -> %d\n",
1287 freezer->charsets_seen, freezer->charsets_allocated,
1288 freezer->leaves_seen, freezer->leaves_allocated);
1289 }
1290 for (i = 0; i < FC_CHAR_SET_HASH_SIZE; i++)
1291 {
1292 FcCharSetEnt *ent, *next;
1293 for (ent = freezer->set_hash_table[i]; ent; ent = next)
1294 {
1295 next = ent->next;
1296 FcMemFree (FC_MEM_CHARSET, (sizeof (FcCharSetEnt) +
1297 ent->set.num * sizeof (FcCharLeaf *) +
1298 ent->set.num * sizeof (FcChar16)));
1299 free (ent);
1300 }
1301 }
1302
1303 for (i = 0; i < FC_CHAR_SET_HASH_SIZE; i++)
1304 {
1305 FcCharSetOrigEnt *ent, *next;
1306 for (ent = freezer->orig_hash_table[i]; ent; ent = next)
1307 {
1308 next = ent->next;
1309 free (ent);
1310 }
1311 }
1312
1313 for (i = 0; i < freezer->leaf_block_count; i++)
1314 {
1315 free (freezer->leaf_blocks[i]);
1316 FcMemFree (FC_MEM_CHARLEAF, FC_CHAR_LEAF_BLOCK * sizeof (FcCharLeafEnt));
1317 }
1318
1319 free (freezer->leaf_blocks);
1320 free (freezer);
1321 }
1322
1323 FcBool
1324 FcCharSetSerializeAlloc (FcSerialize *serialize, const FcCharSet *cs)
1325 {
1326 intptr_t *leaves;
1327 FcChar16 *numbers;
1328 int i;
1329
1330 if (cs->ref != FC_REF_CONSTANT)
1331 {
1332 if (!serialize->cs_freezer)
1333 {
1334 serialize->cs_freezer = FcCharSetFreezerCreate ();
1335 if (!serialize->cs_freezer)
1336 return FcFalse;
1337 }
1338 if (FcCharSetFindFrozen (serialize->cs_freezer, cs))
1339 return FcTrue;
1340
1341 cs = FcCharSetFreeze (serialize->cs_freezer, cs);
1342 }
1343
1344 leaves = FcCharSetLeaves (cs);
1345 numbers = FcCharSetNumbers (cs);
1346
1347 if (!FcSerializeAlloc (serialize, cs, sizeof (FcCharSet)))
1348 return FcFalse;
1349 if (!FcSerializeAlloc (serialize, leaves, cs->num * sizeof (intptr_t)))
1350 return FcFalse;
1351 if (!FcSerializeAlloc (serialize, numbers, cs->num * sizeof (FcChar16)))
1352 return FcFalse;
1353 for (i = 0; i < cs->num; i++)
1354 if (!FcSerializeAlloc (serialize, FcCharSetLeaf(cs, i),
1355 sizeof (FcCharLeaf)))
1356 return FcFalse;
1357 return FcTrue;
1358 }
1359
1360 FcCharSet *
1361 FcCharSetSerialize(FcSerialize *serialize, const FcCharSet *cs)
1362 {
1363 FcCharSet *cs_serialized;
1364 intptr_t *leaves, *leaves_serialized;
1365 FcChar16 *numbers, *numbers_serialized;
1366 FcCharLeaf *leaf, *leaf_serialized;
1367 int i;
1368
1369 if (cs->ref != FC_REF_CONSTANT && serialize->cs_freezer)
1370 {
1371 cs = FcCharSetFindFrozen (serialize->cs_freezer, cs);
1372 if (!cs)
1373 return NULL;
1374 }
1375
1376 cs_serialized = FcSerializePtr (serialize, cs);
1377 if (!cs_serialized)
1378 return NULL;
1379
1380 cs_serialized->ref = FC_REF_CONSTANT;
1381 cs_serialized->num = cs->num;
1382
1383 if (cs->num)
1384 {
1385 leaves = FcCharSetLeaves (cs);
1386 leaves_serialized = FcSerializePtr (serialize, leaves);
1387 if (!leaves_serialized)
1388 return NULL;
1389
1390 cs_serialized->leaves_offset = FcPtrToOffset (cs_serialized,
1391 leaves_serialized);
1392
1393 numbers = FcCharSetNumbers (cs);
1394 numbers_serialized = FcSerializePtr (serialize, numbers);
1395 if (!numbers)
1396 return NULL;
1397
1398 cs_serialized->numbers_offset = FcPtrToOffset (cs_serialized,
1399 numbers_serialized);
1400
1401 for (i = 0; i < cs->num; i++)
1402 {
1403 leaf = FcCharSetLeaf (cs, i);
1404 leaf_serialized = FcSerializePtr (serialize, leaf);
1405 if (!leaf_serialized)
1406 return NULL;
1407 *leaf_serialized = *leaf;
1408 leaves_serialized[i] = FcPtrToOffset (leaves_serialized,
1409 leaf_serialized);
1410 numbers_serialized[i] = numbers[i];
1411 }
1412 }
1413 else
1414 {
1415 cs_serialized->leaves_offset = 0;
1416 cs_serialized->numbers_offset = 0;
1417 }
1418
1419 return cs_serialized;
1420 }
1421 #define __fccharset__
1422 #include "fcaliastail.h"
1423 #undef __fccharset__