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