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