Finish off previous change
[profile/ivi/org.tizen.video-player.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 NULL;
173
174   hb_language_item_t *item = langs.find_or_insert (str, langs_lock);
175
176   return likely (item) ? item->lang : NULL;
177 }
178
179 const char *
180 hb_language_to_string (hb_language_t language)
181 {
182   return language->s;
183 }
184
185 hb_language_t
186 hb_language_get_default (void)
187 {
188   static hb_language_t default_language;
189
190   if (!default_language) {
191     /* This block is not quite threadsafe, but is not as bad as
192      * it looks since it's idempotent.  As long as pointer ops
193      * are atomic, we are safe. */
194
195     /* I hear that setlocale() doesn't honor env vars on Windows,
196      * but for now we ignore that. */
197
198     default_language = hb_language_from_string (setlocale (LC_CTYPE, NULL));
199   }
200
201   return default_language;
202 }
203
204
205 /* hb_script_t */
206
207 hb_script_t
208 hb_script_from_iso15924_tag (hb_tag_t tag)
209 {
210   if (unlikely (tag == HB_TAG_NONE))
211     return HB_SCRIPT_INVALID;
212
213   /* Be lenient, adjust case (one capital letter followed by three small letters) */
214   tag = (tag & 0xDFDFDFDF) | 0x00202020;
215
216   switch (tag) {
217
218     /* These graduated from the 'Q' private-area codes, but
219      * the old code is still aliased by Unicode, and the Qaai
220      * one in use by ICU. */
221     case HB_TAG('Q','a','a','i'): return HB_SCRIPT_INHERITED;
222     case HB_TAG('Q','a','a','c'): return HB_SCRIPT_COPTIC;
223
224     /* Script variants from http://unicode.org/iso15924/ */
225     case HB_TAG('C','y','r','s'): return HB_SCRIPT_CYRILLIC;
226     case HB_TAG('L','a','t','f'): return HB_SCRIPT_LATIN;
227     case HB_TAG('L','a','t','g'): return HB_SCRIPT_LATIN;
228     case HB_TAG('S','y','r','e'): return HB_SCRIPT_SYRIAC;
229     case HB_TAG('S','y','r','j'): return HB_SCRIPT_SYRIAC;
230     case HB_TAG('S','y','r','n'): return HB_SCRIPT_SYRIAC;
231   }
232
233   /* If it looks right, just use the tag as a script */
234   if (((uint32_t) tag & 0xE0E0E0E0) == 0x40606060)
235     return (hb_script_t) tag;
236
237   /* Otherwise, return unknown */
238   return HB_SCRIPT_UNKNOWN;
239 }
240
241 hb_script_t
242 hb_script_from_string (const char *s)
243 {
244   return hb_script_from_iso15924_tag (hb_tag_from_string (s));
245 }
246
247 hb_tag_t
248 hb_script_to_iso15924_tag (hb_script_t script)
249 {
250   return (hb_tag_t) script;
251 }
252
253 hb_direction_t
254 hb_script_get_horizontal_direction (hb_script_t script)
255 {
256   switch ((hb_tag_t) script)
257   {
258     case HB_SCRIPT_ARABIC:
259     case HB_SCRIPT_HEBREW:
260     case HB_SCRIPT_SYRIAC:
261     case HB_SCRIPT_THAANA:
262
263     /* Unicode-4.0 additions */
264     case HB_SCRIPT_CYPRIOT:
265
266     /* Unicode-5.0 additions */
267     case HB_SCRIPT_PHOENICIAN:
268     case HB_SCRIPT_NKO:
269
270     /* Unicode-5.2 additions */
271     case HB_SCRIPT_AVESTAN:
272     case HB_SCRIPT_IMPERIAL_ARAMAIC:
273     case HB_SCRIPT_INSCRIPTIONAL_PAHLAVI:
274     case HB_SCRIPT_INSCRIPTIONAL_PARTHIAN:
275     case HB_SCRIPT_OLD_SOUTH_ARABIAN:
276     case HB_SCRIPT_OLD_TURKIC:
277     case HB_SCRIPT_SAMARITAN:
278
279     /* Unicode-6.0 additions */
280     case HB_SCRIPT_MANDAIC:
281
282       return HB_DIRECTION_RTL;
283   }
284
285   return HB_DIRECTION_LTR;
286 }
287
288
289 /* hb_user_data_array_t */
290
291
292 /* NOTE: Currently we use a global lock for user_data access
293  * threadsafety.  If one day we add a mutex to any object, we
294  * should switch to using that insted for these too.
295  */
296
297 static hb_static_mutex_t user_data_lock;
298
299 bool
300 hb_user_data_array_t::set (hb_user_data_key_t *key,
301                            void *              data,
302                            hb_destroy_func_t   destroy)
303 {
304   if (!key)
305     return false;
306
307   if (!data && !destroy) {
308     items.remove (key, user_data_lock);
309     return true;
310   }
311   hb_user_data_item_t item = {key, data, destroy};
312   bool ret = !!items.replace_or_insert (item, user_data_lock);
313
314   return ret;
315 }
316
317 void *
318 hb_user_data_array_t::get (hb_user_data_key_t *key)
319 {
320   hb_user_data_item_t item = {NULL };
321
322   return items.find (key, &item, user_data_lock) ? item.data : NULL;
323 }
324
325 void
326 hb_user_data_array_t::finish (void)
327 {
328   items.finish (user_data_lock);
329 }
330
331
332 /* hb_version */
333
334 void
335 hb_version (unsigned int *major,
336             unsigned int *minor,
337             unsigned int *micro)
338 {
339   *major = HB_VERSION_MAJOR;
340   *minor = HB_VERSION_MINOR;
341   *micro = HB_VERSION_MICRO;
342 }
343
344 const char *
345 hb_version_string (void)
346 {
347   return HB_VERSION_STRING;
348 }
349
350 hb_bool_t
351 hb_version_check (unsigned int major,
352                   unsigned int minor,
353                   unsigned int micro)
354 {
355   return HB_VERSION_CHECK (major, minor, micro);
356 }
357
358
359 HB_END_DECLS