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