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