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