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