9422555cfbc6897730666c2effbd464e9cca4588
[platform/upstream/harfbuzz.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-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
42 hb_tag_t
43 hb_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 void
62 hb_tag_to_string (hb_tag_t tag, char *buf)
63 {
64   buf[0] = (char) (uint8_t) (tag >> 24);
65   buf[1] = (char) (uint8_t) (tag >> 16);
66   buf[2] = (char) (uint8_t) (tag >>  8);
67   buf[3] = (char) (uint8_t) (tag >>  0);
68 }
69
70
71 /* hb_direction_t */
72
73 const char direction_strings[][4] = {
74   "ltr",
75   "rtl",
76   "ttb",
77   "btt"
78 };
79
80 hb_direction_t
81 hb_direction_from_string (const char *str, int len)
82 {
83   if (unlikely (!str || !len || !*str))
84     return HB_DIRECTION_INVALID;
85
86   /* Lets match loosely: just match the first letter, such that
87    * all of "ltr", "left-to-right", etc work!
88    */
89   char c = TOLOWER (str[0]);
90   for (unsigned int i = 0; i < ARRAY_LENGTH (direction_strings); i++)
91     if (c == direction_strings[i][0])
92       return (hb_direction_t) (HB_DIRECTION_LTR + i);
93
94   return HB_DIRECTION_INVALID;
95 }
96
97 const char *
98 hb_direction_to_string (hb_direction_t direction)
99 {
100   if (likely ((unsigned int) (direction - HB_DIRECTION_LTR)
101               < ARRAY_LENGTH (direction_strings)))
102     return direction_strings[direction - HB_DIRECTION_LTR];
103
104   return "invalid";
105 }
106
107
108 /* hb_language_t */
109
110 struct hb_language_impl_t {
111   const char s[1];
112 };
113
114 static const char canon_map[256] = {
115    0,   0,   0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,   0,   0,
116    0,   0,   0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,   0,   0,
117    0,   0,   0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,  '-',  0,   0,
118   '0', '1', '2', '3', '4', '5', '6', '7',  '8', '9',  0,   0,   0,   0,   0,   0,
119   '-', 'a', 'b', 'c', 'd', 'e', 'f', 'g',  'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
120   'p', 'q', 'r', 's', 't', 'u', 'v', 'w',  'x', 'y', 'z',  0,   0,   0,   0,  '-',
121    0,  'a', 'b', 'c', 'd', 'e', 'f', 'g',  'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
122   'p', 'q', 'r', 's', 't', 'u', 'v', 'w',  'x', 'y', 'z',  0,   0,   0,   0,   0
123 };
124
125 static hb_bool_t
126 lang_equal (hb_language_t  v1,
127             const void    *v2)
128 {
129   const unsigned char *p1 = (const unsigned char *) v1;
130   const unsigned char *p2 = (const unsigned char *) v2;
131
132   while (*p1 && *p1 == canon_map[*p2])
133     p1++, p2++;
134
135   return *p1 == canon_map[*p2];
136 }
137
138 #if 0
139 static unsigned int
140 lang_hash (const void *key)
141 {
142   const unsigned char *p = key;
143   unsigned int h = 0;
144   while (canon_map[*p])
145     {
146       h = (h << 5) - h + canon_map[*p];
147       p++;
148     }
149
150   return h;
151 }
152 #endif
153
154
155 struct hb_language_item_t {
156
157   struct hb_language_item_t *next;
158   hb_language_t lang;
159
160   inline bool operator == (const char *s) const {
161     return lang_equal (lang, s);
162   }
163
164   inline hb_language_item_t & operator = (const char *s) {
165     lang = (hb_language_t) strdup (s);
166     for (unsigned char *p = (unsigned char *) lang; *p; p++)
167       *p = canon_map[*p];
168
169     return *this;
170   }
171
172   void finish (void) { free (lang); }
173 };
174
175
176 /* Thread-safe lock-free language list */
177
178 static hb_language_item_t *langs;
179
180 static
181 void free_langs (void)
182 {
183   while (langs) {
184     hb_language_item_t *next = langs->next;
185     langs->finish ();
186     free (langs);
187     langs = next;
188   }
189 }
190
191 static hb_language_item_t *
192 lang_find_or_insert (const char *key)
193 {
194 retry:
195   hb_language_item_t *first_lang = (hb_language_item_t *) hb_atomic_ptr_get (&langs);
196
197   for (hb_language_item_t *lang = first_lang; lang; lang = lang->next)
198     if (*lang == key)
199       return lang;
200
201   /* Not found; allocate one. */
202   hb_language_item_t *lang = (hb_language_item_t *) calloc (1, sizeof (hb_language_item_t));
203   if (unlikely (!lang))
204     return NULL;
205   lang->next = first_lang;
206   *lang = key;
207
208   if (!hb_atomic_ptr_cmpexch (&langs, first_lang, lang)) {
209     free (lang);
210     goto retry;
211   }
212
213 #ifdef HAVE_ATEXIT
214   if (!first_lang)
215     atexit (free_langs); /* First person registers atexit() callback. */
216 #endif
217
218   return lang;
219 }
220
221
222 hb_language_t
223 hb_language_from_string (const char *str, int len)
224 {
225   if (!str || !len || !*str)
226     return HB_LANGUAGE_INVALID;
227
228   char strbuf[32];
229   if (len >= 0) {
230     len = MIN (len, (int) sizeof (strbuf) - 1);
231     str = (char *) memcpy (strbuf, str, len);
232     strbuf[len] = '\0';
233   }
234
235   hb_language_item_t *item = lang_find_or_insert (str);
236
237   return likely (item) ? item->lang : HB_LANGUAGE_INVALID;
238 }
239
240 const char *
241 hb_language_to_string (hb_language_t language)
242 {
243   /* This is actually NULL-safe! */
244   return language->s;
245 }
246
247 hb_language_t
248 hb_language_get_default (void)
249 {
250   static hb_language_t default_language = HB_LANGUAGE_INVALID;
251
252   hb_language_t language = (hb_language_t) hb_atomic_ptr_get (&default_language);
253   if (unlikely (language == HB_LANGUAGE_INVALID)) {
254     language = hb_language_from_string (setlocale (LC_CTYPE, NULL), -1);
255     hb_atomic_ptr_cmpexch (&default_language, HB_LANGUAGE_INVALID, language);
256   }
257
258   return default_language;
259 }
260
261
262 /* hb_script_t */
263
264 hb_script_t
265 hb_script_from_iso15924_tag (hb_tag_t tag)
266 {
267   if (unlikely (tag == HB_TAG_NONE))
268     return HB_SCRIPT_INVALID;
269
270   /* Be lenient, adjust case (one capital letter followed by three small letters) */
271   tag = (tag & 0xDFDFDFDF) | 0x00202020;
272
273   switch (tag) {
274
275     /* These graduated from the 'Q' private-area codes, but
276      * the old code is still aliased by Unicode, and the Qaai
277      * one in use by ICU. */
278     case HB_TAG('Q','a','a','i'): return HB_SCRIPT_INHERITED;
279     case HB_TAG('Q','a','a','c'): return HB_SCRIPT_COPTIC;
280
281     /* Script variants from http://unicode.org/iso15924/ */
282     case HB_TAG('C','y','r','s'): return HB_SCRIPT_CYRILLIC;
283     case HB_TAG('L','a','t','f'): return HB_SCRIPT_LATIN;
284     case HB_TAG('L','a','t','g'): return HB_SCRIPT_LATIN;
285     case HB_TAG('S','y','r','e'): return HB_SCRIPT_SYRIAC;
286     case HB_TAG('S','y','r','j'): return HB_SCRIPT_SYRIAC;
287     case HB_TAG('S','y','r','n'): return HB_SCRIPT_SYRIAC;
288   }
289
290   /* If it looks right, just use the tag as a script */
291   if (((uint32_t) tag & 0xE0E0E0E0) == 0x40606060)
292     return (hb_script_t) tag;
293
294   /* Otherwise, return unknown */
295   return HB_SCRIPT_UNKNOWN;
296 }
297
298 hb_script_t
299 hb_script_from_string (const char *s, int len)
300 {
301   return hb_script_from_iso15924_tag (hb_tag_from_string (s, len));
302 }
303
304 hb_tag_t
305 hb_script_to_iso15924_tag (hb_script_t script)
306 {
307   return (hb_tag_t) script;
308 }
309
310 hb_direction_t
311 hb_script_get_horizontal_direction (hb_script_t script)
312 {
313   /* http://goo.gl/x9ilM */
314   switch ((hb_tag_t) script)
315   {
316     /* Unicode-1.1 additions */
317     case HB_SCRIPT_ARABIC:
318     case HB_SCRIPT_HEBREW:
319
320     /* Unicode-3.0 additions */
321     case HB_SCRIPT_SYRIAC:
322     case HB_SCRIPT_THAANA:
323
324     /* Unicode-4.0 additions */
325     case HB_SCRIPT_CYPRIOT:
326
327     /* Unicode-4.1 additions */
328     case HB_SCRIPT_KHAROSHTHI:
329
330     /* Unicode-5.0 additions */
331     case HB_SCRIPT_PHOENICIAN:
332     case HB_SCRIPT_NKO:
333
334     /* Unicode-5.1 additions */
335     case HB_SCRIPT_LYDIAN:
336
337     /* Unicode-5.2 additions */
338     case HB_SCRIPT_AVESTAN:
339     case HB_SCRIPT_IMPERIAL_ARAMAIC:
340     case HB_SCRIPT_INSCRIPTIONAL_PAHLAVI:
341     case HB_SCRIPT_INSCRIPTIONAL_PARTHIAN:
342     case HB_SCRIPT_OLD_SOUTH_ARABIAN:
343     case HB_SCRIPT_OLD_TURKIC:
344     case HB_SCRIPT_SAMARITAN:
345
346     /* Unicode-6.0 additions */
347     case HB_SCRIPT_MANDAIC:
348
349     /* Unicode-6.1 additions */
350     case HB_SCRIPT_MEROITIC_CURSIVE:
351     case HB_SCRIPT_MEROITIC_HIEROGLYPHS:
352
353       return HB_DIRECTION_RTL;
354   }
355
356   return HB_DIRECTION_LTR;
357 }
358
359
360 /* hb_user_data_array_t */
361
362 bool
363 hb_user_data_array_t::set (hb_user_data_key_t *key,
364                            void *              data,
365                            hb_destroy_func_t   destroy,
366                            hb_bool_t           replace)
367 {
368   if (!key)
369     return false;
370
371   if (replace) {
372     if (!data && !destroy) {
373       items.remove (key, lock);
374       return true;
375     }
376   }
377   hb_user_data_item_t item = {key, data, destroy};
378   bool ret = !!items.replace_or_insert (item, lock, replace);
379
380   return ret;
381 }
382
383 void *
384 hb_user_data_array_t::get (hb_user_data_key_t *key)
385 {
386   hb_user_data_item_t item = {NULL };
387
388   return items.find (key, &item, lock) ? item.data : NULL;
389 }
390
391
392 /* hb_version */
393
394 void
395 hb_version (unsigned int *major,
396             unsigned int *minor,
397             unsigned int *micro)
398 {
399   *major = HB_VERSION_MAJOR;
400   *minor = HB_VERSION_MINOR;
401   *micro = HB_VERSION_MICRO;
402 }
403
404 const char *
405 hb_version_string (void)
406 {
407   return HB_VERSION_STRING;
408 }
409
410 hb_bool_t
411 hb_version_check (unsigned int major,
412                   unsigned int minor,
413                   unsigned int micro)
414 {
415   return HB_VERSION_CHECK (major, minor, micro);
416 }
417
418