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