hb-common.cc revision 33ccc77902660ed4b49184e5ec99f4fd0ef63175
1/* 2 * Copyright © 2009,2010 Red Hat, Inc. 3 * Copyright © 2011 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) 44{ 45 char tag[4]; 46 unsigned int i; 47 48 if (!s || !*s) 49 return HB_TAG_NONE; 50 51 for (i = 0; i < 4 && s[i]; i++) 52 tag[i] = s[i]; 53 for (; i < 4; i++) 54 tag[i] = ' '; 55 56 return HB_TAG_CHAR4 (tag); 57} 58 59 60/* hb_direction_t */ 61 62const char direction_strings[][4] = { 63 "ltr", 64 "rtl", 65 "ttb", 66 "btt" 67}; 68 69hb_direction_t 70hb_direction_from_string (const char *str) 71{ 72 if (unlikely (!str || !*str)) 73 return HB_DIRECTION_INVALID; 74 75 /* Lets match loosely: just match the first letter, such that 76 * all of "ltr", "left-to-right", etc work! 77 */ 78 char c = TOLOWER (str[0]); 79 for (unsigned int i = 0; i < ARRAY_LENGTH (direction_strings); i++) 80 if (c == direction_strings[i][0]) 81 return (hb_direction_t) i; 82 83 return HB_DIRECTION_INVALID; 84} 85 86const char * 87hb_direction_to_string (hb_direction_t direction) 88{ 89 if (likely ((unsigned int) direction < ARRAY_LENGTH (direction_strings))) 90 return direction_strings[direction]; 91 92 return "invalid"; 93} 94 95 96/* hb_language_t */ 97 98struct _hb_language_t { 99 const char s[1]; 100}; 101 102static const char canon_map[256] = { 103 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 104 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 105 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0, 106 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 0, 0, 0, 0, 0, 0, 107 '-', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 108 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, '-', 109 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 110 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, 0 111}; 112 113static hb_bool_t 114lang_equal (const void *v1, 115 const void *v2) 116{ 117 const unsigned char *p1 = (const unsigned char *) v1; 118 const unsigned char *p2 = (const unsigned char *) v2; 119 120 while (canon_map[*p1] && canon_map[*p1] == canon_map[*p2]) 121 { 122 p1++, p2++; 123 } 124 125 return (canon_map[*p1] == canon_map[*p2]); 126} 127 128#if 0 129static unsigned int 130lang_hash (const void *key) 131{ 132 const unsigned char *p = key; 133 unsigned int h = 0; 134 while (canon_map[*p]) 135 { 136 h = (h << 5) - h + canon_map[*p]; 137 p++; 138 } 139 140 return h; 141} 142#endif 143 144 145struct hb_language_item_t { 146 147 hb_language_t lang; 148 149 inline bool operator == (const char *s) const { 150 return lang_equal (lang, s); 151 } 152 153 inline hb_language_item_t & operator = (const char *s) { 154 lang = (hb_language_t) strdup (s); 155 for (unsigned char *p = (unsigned char *) lang; *p; p++) 156 *p = canon_map[*p]; 157 158 return *this; 159 } 160 161 void finish (void) { free (lang); } 162}; 163 164static struct hb_static_lang_set_t : hb_lockable_set_t<hb_language_item_t, hb_static_mutex_t> { 165 ~hb_static_lang_set_t (void) { this->finish (lock); } 166 hb_static_mutex_t lock; 167} langs; 168 169hb_language_t 170hb_language_from_string (const char *str) 171{ 172 if (!str || !*str) 173 return HB_LANGUAGE_INVALID; 174 175 hb_language_item_t *item = langs.find_or_insert (str, langs.lock); 176 177 return likely (item) ? item->lang : HB_LANGUAGE_INVALID; 178} 179 180const char * 181hb_language_to_string (hb_language_t language) 182{ 183 /* This is actually NULL-safe! */ 184 return language->s; 185} 186 187hb_language_t 188hb_language_get_default (void) 189{ 190 static hb_language_t default_language; 191 192 if (!default_language) { 193 /* This block is not quite threadsafe, but is not as bad as 194 * it looks since it's idempotent. As long as pointer ops 195 * are atomic, we are safe. */ 196 197 /* I hear that setlocale() doesn't honor env vars on Windows, 198 * but for now we ignore that. */ 199 200 default_language = hb_language_from_string (setlocale (LC_CTYPE, NULL)); 201 } 202 203 return default_language; 204} 205 206 207/* hb_script_t */ 208 209hb_script_t 210hb_script_from_iso15924_tag (hb_tag_t tag) 211{ 212 if (unlikely (tag == HB_TAG_NONE)) 213 return HB_SCRIPT_INVALID; 214 215 /* Be lenient, adjust case (one capital letter followed by three small letters) */ 216 tag = (tag & 0xDFDFDFDF) | 0x00202020; 217 218 switch (tag) { 219 220 /* These graduated from the 'Q' private-area codes, but 221 * the old code is still aliased by Unicode, and the Qaai 222 * one in use by ICU. */ 223 case HB_TAG('Q','a','a','i'): return HB_SCRIPT_INHERITED; 224 case HB_TAG('Q','a','a','c'): return HB_SCRIPT_COPTIC; 225 226 /* Script variants from http://unicode.org/iso15924/ */ 227 case HB_TAG('C','y','r','s'): return HB_SCRIPT_CYRILLIC; 228 case HB_TAG('L','a','t','f'): return HB_SCRIPT_LATIN; 229 case HB_TAG('L','a','t','g'): return HB_SCRIPT_LATIN; 230 case HB_TAG('S','y','r','e'): return HB_SCRIPT_SYRIAC; 231 case HB_TAG('S','y','r','j'): return HB_SCRIPT_SYRIAC; 232 case HB_TAG('S','y','r','n'): return HB_SCRIPT_SYRIAC; 233 } 234 235 /* If it looks right, just use the tag as a script */ 236 if (((uint32_t) tag & 0xE0E0E0E0) == 0x40606060) 237 return (hb_script_t) tag; 238 239 /* Otherwise, return unknown */ 240 return HB_SCRIPT_UNKNOWN; 241} 242 243hb_script_t 244hb_script_from_string (const char *s) 245{ 246 return hb_script_from_iso15924_tag (hb_tag_from_string (s)); 247} 248 249hb_tag_t 250hb_script_to_iso15924_tag (hb_script_t script) 251{ 252 return (hb_tag_t) script; 253} 254 255hb_direction_t 256hb_script_get_horizontal_direction (hb_script_t script) 257{ 258 switch ((hb_tag_t) script) 259 { 260 case HB_SCRIPT_ARABIC: 261 case HB_SCRIPT_HEBREW: 262 case HB_SCRIPT_SYRIAC: 263 case HB_SCRIPT_THAANA: 264 265 /* Unicode-4.0 additions */ 266 case HB_SCRIPT_CYPRIOT: 267 268 /* Unicode-5.0 additions */ 269 case HB_SCRIPT_PHOENICIAN: 270 case HB_SCRIPT_NKO: 271 272 /* Unicode-5.2 additions */ 273 case HB_SCRIPT_AVESTAN: 274 case HB_SCRIPT_IMPERIAL_ARAMAIC: 275 case HB_SCRIPT_INSCRIPTIONAL_PAHLAVI: 276 case HB_SCRIPT_INSCRIPTIONAL_PARTHIAN: 277 case HB_SCRIPT_OLD_SOUTH_ARABIAN: 278 case HB_SCRIPT_OLD_TURKIC: 279 case HB_SCRIPT_SAMARITAN: 280 281 /* Unicode-6.0 additions */ 282 case HB_SCRIPT_MANDAIC: 283 284 return HB_DIRECTION_RTL; 285 } 286 287 return HB_DIRECTION_LTR; 288} 289 290 291/* hb_user_data_array_t */ 292 293 294/* NOTE: Currently we use a global lock for user_data access 295 * threadsafety. If one day we add a mutex to any object, we 296 * should switch to using that insted for these too. 297 */ 298 299static hb_static_mutex_t user_data_lock; 300 301bool 302hb_user_data_array_t::set (hb_user_data_key_t *key, 303 void * data, 304 hb_destroy_func_t destroy, 305 hb_bool_t replace) 306{ 307 if (!key) 308 return false; 309 310 if (replace) { 311 if (!data && !destroy) { 312 items.remove (key, user_data_lock); 313 return true; 314 } 315 } 316 hb_user_data_item_t item = {key, data, destroy}; 317 bool ret = !!items.replace_or_insert (item, user_data_lock, replace); 318 319 return ret; 320} 321 322void * 323hb_user_data_array_t::get (hb_user_data_key_t *key) 324{ 325 hb_user_data_item_t item = {NULL }; 326 327 return items.find (key, &item, user_data_lock) ? item.data : NULL; 328} 329 330void 331hb_user_data_array_t::finish (void) 332{ 333 items.finish (user_data_lock); 334} 335 336 337/* hb_version */ 338 339void 340hb_version (unsigned int *major, 341 unsigned int *minor, 342 unsigned int *micro) 343{ 344 *major = HB_VERSION_MAJOR; 345 *minor = HB_VERSION_MINOR; 346 *micro = HB_VERSION_MICRO; 347} 348 349const char * 350hb_version_string (void) 351{ 352 return HB_VERSION_STRING; 353} 354 355hb_bool_t 356hb_version_check (unsigned int major, 357 unsigned int minor, 358 unsigned int micro) 359{ 360 return HB_VERSION_CHECK (major, minor, micro); 361} 362 363 364