]> git.wh0rd.org Git - fontconfig.git/blob - src/fclist.c
Add ref counting to font config patterns so that FcFontSort return values
[fontconfig.git] / src / fclist.c
1 /*
2  * $XFree86: xc/lib/fontconfig/src/fclist.c,v 1.5 2002/06/03 08:31:15 keithp Exp $
3  *
4  * Copyright © 2000 Keith Packard, member of The XFree86 Project, Inc.
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  * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
18  * EVENT SHALL KEITH PACKARD 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 <stdlib.h>
26 #include "fcint.h"
27
28 FcObjectSet *
29 FcObjectSetCreate (void)
30 {
31     FcObjectSet    *os;
32
33     os = (FcObjectSet *) malloc (sizeof (FcObjectSet));
34     if (!os)
35         return 0;
36     FcMemAlloc (FC_MEM_OBJECTSET, sizeof (FcObjectSet));
37     os->nobject = 0;
38     os->sobject = 0;
39     os->objects = 0;
40     return os;
41 }
42
43 FcBool
44 FcObjectSetAdd (FcObjectSet *os, const char *object)
45 {
46     int         s;
47     const char  **objects;
48     int         high, low, mid, c;
49     
50     if (os->nobject == os->sobject)
51     {
52         s = os->sobject + 4;
53         if (os->objects)
54             objects = (const char **) realloc ((void *) os->objects,
55                                                s * sizeof (const char *));
56         else
57             objects = (const char **) malloc (s * sizeof (const char *));
58         if (!objects)
59             return FcFalse;
60         if (os->sobject)
61             FcMemFree (FC_MEM_OBJECTPTR, os->sobject * sizeof (const char *));
62         FcMemAlloc (FC_MEM_OBJECTPTR, s * sizeof (const char *));
63         os->objects = objects;
64         os->sobject = s;
65     }
66     high = os->nobject - 1;
67     low = 0;
68     mid = 0;
69     c = 1;
70     while (low <= high)
71     {
72         mid = (low + high) >> 1;
73         c = strcmp (os->objects[mid], object);
74         if (c == 0)
75             return FcTrue;
76         if (c < 0)
77             low = mid + 1;
78         else
79             high = mid - 1;
80     }
81     if (c < 0)
82         mid++;
83     memmove (os->objects + mid + 1, os->objects + mid, 
84              (os->nobject - mid) * sizeof (const char *));
85     os->objects[mid] = object;
86     os->nobject++;
87     return FcTrue;
88 }
89
90 void
91 FcObjectSetDestroy (FcObjectSet *os)
92 {
93     if (os->objects)
94     {
95         FcMemFree (FC_MEM_OBJECTPTR, os->sobject * sizeof (const char *));
96         free ((void *) os->objects);
97     }
98     FcMemFree (FC_MEM_OBJECTSET, sizeof (FcObjectSet));
99     free (os);
100 }
101
102 FcObjectSet *
103 FcObjectSetVaBuild (const char *first, va_list va)
104 {
105     FcObjectSet    *ret;
106
107     FcObjectSetVapBuild (ret, first, va);
108     return ret;
109 }
110
111 FcObjectSet *
112 FcObjectSetBuild (const char *first, ...)
113 {
114     va_list         va;
115     FcObjectSet    *os;
116
117     va_start (va, first);
118     FcObjectSetVapBuild (os, first, va);
119     va_end (va);
120     return os;
121 }
122
123 static FcBool
124 FcListValueListMatchAny (FcValueList *v1orig,
125                          FcValueList *v2orig)
126 {
127     FcValueList     *v1, *v2;
128
129     for (v1 = v1orig; v1; v1 = v1->next)
130         for (v2 = v2orig; v2; v2 = v2->next)
131             if (FcConfigCompareValue (v2->value, FcOpContains, v1->value))
132                 return FcTrue;
133     return FcFalse;
134 }
135
136 static FcBool
137 FcListValueListEqual (FcValueList   *v1orig,
138                       FcValueList   *v2orig)
139 {
140     FcValueList     *v1, *v2;
141
142     for (v1 = v1orig; v1; v1 = v1->next)
143     {
144         for (v2 = v2orig; v2; v2 = v2->next)
145             if (FcConfigCompareValue (v1->value, FcOpEqual, v2->value))
146                 break;
147         if (!v2)
148             return FcFalse;
149     }
150     for (v2 = v2orig; v2; v2 = v2->next)
151     {
152         for (v1 = v1orig; v1; v1 = v1->next)
153             if (FcConfigCompareValue (v1->value, FcOpEqual, v2->value))
154                 break;
155         if (!v1)
156             return FcFalse;
157     }
158     return FcTrue;
159 }
160
161 static FcBool
162 FcListPatternEqual (FcPattern   *p1,
163                     FcPattern   *p2,
164                     FcObjectSet *os)
165 {
166     int             i;
167     FcPatternElt    *e1, *e2;
168
169     for (i = 0; i < os->nobject; i++)
170     {
171         e1 = FcPatternFindElt (p1, os->objects[i]);
172         e2 = FcPatternFindElt (p2, os->objects[i]);
173         if (!e1 && !e2)
174             return FcTrue;
175         if (!e1 || !e2)
176             return FcFalse;
177         if (!FcListValueListEqual (e1->values, e2->values))
178             return FcFalse;
179     }
180     return FcTrue;
181 }
182
183 /*
184  * FcTrue iff all objects in "p" match "font"
185  */
186
187 static FcBool
188 FcListPatternMatchAny (FcPattern *p,
189                        FcPattern *font)
190 {
191     int             i;
192     FcPatternElt   *e;
193
194     for (i = 0; i < p->num; i++)
195     {
196         e = FcPatternFindElt (font, p->elts[i].object);
197         if (!e)
198             return FcFalse;
199         if (!FcListValueListMatchAny (p->elts[i].values, e->values))
200             return FcFalse;
201     }
202     return FcTrue;
203 }
204
205 static FcChar32
206 FcListStringHash (const FcChar8 *s)
207 {
208     FcChar32    h = 0;
209     FcChar8     c;
210
211     while ((c = *s++))
212     {
213         c = FcToLower (c);
214         h = ((h << 3) ^ (h >> 3)) ^ c;
215     }
216     return h;
217 }
218
219 static FcChar32
220 FcListMatrixHash (const FcMatrix *m)
221 {
222     int     xx = (int) (m->xx * 100), 
223             xy = (int) (m->xy * 100), 
224             yx = (int) (m->yx * 100),
225             yy = (int) (m->yy * 100);
226
227     return ((FcChar32) xx) ^ ((FcChar32) xy) ^ ((FcChar32) yx) ^ ((FcChar32) yy);
228 }
229
230 static FcChar32
231 FcListValueHash (FcValue    v)
232 {
233     switch (v.type) {
234     case FcTypeVoid:
235         return 0;
236     case FcTypeInteger:
237         return (FcChar32) v.u.i;
238     case FcTypeDouble:
239         return (FcChar32) (int) v.u.d;
240     case FcTypeString:
241         return FcListStringHash (v.u.s);
242     case FcTypeBool:
243         return (FcChar32) v.u.b;
244     case FcTypeMatrix:
245         return FcListMatrixHash (v.u.m);
246     case FcTypeCharSet:
247         return FcCharSetCount (v.u.c);
248     case FcTypeFTFace:
249         return (FcChar32) v.u.f;
250     }
251     return 0;
252 }
253
254 static FcChar32
255 FcListValueListHash (FcValueList    *list)
256 {
257     FcChar32    h = 0;
258     
259     while (list)
260     {
261         h = h ^ FcListValueHash (list->value);
262         list = list->next;
263     }
264     return h;
265 }
266
267 static FcChar32
268 FcListPatternHash (FcPattern    *font,
269                    FcObjectSet  *os)
270 {
271     int             n;
272     FcPatternElt    *e;
273     FcChar32        h = 0;
274
275     for (n = 0; n < os->nobject; n++)
276     {
277         e = FcPatternFindElt (font, os->objects[n]);
278         if (e)
279             h = h ^ FcListValueListHash (e->values);
280     }
281     return h;
282 }
283
284 typedef struct _FcListBucket {
285     struct _FcListBucket    *next;
286     FcChar32                hash;
287     FcPattern               *pattern;
288 } FcListBucket;
289
290 #define FC_LIST_HASH_SIZE   4099
291
292 typedef struct _FcListHashTable {
293     int             entries;
294     FcListBucket    *buckets[FC_LIST_HASH_SIZE];
295 } FcListHashTable;
296     
297 static void
298 FcListHashTableInit (FcListHashTable *table)
299 {
300     table->entries = 0;
301     memset (table->buckets, '\0', sizeof (table->buckets));
302 }
303
304 static void
305 FcListHashTableCleanup (FcListHashTable *table)
306 {
307     int i;
308     FcListBucket    *bucket, *next;
309
310     for (i = 0; i < FC_LIST_HASH_SIZE; i++)
311     {
312         for (bucket = table->buckets[i]; bucket; bucket = next)
313         {
314             next = bucket->next;
315             FcPatternDestroy (bucket->pattern);
316             FcMemFree (FC_MEM_LISTBUCK, sizeof (FcListBucket));
317             free (bucket);
318         }
319         table->buckets[i] = 0;
320     }
321     table->entries = 0;
322 }
323
324 static FcBool
325 FcListAppend (FcListHashTable   *table,
326               FcPattern         *font,
327               FcObjectSet       *os)
328 {
329     int             o;
330     FcPatternElt    *e;
331     FcValueList     *v;
332     FcChar32        hash;
333     FcListBucket    **prev, *bucket;
334
335     hash = FcListPatternHash (font, os);
336     for (prev = &table->buckets[hash % FC_LIST_HASH_SIZE];
337          (bucket = *prev); prev = &(bucket->next))
338     {
339         if (bucket->hash == hash && 
340             FcListPatternEqual (bucket->pattern, font, os))
341             return FcTrue;
342     }
343     bucket = (FcListBucket *) malloc (sizeof (FcListBucket));
344     if (!bucket)
345         goto bail0;
346     FcMemAlloc (FC_MEM_LISTBUCK, sizeof (FcListBucket));
347     bucket->next = 0;
348     bucket->hash = hash;
349     bucket->pattern = FcPatternCreate ();
350     if (!bucket->pattern)
351         goto bail1;
352     
353     for (o = 0; o < os->nobject; o++)
354     {
355         e = FcPatternFindElt (font, os->objects[o]);
356         if (e)
357         {
358             for (v = e->values; v; v = v->next)
359             {
360                 if (!FcPatternAdd (bucket->pattern, 
361                                    os->objects[o], 
362                                    v->value, FcTrue))
363                     goto bail2;
364             }
365         }
366     }
367     *prev = bucket;
368     ++table->entries;
369
370     return FcTrue;
371     
372 bail2:
373     FcPatternDestroy (bucket->pattern);
374 bail1:
375     FcMemFree (FC_MEM_LISTBUCK, sizeof (FcListBucket));
376     free (bucket);
377 bail0:
378     return FcFalse;
379 }
380
381 FcFontSet *
382 FcFontSetList (FcConfig     *config,
383                FcFontSet    **sets,
384                int          nsets,
385                FcPattern    *p,
386                FcObjectSet  *os)
387 {
388     FcFontSet       *ret;
389     FcFontSet       *s;
390     int             f;
391     int             set;
392     FcListHashTable table;
393     int             i;
394     FcListBucket    *bucket;
395
396     if (!config)
397     {
398         if (!FcInitBringUptoDate ())
399             goto bail0;
400
401         config = FcConfigGetCurrent ();
402         if (!config)
403             goto bail0;
404     }
405     FcListHashTableInit (&table);
406     /*
407      * Walk all available fonts adding those that
408      * match to the hash table
409      */
410     for (set = 0; set < nsets; set++)
411     {
412         s = sets[set];
413         if (!s)
414             continue;
415         for (f = 0; f < s->nfont; f++)
416             if (FcListPatternMatchAny (p, s->fonts[f]))
417                 if (!FcListAppend (&table, s->fonts[f], os))
418                     goto bail1;
419     }
420 #if 0
421     {
422         int     max = 0;
423         int     full = 0;
424         int     ents = 0;
425         int     len;
426         for (i = 0; i < FC_LIST_HASH_SIZE; i++)
427         {
428             if ((bucket = table.buckets[i]))
429             {
430                 len = 0;
431                 for (; bucket; bucket = bucket->next)
432                 {
433                     ents++;
434                     len++;
435                 }
436                 if (len > max)
437                     max = len;
438                 full++;
439             }
440         }
441         printf ("used: %d max: %d avg: %g\n", full, max, 
442                 (double) ents / FC_LIST_HASH_SIZE);
443     }
444 #endif
445     /*
446      * Walk the hash table and build
447      * a font set
448      */
449     ret = FcFontSetCreate ();
450     if (!ret)
451         goto bail0;
452     for (i = 0; i < FC_LIST_HASH_SIZE; i++)
453         while ((bucket = table.buckets[i]))
454         {
455             if (!FcFontSetAdd (ret, bucket->pattern))
456                 goto bail2;
457             table.buckets[i] = bucket->next;
458             FcMemFree (FC_MEM_LISTBUCK, sizeof (FcListBucket));
459             free (bucket);
460         }
461     
462     return ret;
463
464 bail2:
465     FcFontSetDestroy (ret);
466 bail1:
467     FcListHashTableCleanup (&table);
468 bail0:
469     return 0;
470 }
471
472 FcFontSet *
473 FcFontList (FcConfig    *config,
474             FcPattern   *p,
475             FcObjectSet *os)
476 {
477     FcFontSet   *sets[2];
478     int         nsets;
479
480     if (!config)
481     {
482         config = FcConfigGetCurrent ();
483         if (!config)
484             return 0;
485     }
486     nsets = 0;
487     if (config->fonts[FcSetSystem])
488         sets[nsets++] = config->fonts[FcSetSystem];
489     if (config->fonts[FcSetApplication])
490         sets[nsets++] = config->fonts[FcSetApplication];
491     return FcFontSetList (config, sets, nsets, p, os);
492 }