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