gfx/harfbuzz/src/hb-common.cc

Fri, 16 Jan 2015 18:13:44 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 18:13:44 +0100
branch
TOR_BUG_9701
changeset 14
925c144e1f1f
permissions
-rw-r--r--

Integrate suggestion from review to improve consistency with existing code.

michael@0 1 /*
michael@0 2 * Copyright © 2009,2010 Red Hat, Inc.
michael@0 3 * Copyright © 2011,2012 Google, Inc.
michael@0 4 *
michael@0 5 * This is part of HarfBuzz, a text shaping library.
michael@0 6 *
michael@0 7 * Permission is hereby granted, without written agreement and without
michael@0 8 * license or royalty fees, to use, copy, modify, and distribute this
michael@0 9 * software and its documentation for any purpose, provided that the
michael@0 10 * above copyright notice and the following two paragraphs appear in
michael@0 11 * all copies of this software.
michael@0 12 *
michael@0 13 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
michael@0 14 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
michael@0 15 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
michael@0 16 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
michael@0 17 * DAMAGE.
michael@0 18 *
michael@0 19 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
michael@0 20 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
michael@0 21 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
michael@0 22 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
michael@0 23 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
michael@0 24 *
michael@0 25 * Red Hat Author(s): Behdad Esfahbod
michael@0 26 * Google Author(s): Behdad Esfahbod
michael@0 27 */
michael@0 28
michael@0 29 #include "hb-private.hh"
michael@0 30
michael@0 31 #include "hb-mutex-private.hh"
michael@0 32 #include "hb-object-private.hh"
michael@0 33
michael@0 34 #include <locale.h>
michael@0 35
michael@0 36
michael@0 37 /* hb_options_t */
michael@0 38
michael@0 39 hb_options_union_t _hb_options;
michael@0 40
michael@0 41 void
michael@0 42 _hb_options_init (void)
michael@0 43 {
michael@0 44 hb_options_union_t u;
michael@0 45 u.i = 0;
michael@0 46 u.opts.initialized = 1;
michael@0 47
michael@0 48 char *c = getenv ("HB_OPTIONS");
michael@0 49 u.opts.uniscribe_bug_compatible = c && strstr (c, "uniscribe-bug-compatible");
michael@0 50
michael@0 51 /* This is idempotent and threadsafe. */
michael@0 52 _hb_options = u;
michael@0 53 }
michael@0 54
michael@0 55
michael@0 56 /* hb_tag_t */
michael@0 57
michael@0 58 /**
michael@0 59 * hb_tag_from_string:
michael@0 60 * @str: (array length=len):
michael@0 61 * @len:
michael@0 62 *
michael@0 63 *
michael@0 64 *
michael@0 65 * Return value:
michael@0 66 *
michael@0 67 * Since: 1.0
michael@0 68 **/
michael@0 69 hb_tag_t
michael@0 70 hb_tag_from_string (const char *str, int len)
michael@0 71 {
michael@0 72 char tag[4];
michael@0 73 unsigned int i;
michael@0 74
michael@0 75 if (!str || !len || !*str)
michael@0 76 return HB_TAG_NONE;
michael@0 77
michael@0 78 if (len < 0 || len > 4)
michael@0 79 len = 4;
michael@0 80 for (i = 0; i < (unsigned) len && str[i]; i++)
michael@0 81 tag[i] = str[i];
michael@0 82 for (; i < 4; i++)
michael@0 83 tag[i] = ' ';
michael@0 84
michael@0 85 return HB_TAG_CHAR4 (tag);
michael@0 86 }
michael@0 87
michael@0 88 /**
michael@0 89 * hb_tag_to_string:
michael@0 90 * @tag:
michael@0 91 * @buf: (array fixed-size=4):
michael@0 92 *
michael@0 93 *
michael@0 94 *
michael@0 95 * Since: 1.0
michael@0 96 **/
michael@0 97 void
michael@0 98 hb_tag_to_string (hb_tag_t tag, char *buf)
michael@0 99 {
michael@0 100 buf[0] = (char) (uint8_t) (tag >> 24);
michael@0 101 buf[1] = (char) (uint8_t) (tag >> 16);
michael@0 102 buf[2] = (char) (uint8_t) (tag >> 8);
michael@0 103 buf[3] = (char) (uint8_t) (tag >> 0);
michael@0 104 }
michael@0 105
michael@0 106
michael@0 107 /* hb_direction_t */
michael@0 108
michael@0 109 const char direction_strings[][4] = {
michael@0 110 "ltr",
michael@0 111 "rtl",
michael@0 112 "ttb",
michael@0 113 "btt"
michael@0 114 };
michael@0 115
michael@0 116 /**
michael@0 117 * hb_direction_from_string:
michael@0 118 * @str: (array length=len):
michael@0 119 * @len:
michael@0 120 *
michael@0 121 *
michael@0 122 *
michael@0 123 * Return value:
michael@0 124 *
michael@0 125 * Since: 1.0
michael@0 126 **/
michael@0 127 hb_direction_t
michael@0 128 hb_direction_from_string (const char *str, int len)
michael@0 129 {
michael@0 130 if (unlikely (!str || !len || !*str))
michael@0 131 return HB_DIRECTION_INVALID;
michael@0 132
michael@0 133 /* Lets match loosely: just match the first letter, such that
michael@0 134 * all of "ltr", "left-to-right", etc work!
michael@0 135 */
michael@0 136 char c = TOLOWER (str[0]);
michael@0 137 for (unsigned int i = 0; i < ARRAY_LENGTH (direction_strings); i++)
michael@0 138 if (c == direction_strings[i][0])
michael@0 139 return (hb_direction_t) (HB_DIRECTION_LTR + i);
michael@0 140
michael@0 141 return HB_DIRECTION_INVALID;
michael@0 142 }
michael@0 143
michael@0 144 /**
michael@0 145 * hb_direction_to_string:
michael@0 146 * @direction:
michael@0 147 *
michael@0 148 *
michael@0 149 *
michael@0 150 * Return value: (transfer none):
michael@0 151 *
michael@0 152 * Since: 1.0
michael@0 153 **/
michael@0 154 const char *
michael@0 155 hb_direction_to_string (hb_direction_t direction)
michael@0 156 {
michael@0 157 if (likely ((unsigned int) (direction - HB_DIRECTION_LTR)
michael@0 158 < ARRAY_LENGTH (direction_strings)))
michael@0 159 return direction_strings[direction - HB_DIRECTION_LTR];
michael@0 160
michael@0 161 return "invalid";
michael@0 162 }
michael@0 163
michael@0 164
michael@0 165 /* hb_language_t */
michael@0 166
michael@0 167 struct hb_language_impl_t {
michael@0 168 const char s[1];
michael@0 169 };
michael@0 170
michael@0 171 static const char canon_map[256] = {
michael@0 172 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
michael@0 173 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
michael@0 174 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0,
michael@0 175 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 0, 0, 0, 0, 0, 0,
michael@0 176 '-', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
michael@0 177 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, '-',
michael@0 178 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
michael@0 179 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, 0
michael@0 180 };
michael@0 181
michael@0 182 static hb_bool_t
michael@0 183 lang_equal (hb_language_t v1,
michael@0 184 const void *v2)
michael@0 185 {
michael@0 186 const unsigned char *p1 = (const unsigned char *) v1;
michael@0 187 const unsigned char *p2 = (const unsigned char *) v2;
michael@0 188
michael@0 189 while (*p1 && *p1 == canon_map[*p2])
michael@0 190 p1++, p2++;
michael@0 191
michael@0 192 return *p1 == canon_map[*p2];
michael@0 193 }
michael@0 194
michael@0 195 #if 0
michael@0 196 static unsigned int
michael@0 197 lang_hash (const void *key)
michael@0 198 {
michael@0 199 const unsigned char *p = key;
michael@0 200 unsigned int h = 0;
michael@0 201 while (canon_map[*p])
michael@0 202 {
michael@0 203 h = (h << 5) - h + canon_map[*p];
michael@0 204 p++;
michael@0 205 }
michael@0 206
michael@0 207 return h;
michael@0 208 }
michael@0 209 #endif
michael@0 210
michael@0 211
michael@0 212 struct hb_language_item_t {
michael@0 213
michael@0 214 struct hb_language_item_t *next;
michael@0 215 hb_language_t lang;
michael@0 216
michael@0 217 inline bool operator == (const char *s) const {
michael@0 218 return lang_equal (lang, s);
michael@0 219 }
michael@0 220
michael@0 221 inline hb_language_item_t & operator = (const char *s) {
michael@0 222 lang = (hb_language_t) strdup (s);
michael@0 223 for (unsigned char *p = (unsigned char *) lang; *p; p++)
michael@0 224 *p = canon_map[*p];
michael@0 225
michael@0 226 return *this;
michael@0 227 }
michael@0 228
michael@0 229 void finish (void) { free ((void *) lang); }
michael@0 230 };
michael@0 231
michael@0 232
michael@0 233 /* Thread-safe lock-free language list */
michael@0 234
michael@0 235 static hb_language_item_t *langs;
michael@0 236
michael@0 237 static inline
michael@0 238 void free_langs (void)
michael@0 239 {
michael@0 240 while (langs) {
michael@0 241 hb_language_item_t *next = langs->next;
michael@0 242 langs->finish ();
michael@0 243 free (langs);
michael@0 244 langs = next;
michael@0 245 }
michael@0 246 }
michael@0 247
michael@0 248 static hb_language_item_t *
michael@0 249 lang_find_or_insert (const char *key)
michael@0 250 {
michael@0 251 retry:
michael@0 252 hb_language_item_t *first_lang = (hb_language_item_t *) hb_atomic_ptr_get (&langs);
michael@0 253
michael@0 254 for (hb_language_item_t *lang = first_lang; lang; lang = lang->next)
michael@0 255 if (*lang == key)
michael@0 256 return lang;
michael@0 257
michael@0 258 /* Not found; allocate one. */
michael@0 259 hb_language_item_t *lang = (hb_language_item_t *) calloc (1, sizeof (hb_language_item_t));
michael@0 260 if (unlikely (!lang))
michael@0 261 return NULL;
michael@0 262 lang->next = first_lang;
michael@0 263 *lang = key;
michael@0 264
michael@0 265 if (!hb_atomic_ptr_cmpexch (&langs, first_lang, lang)) {
michael@0 266 free (lang);
michael@0 267 goto retry;
michael@0 268 }
michael@0 269
michael@0 270 #ifdef HAVE_ATEXIT
michael@0 271 if (!first_lang)
michael@0 272 atexit (free_langs); /* First person registers atexit() callback. */
michael@0 273 #endif
michael@0 274
michael@0 275 return lang;
michael@0 276 }
michael@0 277
michael@0 278
michael@0 279 /**
michael@0 280 * hb_language_from_string:
michael@0 281 * @str: (array length=len):
michael@0 282 * @len:
michael@0 283 *
michael@0 284 *
michael@0 285 *
michael@0 286 * Return value:
michael@0 287 *
michael@0 288 * Since: 1.0
michael@0 289 **/
michael@0 290 hb_language_t
michael@0 291 hb_language_from_string (const char *str, int len)
michael@0 292 {
michael@0 293 char strbuf[64];
michael@0 294
michael@0 295 if (!str || !len || !*str)
michael@0 296 return HB_LANGUAGE_INVALID;
michael@0 297
michael@0 298 if (len >= 0)
michael@0 299 {
michael@0 300 len = MIN (len, (int) sizeof (strbuf) - 1);
michael@0 301 str = (char *) memcpy (strbuf, str, len);
michael@0 302 strbuf[len] = '\0';
michael@0 303 }
michael@0 304
michael@0 305 hb_language_item_t *item = lang_find_or_insert (str);
michael@0 306
michael@0 307 return likely (item) ? item->lang : HB_LANGUAGE_INVALID;
michael@0 308 }
michael@0 309
michael@0 310 /**
michael@0 311 * hb_language_to_string:
michael@0 312 * @language:
michael@0 313 *
michael@0 314 *
michael@0 315 *
michael@0 316 * Return value: (transfer none):
michael@0 317 *
michael@0 318 * Since: 1.0
michael@0 319 **/
michael@0 320 const char *
michael@0 321 hb_language_to_string (hb_language_t language)
michael@0 322 {
michael@0 323 /* This is actually NULL-safe! */
michael@0 324 return language->s;
michael@0 325 }
michael@0 326
michael@0 327 /**
michael@0 328 * hb_language_get_default:
michael@0 329 *
michael@0 330 *
michael@0 331 *
michael@0 332 * Return value:
michael@0 333 *
michael@0 334 * Since: 1.0
michael@0 335 **/
michael@0 336 hb_language_t
michael@0 337 hb_language_get_default (void)
michael@0 338 {
michael@0 339 static hb_language_t default_language = HB_LANGUAGE_INVALID;
michael@0 340
michael@0 341 hb_language_t language = (hb_language_t) hb_atomic_ptr_get (&default_language);
michael@0 342 if (unlikely (language == HB_LANGUAGE_INVALID)) {
michael@0 343 language = hb_language_from_string (setlocale (LC_CTYPE, NULL), -1);
michael@0 344 hb_atomic_ptr_cmpexch (&default_language, HB_LANGUAGE_INVALID, language);
michael@0 345 }
michael@0 346
michael@0 347 return default_language;
michael@0 348 }
michael@0 349
michael@0 350
michael@0 351 /* hb_script_t */
michael@0 352
michael@0 353 /**
michael@0 354 * hb_script_from_iso15924_tag:
michael@0 355 * @tag:
michael@0 356 *
michael@0 357 *
michael@0 358 *
michael@0 359 * Return value:
michael@0 360 *
michael@0 361 * Since: 1.0
michael@0 362 **/
michael@0 363 hb_script_t
michael@0 364 hb_script_from_iso15924_tag (hb_tag_t tag)
michael@0 365 {
michael@0 366 if (unlikely (tag == HB_TAG_NONE))
michael@0 367 return HB_SCRIPT_INVALID;
michael@0 368
michael@0 369 /* Be lenient, adjust case (one capital letter followed by three small letters) */
michael@0 370 tag = (tag & 0xDFDFDFDF) | 0x00202020;
michael@0 371
michael@0 372 switch (tag) {
michael@0 373
michael@0 374 /* These graduated from the 'Q' private-area codes, but
michael@0 375 * the old code is still aliased by Unicode, and the Qaai
michael@0 376 * one in use by ICU. */
michael@0 377 case HB_TAG('Q','a','a','i'): return HB_SCRIPT_INHERITED;
michael@0 378 case HB_TAG('Q','a','a','c'): return HB_SCRIPT_COPTIC;
michael@0 379
michael@0 380 /* Script variants from http://unicode.org/iso15924/ */
michael@0 381 case HB_TAG('C','y','r','s'): return HB_SCRIPT_CYRILLIC;
michael@0 382 case HB_TAG('L','a','t','f'): return HB_SCRIPT_LATIN;
michael@0 383 case HB_TAG('L','a','t','g'): return HB_SCRIPT_LATIN;
michael@0 384 case HB_TAG('S','y','r','e'): return HB_SCRIPT_SYRIAC;
michael@0 385 case HB_TAG('S','y','r','j'): return HB_SCRIPT_SYRIAC;
michael@0 386 case HB_TAG('S','y','r','n'): return HB_SCRIPT_SYRIAC;
michael@0 387 }
michael@0 388
michael@0 389 /* If it looks right, just use the tag as a script */
michael@0 390 if (((uint32_t) tag & 0xE0E0E0E0) == 0x40606060)
michael@0 391 return (hb_script_t) tag;
michael@0 392
michael@0 393 /* Otherwise, return unknown */
michael@0 394 return HB_SCRIPT_UNKNOWN;
michael@0 395 }
michael@0 396
michael@0 397 /**
michael@0 398 * hb_script_from_string:
michael@0 399 * @s: (array length=len):
michael@0 400 * @len:
michael@0 401 *
michael@0 402 *
michael@0 403 *
michael@0 404 * Return value:
michael@0 405 *
michael@0 406 * Since: 1.0
michael@0 407 **/
michael@0 408 hb_script_t
michael@0 409 hb_script_from_string (const char *s, int len)
michael@0 410 {
michael@0 411 return hb_script_from_iso15924_tag (hb_tag_from_string (s, len));
michael@0 412 }
michael@0 413
michael@0 414 /**
michael@0 415 * hb_script_to_iso15924_tag:
michael@0 416 * @script:
michael@0 417 *
michael@0 418 *
michael@0 419 *
michael@0 420 * Return value:
michael@0 421 *
michael@0 422 * Since: 1.0
michael@0 423 **/
michael@0 424 hb_tag_t
michael@0 425 hb_script_to_iso15924_tag (hb_script_t script)
michael@0 426 {
michael@0 427 return (hb_tag_t) script;
michael@0 428 }
michael@0 429
michael@0 430 /**
michael@0 431 * hb_script_get_horizontal_direction:
michael@0 432 * @script:
michael@0 433 *
michael@0 434 *
michael@0 435 *
michael@0 436 * Return value:
michael@0 437 *
michael@0 438 * Since: 1.0
michael@0 439 **/
michael@0 440 hb_direction_t
michael@0 441 hb_script_get_horizontal_direction (hb_script_t script)
michael@0 442 {
michael@0 443 /* http://goo.gl/x9ilM */
michael@0 444 switch ((hb_tag_t) script)
michael@0 445 {
michael@0 446 /* Unicode-1.1 additions */
michael@0 447 case HB_SCRIPT_ARABIC:
michael@0 448 case HB_SCRIPT_HEBREW:
michael@0 449
michael@0 450 /* Unicode-3.0 additions */
michael@0 451 case HB_SCRIPT_SYRIAC:
michael@0 452 case HB_SCRIPT_THAANA:
michael@0 453
michael@0 454 /* Unicode-4.0 additions */
michael@0 455 case HB_SCRIPT_CYPRIOT:
michael@0 456
michael@0 457 /* Unicode-4.1 additions */
michael@0 458 case HB_SCRIPT_KHAROSHTHI:
michael@0 459
michael@0 460 /* Unicode-5.0 additions */
michael@0 461 case HB_SCRIPT_PHOENICIAN:
michael@0 462 case HB_SCRIPT_NKO:
michael@0 463
michael@0 464 /* Unicode-5.1 additions */
michael@0 465 case HB_SCRIPT_LYDIAN:
michael@0 466
michael@0 467 /* Unicode-5.2 additions */
michael@0 468 case HB_SCRIPT_AVESTAN:
michael@0 469 case HB_SCRIPT_IMPERIAL_ARAMAIC:
michael@0 470 case HB_SCRIPT_INSCRIPTIONAL_PAHLAVI:
michael@0 471 case HB_SCRIPT_INSCRIPTIONAL_PARTHIAN:
michael@0 472 case HB_SCRIPT_OLD_SOUTH_ARABIAN:
michael@0 473 case HB_SCRIPT_OLD_TURKIC:
michael@0 474 case HB_SCRIPT_SAMARITAN:
michael@0 475
michael@0 476 /* Unicode-6.0 additions */
michael@0 477 case HB_SCRIPT_MANDAIC:
michael@0 478
michael@0 479 /* Unicode-6.1 additions */
michael@0 480 case HB_SCRIPT_MEROITIC_CURSIVE:
michael@0 481 case HB_SCRIPT_MEROITIC_HIEROGLYPHS:
michael@0 482
michael@0 483 return HB_DIRECTION_RTL;
michael@0 484 }
michael@0 485
michael@0 486 return HB_DIRECTION_LTR;
michael@0 487 }
michael@0 488
michael@0 489
michael@0 490 /* hb_user_data_array_t */
michael@0 491
michael@0 492 bool
michael@0 493 hb_user_data_array_t::set (hb_user_data_key_t *key,
michael@0 494 void * data,
michael@0 495 hb_destroy_func_t destroy,
michael@0 496 hb_bool_t replace)
michael@0 497 {
michael@0 498 if (!key)
michael@0 499 return false;
michael@0 500
michael@0 501 if (replace) {
michael@0 502 if (!data && !destroy) {
michael@0 503 items.remove (key, lock);
michael@0 504 return true;
michael@0 505 }
michael@0 506 }
michael@0 507 hb_user_data_item_t item = {key, data, destroy};
michael@0 508 bool ret = !!items.replace_or_insert (item, lock, replace);
michael@0 509
michael@0 510 return ret;
michael@0 511 }
michael@0 512
michael@0 513 void *
michael@0 514 hb_user_data_array_t::get (hb_user_data_key_t *key)
michael@0 515 {
michael@0 516 hb_user_data_item_t item = {NULL };
michael@0 517
michael@0 518 return items.find (key, &item, lock) ? item.data : NULL;
michael@0 519 }
michael@0 520
michael@0 521
michael@0 522 /* hb_version */
michael@0 523
michael@0 524 /**
michael@0 525 * hb_version:
michael@0 526 * @major: (out): Library major version component.
michael@0 527 * @minor: (out): Library minor version component.
michael@0 528 * @micro: (out): Library micro version component.
michael@0 529 *
michael@0 530 * Returns library version as three integer components.
michael@0 531 *
michael@0 532 * Since: 1.0
michael@0 533 **/
michael@0 534 void
michael@0 535 hb_version (unsigned int *major,
michael@0 536 unsigned int *minor,
michael@0 537 unsigned int *micro)
michael@0 538 {
michael@0 539 *major = HB_VERSION_MAJOR;
michael@0 540 *minor = HB_VERSION_MINOR;
michael@0 541 *micro = HB_VERSION_MICRO;
michael@0 542 }
michael@0 543
michael@0 544 /**
michael@0 545 * hb_version_string:
michael@0 546 *
michael@0 547 * Returns library version as a string with three components.
michael@0 548 *
michael@0 549 * Return value: library version string.
michael@0 550 *
michael@0 551 * Since: 1.0
michael@0 552 **/
michael@0 553 const char *
michael@0 554 hb_version_string (void)
michael@0 555 {
michael@0 556 return HB_VERSION_STRING;
michael@0 557 }
michael@0 558
michael@0 559 /**
michael@0 560 * hb_version_check:
michael@0 561 * @major:
michael@0 562 * @minor:
michael@0 563 * @micro:
michael@0 564 *
michael@0 565 *
michael@0 566 *
michael@0 567 * Return value:
michael@0 568 *
michael@0 569 * Since: 1.0
michael@0 570 **/
michael@0 571 hb_bool_t
michael@0 572 hb_version_check (unsigned int major,
michael@0 573 unsigned int minor,
michael@0 574 unsigned int micro)
michael@0 575 {
michael@0 576 return HB_VERSION_CHECK (major, minor, micro);
michael@0 577 }

mercurial