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