]> git.wh0rd.org - fontconfig.git/blob - src/fclang.c
Fix bug 2878 (excessive relocations at startup for charsets, reported by
[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 static FcBool
725 FcLangSetEnsureBank (int bi)
726 {
727 if (!langsets || bi >= langset_bank_count)
728 {
729 int new_count = langset_bank_count + 2;
730 int i;
731 FcLangSet** tt;
732 tt = realloc(langsets, new_count * sizeof(FcLangSet *));
733 if (!tt)
734 return FcFalse;
735
736 langsets = tt;
737 for (i = langset_bank_count; i < new_count; i++)
738 langsets[i] = 0;
739 langset_bank_count = new_count;
740 }
741
742 return FcTrue;
743 }
744
745 void *
746 FcLangSetDistributeBytes (FcCache * metadata, void * block_ptr)
747 {
748 int bi = FcCacheBankToIndex(metadata->bank);
749 if (!FcLangSetEnsureBank(bi))
750 return 0;
751
752 langsets[bi] = block_ptr;
753 block_ptr = (void *)((char *)block_ptr +
754 langset_count * sizeof(FcLangSet));
755 langset_ptr = 0;
756 metadata->langset_count = langset_count;
757 return block_ptr;
758 }
759
760 FcLangSet *
761 FcLangSetSerialize(int bank, FcLangSet *l)
762 {
763 int p = langset_ptr, bi = FcCacheBankToIndex(bank);
764
765 if (!l) return 0;
766
767 langsets[bi][langset_ptr] = *l;
768 langsets[bi][langset_ptr].extra = 0;
769 langset_ptr++;
770 return &langsets[bi][p];
771 }
772
773 void *
774 FcLangSetUnserialize (FcCache metadata, void *block_ptr)
775 {
776 int bi = FcCacheBankToIndex(metadata.bank);
777 if (!FcLangSetEnsureBank(bi))
778 return 0;
779
780 FcMemAlloc (FC_MEM_LANGSET, metadata.langset_count * sizeof(FcLangSet));
781 langsets[bi] = (FcLangSet *)block_ptr;
782 block_ptr = (void *)((char *)block_ptr +
783 metadata.langset_count * sizeof(FcLangSet));
784 return block_ptr;
785 }