331d255b45f38e12cdf839c6ee6a062bc317594f
[framework/uifw/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_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;
242
243   if (!default_language) {
244     /* This block is not quite threadsafe, but is not as bad as
245      * it looks since it's idempotent.  As long as pointer ops
246      * are atomic, we are safe. */
247
248     /* I hear that setlocale() doesn't honor env vars on Windows,
249      * but for now we ignore that. */
250
251     default_language = hb_language_from_string (setlocale (LC_CTYPE, NULL), -1);
252   }
253
254   return default_language;
255 }
256
257
258 /* hb_script_t */
259
260 hb_script_t
261 hb_script_from_iso15924_tag (hb_tag_t tag)
262 {
263   if (unlikely (tag == HB_TAG_NONE))
264     return HB_SCRIPT_INVALID;
265
266   /* Be lenient, adjust case (one capital letter followed by three small letters) */
267   tag = (tag & 0xDFDFDFDF) | 0x00202020;
268
269   switch (tag) {
270
271     /* These graduated from the 'Q' private-area codes, but
272      * the old code is still aliased by Unicode, and the Qaai
273      * one in use by ICU. */
274     case HB_TAG('Q','a','a','i'): return HB_SCRIPT_INHERITED;
275     case HB_TAG('Q','a','a','c'): return HB_SCRIPT_COPTIC;
276
277     /* Script variants from http://unicode.org/iso15924/ */
278     case HB_TAG('C','y','r','s'): return HB_SCRIPT_CYRILLIC;
279     case HB_TAG('L','a','t','f'): return HB_SCRIPT_LATIN;
280     case HB_TAG('L','a','t','g'): return HB_SCRIPT_LATIN;
281     case HB_TAG('S','y','r','e'): return HB_SCRIPT_SYRIAC;
282     case HB_TAG('S','y','r','j'): return HB_SCRIPT_SYRIAC;
283     case HB_TAG('S','y','r','n'): return HB_SCRIPT_SYRIAC;
284   }
285
286   /* If it looks right, just use the tag as a script */
287   if (((uint32_t) tag & 0xE0E0E0E0) == 0x40606060)
288     return (hb_script_t) tag;
289
290   /* Otherwise, return unknown */
291   return HB_SCRIPT_UNKNOWN;
292 }
293
294 hb_script_t
295 hb_script_from_string (const char *s, int len)
296 {
297   return hb_script_from_iso15924_tag (hb_tag_from_string (s, len));
298 }
299
300 hb_tag_t
301 hb_script_to_iso15924_tag (hb_script_t script)
302 {
303   return (hb_tag_t) script;
304 }
305
306 hb_direction_t
307 hb_script_get_horizontal_direction (hb_script_t script)
308 {
309   /* http://goo.gl/x9ilM */
310   switch ((hb_tag_t) script)
311   {
312     /* Unicode-1.1 additions */
313     case HB_SCRIPT_ARABIC:
314     case HB_SCRIPT_HEBREW:
315
316     /* Unicode-3.0 additions */
317     case HB_SCRIPT_SYRIAC:
318     case HB_SCRIPT_THAANA:
319
320     /* Unicode-4.0 additions */
321     case HB_SCRIPT_CYPRIOT:
322
323     /* Unicode-4.1 additions */
324     case HB_SCRIPT_KHAROSHTHI:
325
326     /* Unicode-5.0 additions */
327     case HB_SCRIPT_PHOENICIAN:
328     case HB_SCRIPT_NKO:
329
330     /* Unicode-5.1 additions */
331     case HB_SCRIPT_LYDIAN:
332
333     /* Unicode-5.2 additions */
334     case HB_SCRIPT_AVESTAN:
335     case HB_SCRIPT_IMPERIAL_ARAMAIC:
336     case HB_SCRIPT_INSCRIPTIONAL_PAHLAVI:
337     case HB_SCRIPT_INSCRIPTIONAL_PARTHIAN:
338     case HB_SCRIPT_OLD_SOUTH_ARABIAN:
339     case HB_SCRIPT_OLD_TURKIC:
340     case HB_SCRIPT_SAMARITAN:
341
342     /* Unicode-6.0 additions */
343     case HB_SCRIPT_MANDAIC:
344
345     /* Unicode-6.1 additions */
346     case HB_SCRIPT_MEROITIC_CURSIVE:
347     case HB_SCRIPT_MEROITIC_HIEROGLYPHS:
348
349       return HB_DIRECTION_RTL;
350   }
351
352   return HB_DIRECTION_LTR;
353 }
354
355
356 /* hb_user_data_array_t */
357
358 bool
359 hb_user_data_array_t::set (hb_user_data_key_t *key,
360                            void *              data,
361                            hb_destroy_func_t   destroy,
362                            hb_bool_t           replace,
363                            hb_mutex_t         &lock)
364 {
365   if (!key)
366     return false;
367
368   if (replace) {
369     if (!data && !destroy) {
370       items.remove (key, lock);
371       return true;
372     }
373   }
374   hb_user_data_item_t item = {key, data, destroy};
375   bool ret = !!items.replace_or_insert (item, lock, replace);
376
377   return ret;
378 }
379
380 void *
381 hb_user_data_array_t::get (hb_user_data_key_t *key,
382                            hb_mutex_t         &lock)
383 {
384   hb_user_data_item_t item = {NULL };
385
386   return items.find (key, &item, lock) ? item.data : NULL;
387 }
388
389 void
390 hb_user_data_array_t::finish (hb_mutex_t &lock)
391 {
392   items.finish (lock);
393 }
394
395
396 /* hb_version */
397
398 void
399 hb_version (unsigned int *major,
400             unsigned int *minor,
401             unsigned int *micro)
402 {
403   *major = HB_VERSION_MAJOR;
404   *minor = HB_VERSION_MINOR;
405   *micro = HB_VERSION_MICRO;
406 }
407
408 const char *
409 hb_version_string (void)
410 {
411   return HB_VERSION_STRING;
412 }
413
414 hb_bool_t
415 hb_version_check (unsigned int major,
416                   unsigned int minor,
417                   unsigned int micro)
418 {
419   return HB_VERSION_CHECK (major, minor, micro);
420 }
421
422