hb-common.cc revision 7e7d245b332306949a19c628bacd920717434769
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
42hb_tag_t
43hb_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
64const char direction_strings[][4] = {
65  "ltr",
66  "rtl",
67  "ttb",
68  "btt"
69};
70
71hb_direction_t
72hb_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
88const char *
89hb_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
101struct hb_language_impl_t {
102  const char s[1];
103};
104
105static 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
116static hb_bool_t
117lang_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
130static unsigned int
131lang_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
146struct 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
169static hb_language_item_t *langs;
170
171static
172void 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
182static hb_language_item_t *
183lang_find_or_insert (const char *key)
184{
185retry:
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
213hb_language_t
214hb_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
231const char *
232hb_language_to_string (hb_language_t language)
233{
234  /* This is actually NULL-safe! */
235  return language->s;
236}
237
238hb_language_t
239hb_language_get_default (void)
240{
241  static hb_language_t default_language = HB_LANGUAGE_INVALID;
242
243  hb_language_t language = (hb_language_t) hb_atomic_ptr_get (&default_language);
244  if (unlikely (language == HB_LANGUAGE_INVALID)) {
245    language = hb_language_from_string (setlocale (LC_CTYPE, NULL), -1);
246    hb_atomic_ptr_cmpexch (&default_language, HB_LANGUAGE_INVALID, language);
247  }
248
249  return default_language;
250}
251
252
253/* hb_script_t */
254
255hb_script_t
256hb_script_from_iso15924_tag (hb_tag_t tag)
257{
258  if (unlikely (tag == HB_TAG_NONE))
259    return HB_SCRIPT_INVALID;
260
261  /* Be lenient, adjust case (one capital letter followed by three small letters) */
262  tag = (tag & 0xDFDFDFDF) | 0x00202020;
263
264  switch (tag) {
265
266    /* These graduated from the 'Q' private-area codes, but
267     * the old code is still aliased by Unicode, and the Qaai
268     * one in use by ICU. */
269    case HB_TAG('Q','a','a','i'): return HB_SCRIPT_INHERITED;
270    case HB_TAG('Q','a','a','c'): return HB_SCRIPT_COPTIC;
271
272    /* Script variants from http://unicode.org/iso15924/ */
273    case HB_TAG('C','y','r','s'): return HB_SCRIPT_CYRILLIC;
274    case HB_TAG('L','a','t','f'): return HB_SCRIPT_LATIN;
275    case HB_TAG('L','a','t','g'): return HB_SCRIPT_LATIN;
276    case HB_TAG('S','y','r','e'): return HB_SCRIPT_SYRIAC;
277    case HB_TAG('S','y','r','j'): return HB_SCRIPT_SYRIAC;
278    case HB_TAG('S','y','r','n'): return HB_SCRIPT_SYRIAC;
279  }
280
281  /* If it looks right, just use the tag as a script */
282  if (((uint32_t) tag & 0xE0E0E0E0) == 0x40606060)
283    return (hb_script_t) tag;
284
285  /* Otherwise, return unknown */
286  return HB_SCRIPT_UNKNOWN;
287}
288
289hb_script_t
290hb_script_from_string (const char *s, int len)
291{
292  return hb_script_from_iso15924_tag (hb_tag_from_string (s, len));
293}
294
295hb_tag_t
296hb_script_to_iso15924_tag (hb_script_t script)
297{
298  return (hb_tag_t) script;
299}
300
301hb_direction_t
302hb_script_get_horizontal_direction (hb_script_t script)
303{
304  /* http://goo.gl/x9ilM */
305  switch ((hb_tag_t) script)
306  {
307    /* Unicode-1.1 additions */
308    case HB_SCRIPT_ARABIC:
309    case HB_SCRIPT_HEBREW:
310
311    /* Unicode-3.0 additions */
312    case HB_SCRIPT_SYRIAC:
313    case HB_SCRIPT_THAANA:
314
315    /* Unicode-4.0 additions */
316    case HB_SCRIPT_CYPRIOT:
317
318    /* Unicode-4.1 additions */
319    case HB_SCRIPT_KHAROSHTHI:
320
321    /* Unicode-5.0 additions */
322    case HB_SCRIPT_PHOENICIAN:
323    case HB_SCRIPT_NKO:
324
325    /* Unicode-5.1 additions */
326    case HB_SCRIPT_LYDIAN:
327
328    /* Unicode-5.2 additions */
329    case HB_SCRIPT_AVESTAN:
330    case HB_SCRIPT_IMPERIAL_ARAMAIC:
331    case HB_SCRIPT_INSCRIPTIONAL_PAHLAVI:
332    case HB_SCRIPT_INSCRIPTIONAL_PARTHIAN:
333    case HB_SCRIPT_OLD_SOUTH_ARABIAN:
334    case HB_SCRIPT_OLD_TURKIC:
335    case HB_SCRIPT_SAMARITAN:
336
337    /* Unicode-6.0 additions */
338    case HB_SCRIPT_MANDAIC:
339
340    /* Unicode-6.1 additions */
341    case HB_SCRIPT_MEROITIC_CURSIVE:
342    case HB_SCRIPT_MEROITIC_HIEROGLYPHS:
343
344      return HB_DIRECTION_RTL;
345  }
346
347  return HB_DIRECTION_LTR;
348}
349
350
351/* hb_user_data_array_t */
352
353bool
354hb_user_data_array_t::set (hb_user_data_key_t *key,
355			   void *              data,
356			   hb_destroy_func_t   destroy,
357			   hb_bool_t           replace,
358			   hb_mutex_t         &lock)
359{
360  if (!key)
361    return false;
362
363  if (replace) {
364    if (!data && !destroy) {
365      items.remove (key, lock);
366      return true;
367    }
368  }
369  hb_user_data_item_t item = {key, data, destroy};
370  bool ret = !!items.replace_or_insert (item, lock, replace);
371
372  return ret;
373}
374
375void *
376hb_user_data_array_t::get (hb_user_data_key_t *key,
377			   hb_mutex_t         &lock)
378{
379  hb_user_data_item_t item = {NULL };
380
381  return items.find (key, &item, lock) ? item.data : NULL;
382}
383
384void
385hb_user_data_array_t::finish (hb_mutex_t &lock)
386{
387  items.finish (lock);
388}
389
390
391/* hb_version */
392
393void
394hb_version (unsigned int *major,
395	    unsigned int *minor,
396	    unsigned int *micro)
397{
398  *major = HB_VERSION_MAJOR;
399  *minor = HB_VERSION_MINOR;
400  *micro = HB_VERSION_MICRO;
401}
402
403const char *
404hb_version_string (void)
405{
406  return HB_VERSION_STRING;
407}
408
409hb_bool_t
410hb_version_check (unsigned int major,
411		  unsigned int minor,
412		  unsigned int micro)
413{
414  return HB_VERSION_CHECK (major, minor, micro);
415}
416
417
418