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