Towards normalization
[framework/uifw/harfbuzz.git] / src / hb-common.cc
1 /*
2  * Copyright © 2009,2010  Red Hat, Inc.
3  * Copyright © 2011  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 HB_BEGIN_DECLS
39
40
41 /* hb_tag_t */
42
43 hb_tag_t
44 hb_tag_from_string (const char *s)
45 {
46   char tag[4];
47   unsigned int i;
48
49   if (!s || !*s)
50     return HB_TAG_NONE;
51
52   for (i = 0; i < 4 && s[i]; i++)
53     tag[i] = s[i];
54   for (; i < 4; i++)
55     tag[i] = ' ';
56
57   return HB_TAG_CHAR4 (tag);
58 }
59
60
61 /* hb_direction_t */
62
63 const char direction_strings[][4] = {
64   "ltr",
65   "rtl",
66   "ttb",
67   "btt"
68 };
69
70 hb_direction_t
71 hb_direction_from_string (const char *str)
72 {
73   if (unlikely (!str || !*str))
74     return HB_DIRECTION_INVALID;
75
76   /* Lets match loosely: just match the first letter, such that
77    * all of "ltr", "left-to-right", etc work!
78    */
79   char c = TOLOWER (str[0]);
80   for (unsigned int i = 0; i < ARRAY_LENGTH (direction_strings); i++)
81     if (c == direction_strings[i][0])
82       return (hb_direction_t) i;
83
84   return HB_DIRECTION_INVALID;
85 }
86
87 const char *
88 hb_direction_to_string (hb_direction_t direction)
89 {
90   if (likely ((unsigned int) direction < ARRAY_LENGTH (direction_strings)))
91     return direction_strings[direction];
92
93   return "invalid";
94 }
95
96
97 /* hb_language_t */
98
99 struct _hb_language_t {
100   const char s[1];
101 };
102
103 static const char canon_map[256] = {
104    0,   0,   0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,   0,   0,
105    0,   0,   0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,   0,   0,
106    0,   0,   0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,  '-',  0,   0,
107   '0', '1', '2', '3', '4', '5', '6', '7',  '8', '9',  0,   0,   0,   0,   0,   0,
108   '-', 'a', 'b', 'c', 'd', 'e', 'f', 'g',  'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
109   'p', 'q', 'r', 's', 't', 'u', 'v', 'w',  'x', 'y', 'z',  0,   0,   0,   0,  '-',
110    0,  '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,   0
112 };
113
114 static hb_bool_t
115 lang_equal (const void *v1,
116             const void *v2)
117 {
118   const unsigned char *p1 = (const unsigned char *) v1;
119   const unsigned char *p2 = (const unsigned char *) v2;
120
121   while (canon_map[*p1] && canon_map[*p1] == canon_map[*p2])
122     {
123       p1++, p2++;
124     }
125
126   return (canon_map[*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   hb_language_t lang;
149
150   inline bool operator == (const char *s) const {
151     return lang_equal (lang, s);
152   }
153
154   inline hb_language_item_t & operator = (const char *s) {
155     lang = (hb_language_t) strdup (s);
156     for (unsigned char *p = (unsigned char *) lang; *p; p++)
157       *p = canon_map[*p];
158
159     return *this;
160   }
161
162   void finish (void) { free (lang); }
163 };
164
165 static hb_static_mutex_t langs_lock;
166 static hb_lockable_set_t<hb_language_item_t, hb_static_mutex_t> langs;
167
168 hb_language_t
169 hb_language_from_string (const char *str)
170 {
171   if (!str || !*str)
172     return HB_LANGUAGE_INVALID;
173
174   hb_language_item_t *item = langs.find_or_insert (str, langs_lock);
175
176   return likely (item) ? item->lang : HB_LANGUAGE_INVALID;
177 }
178
179 const char *
180 hb_language_to_string (hb_language_t language)
181 {
182   /* This is actually NULL-safe! */
183   return language->s;
184 }
185
186 hb_language_t
187 hb_language_get_default (void)
188 {
189   static hb_language_t default_language;
190
191   if (!default_language) {
192     /* This block is not quite threadsafe, but is not as bad as
193      * it looks since it's idempotent.  As long as pointer ops
194      * are atomic, we are safe. */
195
196     /* I hear that setlocale() doesn't honor env vars on Windows,
197      * but for now we ignore that. */
198
199     default_language = hb_language_from_string (setlocale (LC_CTYPE, NULL));
200   }
201
202   return default_language;
203 }
204
205
206 /* hb_script_t */
207
208 hb_script_t
209 hb_script_from_iso15924_tag (hb_tag_t tag)
210 {
211   if (unlikely (tag == HB_TAG_NONE))
212     return HB_SCRIPT_INVALID;
213
214   /* Be lenient, adjust case (one capital letter followed by three small letters) */
215   tag = (tag & 0xDFDFDFDF) | 0x00202020;
216
217   switch (tag) {
218
219     /* These graduated from the 'Q' private-area codes, but
220      * the old code is still aliased by Unicode, and the Qaai
221      * one in use by ICU. */
222     case HB_TAG('Q','a','a','i'): return HB_SCRIPT_INHERITED;
223     case HB_TAG('Q','a','a','c'): return HB_SCRIPT_COPTIC;
224
225     /* Script variants from http://unicode.org/iso15924/ */
226     case HB_TAG('C','y','r','s'): return HB_SCRIPT_CYRILLIC;
227     case HB_TAG('L','a','t','f'): return HB_SCRIPT_LATIN;
228     case HB_TAG('L','a','t','g'): return HB_SCRIPT_LATIN;
229     case HB_TAG('S','y','r','e'): return HB_SCRIPT_SYRIAC;
230     case HB_TAG('S','y','r','j'): return HB_SCRIPT_SYRIAC;
231     case HB_TAG('S','y','r','n'): return HB_SCRIPT_SYRIAC;
232   }
233
234   /* If it looks right, just use the tag as a script */
235   if (((uint32_t) tag & 0xE0E0E0E0) == 0x40606060)
236     return (hb_script_t) tag;
237
238   /* Otherwise, return unknown */
239   return HB_SCRIPT_UNKNOWN;
240 }
241
242 hb_script_t
243 hb_script_from_string (const char *s)
244 {
245   return hb_script_from_iso15924_tag (hb_tag_from_string (s));
246 }
247
248 hb_tag_t
249 hb_script_to_iso15924_tag (hb_script_t script)
250 {
251   return (hb_tag_t) script;
252 }
253
254 hb_direction_t
255 hb_script_get_horizontal_direction (hb_script_t script)
256 {
257   switch ((hb_tag_t) script)
258   {
259     case HB_SCRIPT_ARABIC:
260     case HB_SCRIPT_HEBREW:
261     case HB_SCRIPT_SYRIAC:
262     case HB_SCRIPT_THAANA:
263
264     /* Unicode-4.0 additions */
265     case HB_SCRIPT_CYPRIOT:
266
267     /* Unicode-5.0 additions */
268     case HB_SCRIPT_PHOENICIAN:
269     case HB_SCRIPT_NKO:
270
271     /* Unicode-5.2 additions */
272     case HB_SCRIPT_AVESTAN:
273     case HB_SCRIPT_IMPERIAL_ARAMAIC:
274     case HB_SCRIPT_INSCRIPTIONAL_PAHLAVI:
275     case HB_SCRIPT_INSCRIPTIONAL_PARTHIAN:
276     case HB_SCRIPT_OLD_SOUTH_ARABIAN:
277     case HB_SCRIPT_OLD_TURKIC:
278     case HB_SCRIPT_SAMARITAN:
279
280     /* Unicode-6.0 additions */
281     case HB_SCRIPT_MANDAIC:
282
283       return HB_DIRECTION_RTL;
284   }
285
286   return HB_DIRECTION_LTR;
287 }
288
289
290 /* hb_user_data_array_t */
291
292
293 /* NOTE: Currently we use a global lock for user_data access
294  * threadsafety.  If one day we add a mutex to any object, we
295  * should switch to using that insted for these too.
296  */
297
298 static hb_static_mutex_t user_data_lock;
299
300 bool
301 hb_user_data_array_t::set (hb_user_data_key_t *key,
302                            void *              data,
303                            hb_destroy_func_t   destroy)
304 {
305   if (!key)
306     return false;
307
308   if (!data && !destroy) {
309     items.remove (key, user_data_lock);
310     return true;
311   }
312   hb_user_data_item_t item = {key, data, destroy};
313   bool ret = !!items.replace_or_insert (item, user_data_lock);
314
315   return ret;
316 }
317
318 void *
319 hb_user_data_array_t::get (hb_user_data_key_t *key)
320 {
321   hb_user_data_item_t item = {NULL };
322
323   return items.find (key, &item, user_data_lock) ? item.data : NULL;
324 }
325
326 void
327 hb_user_data_array_t::finish (void)
328 {
329   items.finish (user_data_lock);
330 }
331
332
333 /* hb_version */
334
335 void
336 hb_version (unsigned int *major,
337             unsigned int *minor,
338             unsigned int *micro)
339 {
340   *major = HB_VERSION_MAJOR;
341   *minor = HB_VERSION_MINOR;
342   *micro = HB_VERSION_MICRO;
343 }
344
345 const char *
346 hb_version_string (void)
347 {
348   return HB_VERSION_STRING;
349 }
350
351 hb_bool_t
352 hb_version_check (unsigned int major,
353                   unsigned int minor,
354                   unsigned int micro)
355 {
356   return HB_VERSION_CHECK (major, minor, micro);
357 }
358
359
360 HB_END_DECLS