Add *NeededBytesAlign(), which overestimates the padding which is later
[platform/upstream/fontconfig.git] / src / fclang.c
1 /*
2  * $RCSId: xc/lib/fontconfig/src/fclang.c,v 1.7 2002/08/26 23:34:31 keithp Exp $
3  *
4  * Copyright © 2002 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  * 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 "fcint.h"
26
27 typedef struct {
28     const FcChar8       *lang;
29     const FcCharSet     charset;
30 } FcLangCharSet;
31
32 typedef struct {
33     int begin;
34     int end;
35 } FcLangCharSetRange;
36
37 #include "../fc-lang/fclang.h"
38
39 struct _FcLangSet {
40     FcChar32    map[NUM_LANG_SET_MAP];
41     FcStrSet    *extra;
42 };
43
44 #define FcLangSetBitSet(ls, id) ((ls)->map[(id)>>5] |= ((FcChar32) 1 << ((id) & 0x1f)))
45 #define FcLangSetBitGet(ls, id) (((ls)->map[(id)>>5] >> ((id) & 0x1f)) & 1)
46
47 static FcBool langsets_populated = FcFalse;
48
49 FcLangSet *
50 FcFreeTypeLangSet (const FcCharSet  *charset, 
51                    const FcChar8    *exclusiveLang)
52 {
53     int             i, j;
54     FcChar32        missing;
55     const FcCharSet *exclusiveCharset = 0;
56     FcLangSet       *ls;
57
58     if (!langsets_populated)
59     {
60         FcLangCharSetPopulate ();
61         langsets_populated = FcTrue;
62     }
63
64     if (exclusiveLang)
65         exclusiveCharset = FcCharSetForLang (exclusiveLang);
66     ls = FcLangSetCreate ();
67     if (!ls)
68         return 0;
69     for (i = 0; i < NUM_LANG_CHAR_SET; i++)
70     {
71         /*
72          * Check for Han charsets to make fonts
73          * which advertise support for a single language
74          * not support other Han languages
75          */
76         if (exclusiveCharset &&
77             FcFreeTypeIsExclusiveLang (fcLangCharSets[i].lang))
78         {
79             if (fcLangCharSets[i].charset.num != exclusiveCharset->num)
80                 continue;
81
82             for (j = 0; j < fcLangCharSets[i].charset.num; j++)
83                 if (FcCharSetGetLeaf(&fcLangCharSets[i].charset, j) != 
84                     FcCharSetGetLeaf(exclusiveCharset, j))
85                     continue;
86         }
87         missing = FcCharSetSubtractCount (&fcLangCharSets[i].charset, charset);
88         if (FcDebug() & FC_DBG_SCANV)
89         {
90             if (missing && missing < 10)
91             {
92                 FcCharSet   *missed = FcCharSetSubtract (&fcLangCharSets[i].charset, 
93                                                          charset);
94                 FcChar32    ucs4;
95                 FcChar32    map[FC_CHARSET_MAP_SIZE];
96                 FcChar32    next;
97
98                 printf ("\n%s(%d) ", fcLangCharSets[i].lang, missing);
99                 printf ("{");
100                 for (ucs4 = FcCharSetFirstPage (missed, map, &next);
101                      ucs4 != FC_CHARSET_DONE;
102                      ucs4 = FcCharSetNextPage (missed, map, &next))
103                 {
104                     int     i, j;
105                     for (i = 0; i < FC_CHARSET_MAP_SIZE; i++)
106                         if (map[i])
107                         {
108                             for (j = 0; j < 32; j++)
109                                 if (map[i] & (1 << j))
110                                     printf (" %04x", ucs4 + i * 32 + j);
111                         }
112                 }
113                 printf (" }\n\t");
114                 FcCharSetDestroy (missed);
115             }
116             else
117                 printf ("%s(%d) ", fcLangCharSets[i].lang, missing);
118         }
119         if (!missing)
120             FcLangSetBitSet (ls, i);
121     }
122
123     if (FcDebug() & FC_DBG_SCANV)
124         printf ("\n");
125     
126     
127     return ls;
128 }
129
130 #define FcLangEnd(c)    ((c) == '-' || (c) == '\0')
131
132 FcLangResult
133 FcLangCompare (const FcChar8 *s1, const FcChar8 *s2)
134 {
135     FcChar8         c1, c2;
136     FcLangResult    result = FcLangDifferentLang;
137
138     for (;;)
139     {
140         c1 = *s1++;
141         c2 = *s2++;
142         
143         c1 = FcToLower (c1);
144         c2 = FcToLower (c2);
145         if (c1 != c2)
146         {
147             if (FcLangEnd (c1) && FcLangEnd (c2))
148                 result = FcLangDifferentCountry;
149             return result;
150         }
151         else if (!c1)
152             return FcLangEqual;
153         else if (c1 == '-')
154             result = FcLangDifferentCountry;
155     }
156 }
157
158 /*
159  * Return FcTrue when super contains sub. 
160  *
161  * super contains sub if super and sub have the same
162  * language and either the same country or one
163  * is missing the country
164  */
165
166 static FcBool
167 FcLangContains (const FcChar8 *super, const FcChar8 *sub)
168 {
169     FcChar8         c1, c2;
170
171     for (;;)
172     {
173         c1 = *super++;
174         c2 = *sub++;
175         
176         c1 = FcToLower (c1);
177         c2 = FcToLower (c2);
178         if (c1 != c2)
179         {
180             /* see if super has a country while sub is mising one */
181             if (c1 == '-' && c2 == '\0')
182                 return FcTrue;
183             /* see if sub has a country while super is mising one */
184             if (c1 == '\0' && c2 == '-')
185                 return FcTrue;
186             return FcFalse;
187         }
188         else if (!c1)
189             return FcTrue;
190     }
191 }
192
193 const FcCharSet *
194 FcCharSetForLang (const FcChar8 *lang)
195 {
196     int         i;
197     int         country = -1;
198
199     if (!langsets_populated)
200     {
201         FcLangCharSetPopulate ();
202         langsets_populated = FcTrue;
203     }
204
205     for (i = 0; i < NUM_LANG_CHAR_SET; i++)
206     {
207         switch (FcLangCompare (lang, fcLangCharSets[i].lang)) {
208         case FcLangEqual:
209             return &fcLangCharSets[i].charset;
210         case FcLangDifferentCountry:
211             if (country == -1)
212                 country = i;
213         default:
214             break;
215         }
216     }
217     if (country == -1)
218         return 0;
219     return &fcLangCharSets[i].charset;
220 }
221
222 FcLangSet *
223 FcLangSetCreate (void)
224 {
225     FcLangSet   *ls;
226
227     ls = malloc (sizeof (FcLangSet));
228     if (!ls)
229         return 0;
230     FcMemAlloc (FC_MEM_LANGSET, sizeof (FcLangSet));
231     memset (ls->map, '\0', sizeof (ls->map));
232     ls->extra = 0;
233     return ls;
234 }
235
236 void
237 FcLangSetDestroy (FcLangSet *ls)
238 {
239     if (ls->extra)
240         FcStrSetDestroy (ls->extra);
241     FcMemFree (FC_MEM_LANGSET, sizeof (FcLangSet));
242     free (ls);
243 }
244
245 FcLangSet *
246 FcLangSetCopy (const FcLangSet *ls)
247 {
248     FcLangSet   *new;
249
250     new = FcLangSetCreate ();
251     if (!new)
252         goto bail0;
253     memcpy (new->map, ls->map, sizeof (new->map));
254     if (ls->extra)
255     {
256         FcStrList       *list;
257         FcChar8         *extra;
258         
259         new->extra = FcStrSetCreate ();
260         if (!new->extra)
261             goto bail1;
262
263         list = FcStrListCreate (ls->extra);     
264         if (!list)
265             goto bail1;
266         
267         while ((extra = FcStrListNext (list)))
268             if (!FcStrSetAdd (new->extra, extra))
269             {
270                 FcStrListDone (list);
271                 goto bail1;
272             }
273         FcStrListDone (list);
274     }
275     return new;
276 bail1:
277     FcLangSetDestroy (new);
278 bail0:
279     return 0;
280 }
281
282 static int
283 FcLangSetIndex (const FcChar8 *lang)
284 {
285     int     low, high, mid = 0;
286     int     cmp = 0;
287     FcChar8 firstChar = FcToLower(lang[0]); 
288     FcChar8 secondChar = firstChar ? FcToLower(lang[1]) : '\0';
289     
290     if (firstChar < 'a')
291     {
292         low = 0;
293         high = fcLangCharSetRanges[0].begin;
294     }
295     else if(firstChar > 'z')
296     {
297         low = fcLangCharSetRanges[25].begin;
298         high = NUM_LANG_CHAR_SET - 1;
299     }
300     else
301     {
302         low = fcLangCharSetRanges[firstChar - 'a'].begin;
303         high = fcLangCharSetRanges[firstChar - 'a'].end;
304         /* no matches */
305         if (low > high)
306             return -low; /* next entry after where it would be */
307     }
308
309     while (low <= high)
310     {
311         mid = (high + low) >> 1;
312         if(fcLangCharSets[mid].lang[0] != firstChar)
313             cmp = FcStrCmpIgnoreCase(fcLangCharSets[mid].lang, lang);
314         else
315         {   /* fast path for resolving 2-letter languages (by far the most common) after
316              * finding the first char (probably already true because of the hash table) */
317             cmp = fcLangCharSets[mid].lang[1] - secondChar;
318             if (cmp == 0 && 
319                 (fcLangCharSets[mid].lang[2] != '\0' || 
320                  lang[2] != '\0'))
321             {
322                 cmp = FcStrCmpIgnoreCase(fcLangCharSets[mid].lang+2, 
323                                          lang+2);
324             }
325         }
326         if (cmp == 0)
327             return mid;
328         if (cmp < 0)
329             low = mid + 1;
330         else
331             high = mid - 1;
332     }
333     if (cmp < 0)
334         mid++;
335     return -(mid + 1);
336 }
337
338 FcBool
339 FcLangSetAdd (FcLangSet *ls, const FcChar8 *lang)
340 {
341     int     id;
342
343     id = FcLangSetIndex (lang);
344     if (id >= 0)
345     {
346         FcLangSetBitSet (ls, id);
347         return FcTrue;
348     }
349     if (!ls->extra)
350     {
351         ls->extra = FcStrSetCreate ();
352         if (!ls->extra)
353             return FcFalse;
354     }
355     return FcStrSetAdd (ls->extra, lang);
356 }
357
358 FcLangResult
359 FcLangSetHasLang (const FcLangSet *ls, const FcChar8 *lang)
360 {
361     int             id;
362     FcLangResult    best, r;
363     int             i;
364
365     id = FcLangSetIndex (lang);
366     if (id < 0)
367         id = -id - 1;
368     else if (FcLangSetBitGet (ls, id))
369         return FcLangEqual;
370     best = FcLangDifferentLang;
371     for (i = id - 1; i >= 0; i--)
372     {
373         r = FcLangCompare (lang, fcLangCharSets[i].lang);
374         if (r == FcLangDifferentLang)
375             break;
376         if (FcLangSetBitGet (ls, i) && r < best)
377             best = r;
378     }
379     for (i = id; i < NUM_LANG_CHAR_SET; i++)
380     {
381         r = FcLangCompare (lang, fcLangCharSets[i].lang);
382         if (r == FcLangDifferentLang)
383             break;
384         if (FcLangSetBitGet (ls, i) && r < best)
385             best = r;
386     }
387     if (ls->extra)
388     {
389         FcStrList       *list = FcStrListCreate (ls->extra);
390         FcChar8         *extra;
391         FcLangResult    r;
392         
393         if (list)
394         {
395             while (best > FcLangEqual && (extra = FcStrListNext (list)))
396             {
397                 r = FcLangCompare (lang, extra);
398                 if (r < best)
399                     best = r;
400             }
401             FcStrListDone (list);
402         }
403     }
404     return best;
405 }
406
407 static FcLangResult
408 FcLangSetCompareStrSet (const FcLangSet *ls, FcStrSet *set)
409 {
410     FcStrList       *list = FcStrListCreate (set);
411     FcLangResult    r, best = FcLangDifferentLang;
412     FcChar8         *extra;
413
414     if (list)
415     {
416         while (best > FcLangEqual && (extra = FcStrListNext (list)))
417         {
418             r = FcLangSetHasLang (ls, extra);
419             if (r < best)
420                 best = r;
421         }
422         FcStrListDone (list);
423     }
424     return best;
425 }
426
427 FcLangResult
428 FcLangSetCompare (const FcLangSet *lsa, const FcLangSet *lsb)
429 {
430     int             i, j;
431     FcLangResult    best, r;
432
433     for (i = 0; i < NUM_LANG_SET_MAP; i++)
434         if (lsa->map[i] & lsb->map[i])
435             return FcLangEqual;
436     best = FcLangDifferentLang;
437     for (j = 0; j < NUM_COUNTRY_SET; j++)
438         for (i = 0; i < NUM_LANG_SET_MAP; i++)
439             if ((lsa->map[i] & fcLangCountrySets[j][i]) &&
440                 (lsb->map[i] & fcLangCountrySets[j][i]))
441             {
442                 best = FcLangDifferentCountry;
443                 break;
444             }
445     if (lsa->extra)
446     {
447         r = FcLangSetCompareStrSet (lsb, lsa->extra);
448         if (r < best)
449             best = r;
450     }
451     if (best > FcLangEqual && lsb->extra)
452     {
453         r = FcLangSetCompareStrSet (lsa, lsb->extra);
454         if (r < best)
455             best = r;
456     }
457     return best;
458 }
459
460 /*
461  * Used in computing values -- mustn't allocate any storage
462  */
463 FcLangSet *
464 FcLangSetPromote (const FcChar8 *lang)
465 {
466     static FcLangSet    ls;
467     static FcStrSet     strs;
468     static FcChar8      *str;
469     int                 id;
470
471     memset (ls.map, '\0', sizeof (ls.map));
472     ls.extra = 0;
473     id = FcLangSetIndex (lang);
474     if (id > 0)
475     {
476         FcLangSetBitSet (&ls, id);
477     }
478     else
479     {
480         ls.extra = &strs;
481         strs.num = 1;
482         strs.size = 1;
483         strs.strs = &str;
484         strs.ref = 1;
485         str = (FcChar8 *) lang;
486     }
487     return &ls;
488 }
489
490 FcChar32
491 FcLangSetHash (const FcLangSet *ls)
492 {
493     FcChar32    h = 0;
494     int         i;
495
496     for (i = 0; i < NUM_LANG_SET_MAP; i++)
497         h ^= ls->map[i];
498     if (ls->extra)
499         h ^= ls->extra->num;
500     return h;
501 }
502
503 FcLangSet *
504 FcNameParseLangSet (const FcChar8 *string)
505 {
506     FcChar8         lang[32],c;
507     int i;
508     FcLangSet       *ls;
509
510     ls = FcLangSetCreate ();
511     if (!ls)
512         goto bail0;
513
514     for(;;)
515     {
516         for(i = 0; i < 31;i++)
517         {
518             c = *string++;
519             if(c == '\0' || c == '|')
520                 break; /* end of this code */
521             lang[i] = c;
522         }
523         lang[i] = '\0';
524         if (!FcLangSetAdd (ls, lang))
525             goto bail1;
526         if(c == '\0')
527             break;
528     }
529     return ls;
530 bail1:
531     FcLangSetDestroy (ls);
532 bail0:
533     return 0;
534 }
535
536 FcBool
537 FcNameUnparseLangSet (FcStrBuf *buf, const FcLangSet *ls)
538 {
539     int         i, bit;
540     FcChar32    bits;
541     FcBool      first = FcTrue;
542
543     for (i = 0; i < NUM_LANG_SET_MAP; i++)
544     {
545         if ((bits = ls->map[i]))
546         {
547             for (bit = 0; bit <= 31; bit++)
548                 if (bits & (1 << bit))
549                 {
550                     int id = (i << 5) | bit;
551                     if (!first)
552                         if (!FcStrBufChar (buf, '|'))
553                             return FcFalse;
554                     if (!FcStrBufString (buf, fcLangCharSets[id].lang))
555                         return FcFalse;
556                     first = FcFalse;
557                 }
558         }
559     }
560     if (ls->extra)
561     {
562         FcStrList   *list = FcStrListCreate (ls->extra);
563         FcChar8     *extra;
564
565         if (!list)
566             return FcFalse;
567         while ((extra = FcStrListNext (list)))
568         {
569             if (!first)
570                 if (!FcStrBufChar (buf, '|'))
571                     return FcFalse;
572             if (!FcStrBufString (buf, extra))
573                 return FcFalse;
574             first = FcFalse;
575         }
576     }
577     return FcTrue;
578 }
579
580 FcBool
581 FcLangSetEqual (const FcLangSet *lsa, const FcLangSet *lsb)
582 {
583     int     i;
584
585     for (i = 0; i < NUM_LANG_SET_MAP; i++)
586     {
587         if (lsa->map[i] != lsb->map[i])
588             return FcFalse;
589     }
590     if (!lsa->extra && !lsb->extra)
591         return FcTrue;
592     if (lsa->extra && lsb->extra)
593         return FcStrSetEqual (lsa->extra, lsb->extra);
594     return FcFalse;
595 }
596
597 static FcBool
598 FcLangSetContainsLang (const FcLangSet *ls, const FcChar8 *lang)
599 {
600     int             id;
601     int             i;
602
603     id = FcLangSetIndex (lang);
604     if (id < 0)
605         id = -id - 1;
606     else if (FcLangSetBitGet (ls, id))
607         return FcTrue;
608     /*
609      * search up and down among equal languages for a match
610      */
611     for (i = id - 1; i >= 0; i--)
612     {
613         if (FcLangCompare (fcLangCharSets[i].lang, lang) == FcLangDifferentLang)
614             break;
615         if (FcLangSetBitGet (ls, i) &&
616             FcLangContains (fcLangCharSets[i].lang, lang))
617             return FcTrue;
618     }
619     for (i = id; i < NUM_LANG_CHAR_SET; i++)
620     {
621         if (FcLangCompare (fcLangCharSets[i].lang, lang) == FcLangDifferentLang)
622             break;
623         if (FcLangSetBitGet (ls, i) &&
624             FcLangContains (fcLangCharSets[i].lang, lang))
625             return FcTrue;
626     }
627     if (ls->extra)
628     {
629         FcStrList       *list = FcStrListCreate (ls->extra);
630         FcChar8         *extra;
631         
632         if (list)
633         {
634             while ((extra = FcStrListNext (list)))
635             {
636                 if (FcLangContains (extra, lang))
637                     break;
638             }
639             FcStrListDone (list);
640             if (extra)
641                 return FcTrue;
642         }
643     }
644     return FcFalse;
645 }
646
647 /*
648  * return FcTrue if lsa contains every language in lsb
649  */
650 FcBool
651 FcLangSetContains (const FcLangSet *lsa, const FcLangSet *lsb)
652 {
653     int             i, j;
654     FcChar32        missing;
655
656     if (FcDebug() & FC_DBG_MATCHV)
657     {
658         printf ("FcLangSet "); FcLangSetPrint (lsa);
659         printf (" contains "); FcLangSetPrint (lsb);
660         printf ("\n");
661     }
662     /*
663      * check bitmaps for missing language support
664      */
665     for (i = 0; i < NUM_LANG_SET_MAP; i++)
666     {
667         missing = lsb->map[i] & ~lsa->map[i];
668         if (missing)
669         {
670             for (j = 0; j < 32; j++)
671                 if (missing & (1 << j)) 
672                 {
673                     if (!FcLangSetContainsLang (lsa,
674                                                 fcLangCharSets[i*32 + j].lang))
675                     {
676                         if (FcDebug() & FC_DBG_MATCHV)
677                             printf ("\tMissing bitmap %s\n", fcLangCharSets[i*32+j].lang);
678                         return FcFalse;
679                     }
680                 }
681         }
682     }
683     if (lsb->extra)
684     {
685         FcStrList   *list = FcStrListCreate (lsb->extra);
686         FcChar8     *extra;
687
688         if (list)
689         {
690             while ((extra = FcStrListNext (list)))
691             {
692                 if (!FcLangSetContainsLang (lsa, extra))
693                 {
694                     if (FcDebug() & FC_DBG_MATCHV)
695                         printf ("\tMissing string %s\n", extra);
696                     break;
697                 }
698             }
699             FcStrListDone (list);
700             if (extra)
701                 return FcFalse;
702         }
703     }
704     return FcTrue;
705 }
706
707 static FcLangSet ** langsets = 0;
708 static int langset_bank_count = 0, langset_ptr = 0, langset_count = 0;
709
710 void
711 FcLangSetNewBank (void)
712 {
713     langset_count = 0;
714 }
715
716 /* ideally, should only write one copy of any particular FcLangSet */
717 int
718 FcLangSetNeededBytes (const FcLangSet *l)
719 {
720     langset_count++;
721     return sizeof (FcLangSet);
722 }
723
724 int
725 FcLangSetNeededBytesAlign (void)
726 {
727     return __alignof__ (FcLangSet);
728 }
729
730 static FcBool
731 FcLangSetEnsureBank (int bi)
732 {
733     if (!langsets || bi >= langset_bank_count)
734     {
735         int new_count = langset_bank_count + 2;
736         int i;
737         FcLangSet** tt;
738         tt = realloc(langsets, new_count * sizeof(FcLangSet *));
739         if (!tt)
740             return FcFalse;
741
742         langsets = tt;
743         for (i = langset_bank_count; i < new_count; i++)
744             langsets[i] = 0; 
745         langset_bank_count = new_count;
746     }
747
748     return FcTrue;
749 }
750
751 void *
752 FcLangSetDistributeBytes (FcCache * metadata, void * block_ptr)
753 {
754     int bi = FcCacheBankToIndex(metadata->bank);
755     if (!FcLangSetEnsureBank(bi))
756         return 0;
757
758     block_ptr = ALIGN(block_ptr, FcLangSet);
759     langsets[bi] = block_ptr;
760     block_ptr = (void *)((char *)block_ptr +
761                          langset_count * sizeof(FcLangSet));
762     langset_ptr = 0;
763     metadata->langset_count = langset_count;
764     return block_ptr;
765 }
766
767 FcLangSet *
768 FcLangSetSerialize(int bank, FcLangSet *l)
769 {
770     int p = langset_ptr, bi = FcCacheBankToIndex(bank);
771
772     if (!l) return 0;
773
774     langsets[bi][langset_ptr] = *l;
775     langsets[bi][langset_ptr].extra = 0;
776     langset_ptr++;
777     return &langsets[bi][p];
778 }
779
780 void *
781 FcLangSetUnserialize (FcCache metadata, void *block_ptr)
782 {
783     int bi = FcCacheBankToIndex(metadata.bank);
784     if (!FcLangSetEnsureBank(bi))
785         return 0;
786
787     FcMemAlloc (FC_MEM_LANGSET, metadata.langset_count * sizeof(FcLangSet));
788     block_ptr = ALIGN(block_ptr, FcLangSet);
789     langsets[bi] = (FcLangSet *)block_ptr;
790     block_ptr = (void *)((char *)block_ptr +
791                          metadata.langset_count * sizeof(FcLangSet));
792     return block_ptr;
793 }