Add git cl format presubmit warning for extension and apps.
[chromium-blink-merge.git] / third_party / harfbuzz-ng / src / hb-common.cc
blobcf24660f79839fcbc9be724455feed8d08e42725
1 /*
2 * Copyright © 2009,2010 Red Hat, Inc.
3 * Copyright © 2011,2012 Google, Inc.
5 * This is part of HarfBuzz, a text shaping library.
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.
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.
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.
25 * Red Hat Author(s): Behdad Esfahbod
26 * Google Author(s): Behdad Esfahbod
29 #include "hb-private.hh"
31 #include "hb-version.h"
33 #include "hb-mutex-private.hh"
34 #include "hb-object-private.hh"
36 #include <locale.h>
39 /* hb_options_t */
41 hb_options_union_t _hb_options;
43 void
44 _hb_options_init (void)
46 hb_options_union_t u;
47 u.i = 0;
48 u.opts.initialized = 1;
50 char *c = getenv ("HB_OPTIONS");
51 u.opts.uniscribe_bug_compatible = c && strstr (c, "uniscribe-bug-compatible");
53 /* This is idempotent and threadsafe. */
54 _hb_options = u;
58 /* hb_tag_t */
60 /**
61 * hb_tag_from_string:
62 * @str: (array length=len):
63 * @len:
67 * Return value:
69 * Since: 1.0
70 **/
71 hb_tag_t
72 hb_tag_from_string (const char *str, int len)
74 char tag[4];
75 unsigned int i;
77 if (!str || !len || !*str)
78 return HB_TAG_NONE;
80 if (len < 0 || len > 4)
81 len = 4;
82 for (i = 0; i < (unsigned) len && str[i]; i++)
83 tag[i] = str[i];
84 for (; i < 4; i++)
85 tag[i] = ' ';
87 return HB_TAG_CHAR4 (tag);
90 /**
91 * hb_tag_to_string:
92 * @tag:
93 * @buf: (array fixed-size=4):
97 * Since: 1.0
98 **/
99 void
100 hb_tag_to_string (hb_tag_t tag, char *buf)
102 buf[0] = (char) (uint8_t) (tag >> 24);
103 buf[1] = (char) (uint8_t) (tag >> 16);
104 buf[2] = (char) (uint8_t) (tag >> 8);
105 buf[3] = (char) (uint8_t) (tag >> 0);
109 /* hb_direction_t */
111 const char direction_strings[][4] = {
112 "ltr",
113 "rtl",
114 "ttb",
115 "btt"
119 * hb_direction_from_string:
120 * @str: (array length=len):
121 * @len:
125 * Return value:
127 * Since: 1.0
129 hb_direction_t
130 hb_direction_from_string (const char *str, int len)
132 if (unlikely (!str || !len || !*str))
133 return HB_DIRECTION_INVALID;
135 /* Lets match loosely: just match the first letter, such that
136 * all of "ltr", "left-to-right", etc work!
138 char c = TOLOWER (str[0]);
139 for (unsigned int i = 0; i < ARRAY_LENGTH (direction_strings); i++)
140 if (c == direction_strings[i][0])
141 return (hb_direction_t) (HB_DIRECTION_LTR + i);
143 return HB_DIRECTION_INVALID;
147 * hb_direction_to_string:
148 * @direction:
152 * Return value: (transfer none):
154 * Since: 1.0
156 const char *
157 hb_direction_to_string (hb_direction_t direction)
159 if (likely ((unsigned int) (direction - HB_DIRECTION_LTR)
160 < ARRAY_LENGTH (direction_strings)))
161 return direction_strings[direction - HB_DIRECTION_LTR];
163 return "invalid";
167 /* hb_language_t */
169 struct hb_language_impl_t {
170 const char s[1];
173 static const char canon_map[256] = {
174 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
175 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
176 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0,
177 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 0, 0, 0, 0, 0, 0,
178 '-', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
179 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, '-',
180 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
181 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, 0
184 static hb_bool_t
185 lang_equal (hb_language_t v1,
186 const void *v2)
188 const unsigned char *p1 = (const unsigned char *) v1;
189 const unsigned char *p2 = (const unsigned char *) v2;
191 while (*p1 && *p1 == canon_map[*p2])
192 p1++, p2++;
194 return *p1 == canon_map[*p2];
197 #if 0
198 static unsigned int
199 lang_hash (const void *key)
201 const unsigned char *p = key;
202 unsigned int h = 0;
203 while (canon_map[*p])
205 h = (h << 5) - h + canon_map[*p];
206 p++;
209 return h;
211 #endif
214 struct hb_language_item_t {
216 struct hb_language_item_t *next;
217 hb_language_t lang;
219 inline bool operator == (const char *s) const {
220 return lang_equal (lang, s);
223 inline hb_language_item_t & operator = (const char *s) {
224 lang = (hb_language_t) strdup (s);
225 for (unsigned char *p = (unsigned char *) lang; *p; p++)
226 *p = canon_map[*p];
228 return *this;
231 void finish (void) { free ((void *) lang); }
235 /* Thread-safe lock-free language list */
237 static hb_language_item_t *langs;
239 static inline
240 void free_langs (void)
242 while (langs) {
243 hb_language_item_t *next = langs->next;
244 langs->finish ();
245 free (langs);
246 langs = next;
250 static hb_language_item_t *
251 lang_find_or_insert (const char *key)
253 retry:
254 hb_language_item_t *first_lang = (hb_language_item_t *) hb_atomic_ptr_get (&langs);
256 for (hb_language_item_t *lang = first_lang; lang; lang = lang->next)
257 if (*lang == key)
258 return lang;
260 /* Not found; allocate one. */
261 hb_language_item_t *lang = (hb_language_item_t *) calloc (1, sizeof (hb_language_item_t));
262 if (unlikely (!lang))
263 return NULL;
264 lang->next = first_lang;
265 *lang = key;
267 if (!hb_atomic_ptr_cmpexch (&langs, first_lang, lang)) {
268 free (lang);
269 goto retry;
272 #ifdef HAVE_ATEXIT
273 if (!first_lang)
274 atexit (free_langs); /* First person registers atexit() callback. */
275 #endif
277 return lang;
282 * hb_language_from_string:
283 * @str: (array length=len):
284 * @len:
288 * Return value:
290 * Since: 1.0
292 hb_language_t
293 hb_language_from_string (const char *str, int len)
295 char strbuf[64];
297 if (!str || !len || !*str)
298 return HB_LANGUAGE_INVALID;
300 if (len >= 0)
302 len = MIN (len, (int) sizeof (strbuf) - 1);
303 str = (char *) memcpy (strbuf, str, len);
304 strbuf[len] = '\0';
307 hb_language_item_t *item = lang_find_or_insert (str);
309 return likely (item) ? item->lang : HB_LANGUAGE_INVALID;
313 * hb_language_to_string:
314 * @language:
318 * Return value: (transfer none):
320 * Since: 1.0
322 const char *
323 hb_language_to_string (hb_language_t language)
325 /* This is actually NULL-safe! */
326 return language->s;
330 * hb_language_get_default:
334 * Return value:
336 * Since: 1.0
338 hb_language_t
339 hb_language_get_default (void)
341 static hb_language_t default_language = HB_LANGUAGE_INVALID;
343 hb_language_t language = (hb_language_t) hb_atomic_ptr_get (&default_language);
344 if (unlikely (language == HB_LANGUAGE_INVALID)) {
345 language = hb_language_from_string (setlocale (LC_CTYPE, NULL), -1);
346 hb_atomic_ptr_cmpexch (&default_language, HB_LANGUAGE_INVALID, language);
349 return default_language;
353 /* hb_script_t */
356 * hb_script_from_iso15924_tag:
357 * @tag:
361 * Return value:
363 * Since: 1.0
365 hb_script_t
366 hb_script_from_iso15924_tag (hb_tag_t tag)
368 if (unlikely (tag == HB_TAG_NONE))
369 return HB_SCRIPT_INVALID;
371 /* Be lenient, adjust case (one capital letter followed by three small letters) */
372 tag = (tag & 0xDFDFDFDF) | 0x00202020;
374 switch (tag) {
376 /* These graduated from the 'Q' private-area codes, but
377 * the old code is still aliased by Unicode, and the Qaai
378 * one in use by ICU. */
379 case HB_TAG('Q','a','a','i'): return HB_SCRIPT_INHERITED;
380 case HB_TAG('Q','a','a','c'): return HB_SCRIPT_COPTIC;
382 /* Script variants from http://unicode.org/iso15924/ */
383 case HB_TAG('C','y','r','s'): return HB_SCRIPT_CYRILLIC;
384 case HB_TAG('L','a','t','f'): return HB_SCRIPT_LATIN;
385 case HB_TAG('L','a','t','g'): return HB_SCRIPT_LATIN;
386 case HB_TAG('S','y','r','e'): return HB_SCRIPT_SYRIAC;
387 case HB_TAG('S','y','r','j'): return HB_SCRIPT_SYRIAC;
388 case HB_TAG('S','y','r','n'): return HB_SCRIPT_SYRIAC;
391 /* If it looks right, just use the tag as a script */
392 if (((uint32_t) tag & 0xE0E0E0E0) == 0x40606060)
393 return (hb_script_t) tag;
395 /* Otherwise, return unknown */
396 return HB_SCRIPT_UNKNOWN;
400 * hb_script_from_string:
401 * @s: (array length=len):
402 * @len:
406 * Return value:
408 * Since: 1.0
410 hb_script_t
411 hb_script_from_string (const char *s, int len)
413 return hb_script_from_iso15924_tag (hb_tag_from_string (s, len));
417 * hb_script_to_iso15924_tag:
418 * @script:
422 * Return value:
424 * Since: 1.0
426 hb_tag_t
427 hb_script_to_iso15924_tag (hb_script_t script)
429 return (hb_tag_t) script;
433 * hb_script_get_horizontal_direction:
434 * @script:
438 * Return value:
440 * Since: 1.0
442 hb_direction_t
443 hb_script_get_horizontal_direction (hb_script_t script)
445 /* http://goo.gl/x9ilM */
446 switch ((hb_tag_t) script)
448 /* Unicode-1.1 additions */
449 case HB_SCRIPT_ARABIC:
450 case HB_SCRIPT_HEBREW:
452 /* Unicode-3.0 additions */
453 case HB_SCRIPT_SYRIAC:
454 case HB_SCRIPT_THAANA:
456 /* Unicode-4.0 additions */
457 case HB_SCRIPT_CYPRIOT:
459 /* Unicode-4.1 additions */
460 case HB_SCRIPT_KHAROSHTHI:
462 /* Unicode-5.0 additions */
463 case HB_SCRIPT_PHOENICIAN:
464 case HB_SCRIPT_NKO:
466 /* Unicode-5.1 additions */
467 case HB_SCRIPT_LYDIAN:
469 /* Unicode-5.2 additions */
470 case HB_SCRIPT_AVESTAN:
471 case HB_SCRIPT_IMPERIAL_ARAMAIC:
472 case HB_SCRIPT_INSCRIPTIONAL_PAHLAVI:
473 case HB_SCRIPT_INSCRIPTIONAL_PARTHIAN:
474 case HB_SCRIPT_OLD_SOUTH_ARABIAN:
475 case HB_SCRIPT_OLD_TURKIC:
476 case HB_SCRIPT_SAMARITAN:
478 /* Unicode-6.0 additions */
479 case HB_SCRIPT_MANDAIC:
481 /* Unicode-6.1 additions */
482 case HB_SCRIPT_MEROITIC_CURSIVE:
483 case HB_SCRIPT_MEROITIC_HIEROGLYPHS:
485 return HB_DIRECTION_RTL;
488 return HB_DIRECTION_LTR;
492 /* hb_user_data_array_t */
494 bool
495 hb_user_data_array_t::set (hb_user_data_key_t *key,
496 void * data,
497 hb_destroy_func_t destroy,
498 hb_bool_t replace)
500 if (!key)
501 return false;
503 if (replace) {
504 if (!data && !destroy) {
505 items.remove (key, lock);
506 return true;
509 hb_user_data_item_t item = {key, data, destroy};
510 bool ret = !!items.replace_or_insert (item, lock, replace);
512 return ret;
515 void *
516 hb_user_data_array_t::get (hb_user_data_key_t *key)
518 hb_user_data_item_t item = {NULL };
520 return items.find (key, &item, lock) ? item.data : NULL;
524 /* hb_version */
527 * hb_version:
528 * @major: (out): Library major version component.
529 * @minor: (out): Library minor version component.
530 * @micro: (out): Library micro version component.
532 * Returns library version as three integer components.
534 * Since: 1.0
536 void
537 hb_version (unsigned int *major,
538 unsigned int *minor,
539 unsigned int *micro)
541 *major = HB_VERSION_MAJOR;
542 *minor = HB_VERSION_MINOR;
543 *micro = HB_VERSION_MICRO;
547 * hb_version_string:
549 * Returns library version as a string with three components.
551 * Return value: library version string.
553 * Since: 1.0
555 const char *
556 hb_version_string (void)
558 return HB_VERSION_STRING;
562 * hb_version_check:
563 * @major:
564 * @minor:
565 * @micro:
569 * Return value:
571 * Since: 1.0
573 hb_bool_t
574 hb_version_check (unsigned int major,
575 unsigned int minor,
576 unsigned int micro)
578 return HB_VERSION_CHECK (major, minor, micro);