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-mutex-private.hh"
32 #include "hb-object-private.hh"
33 
34 #include <locale.h>
35 
36 
37 /* hb_options_t */
38 
39 hb_options_union_t _hb_options;
40 
41 void
_hb_options_init(void)42 _hb_options_init (void)
43 {
44   hb_options_union_t u;
45   u.i = 0;
46   u.opts.initialized = 1;
47 
48   char *c = getenv ("HB_OPTIONS");
49   u.opts.uniscribe_bug_compatible = c && strstr (c, "uniscribe-bug-compatible");
50 
51   /* This is idempotent and threadsafe. */
52   _hb_options = u;
53 }
54 
55 
56 /* hb_tag_t */
57 
58 /**
59  * hb_tag_from_string:
60  * @str: (array length=len):
61  * @len:
62  *
63  *
64  *
65  * Return value:
66  *
67  * Since: 1.0
68  **/
69 hb_tag_t
hb_tag_from_string(const char * str,int len)70 hb_tag_from_string (const char *str, int len)
71 {
72   char tag[4];
73   unsigned int i;
74 
75   if (!str || !len || !*str)
76     return HB_TAG_NONE;
77 
78   if (len < 0 || len > 4)
79     len = 4;
80   for (i = 0; i < (unsigned) len && str[i]; i++)
81     tag[i] = str[i];
82   for (; i < 4; i++)
83     tag[i] = ' ';
84 
85   return HB_TAG_CHAR4 (tag);
86 }
87 
88 /**
89  * hb_tag_to_string:
90  * @tag:
91  * @buf: (array fixed-size=4):
92  *
93  *
94  *
95  * Since: 1.0
96  **/
97 void
hb_tag_to_string(hb_tag_t tag,char * buf)98 hb_tag_to_string (hb_tag_t tag, char *buf)
99 {
100   buf[0] = (char) (uint8_t) (tag >> 24);
101   buf[1] = (char) (uint8_t) (tag >> 16);
102   buf[2] = (char) (uint8_t) (tag >>  8);
103   buf[3] = (char) (uint8_t) (tag >>  0);
104 }
105 
106 
107 /* hb_direction_t */
108 
109 const char direction_strings[][4] = {
110   "ltr",
111   "rtl",
112   "ttb",
113   "btt"
114 };
115 
116 /**
117  * hb_direction_from_string:
118  * @str: (array length=len):
119  * @len:
120  *
121  *
122  *
123  * Return value:
124  *
125  * Since: 1.0
126  **/
127 hb_direction_t
hb_direction_from_string(const char * str,int len)128 hb_direction_from_string (const char *str, int len)
129 {
130   if (unlikely (!str || !len || !*str))
131     return HB_DIRECTION_INVALID;
132 
133   /* Lets match loosely: just match the first letter, such that
134    * all of "ltr", "left-to-right", etc work!
135    */
136   char c = TOLOWER (str[0]);
137   for (unsigned int i = 0; i < ARRAY_LENGTH (direction_strings); i++)
138     if (c == direction_strings[i][0])
139       return (hb_direction_t) (HB_DIRECTION_LTR + i);
140 
141   return HB_DIRECTION_INVALID;
142 }
143 
144 /**
145  * hb_direction_to_string:
146  * @direction:
147  *
148  *
149  *
150  * Return value: (transfer none):
151  *
152  * Since: 1.0
153  **/
154 const char *
hb_direction_to_string(hb_direction_t direction)155 hb_direction_to_string (hb_direction_t direction)
156 {
157   if (likely ((unsigned int) (direction - HB_DIRECTION_LTR)
158 	      < ARRAY_LENGTH (direction_strings)))
159     return direction_strings[direction - HB_DIRECTION_LTR];
160 
161   return "invalid";
162 }
163 
164 
165 /* hb_language_t */
166 
167 struct hb_language_impl_t {
168   const char s[1];
169 };
170 
171 static const char canon_map[256] = {
172    0,   0,   0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,   0,   0,
173    0,   0,   0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,   0,   0,
174    0,   0,   0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,  '-',  0,   0,
175   '0', '1', '2', '3', '4', '5', '6', '7',  '8', '9',  0,   0,   0,   0,   0,   0,
176   '-', 'a', 'b', 'c', 'd', 'e', 'f', 'g',  'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
177   'p', 'q', 'r', 's', 't', 'u', 'v', 'w',  'x', 'y', 'z',  0,   0,   0,   0,  '-',
178    0,  'a', 'b', 'c', 'd', 'e', 'f', 'g',  'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
179   'p', 'q', 'r', 's', 't', 'u', 'v', 'w',  'x', 'y', 'z',  0,   0,   0,   0,   0
180 };
181 
182 static hb_bool_t
lang_equal(hb_language_t v1,const void * v2)183 lang_equal (hb_language_t  v1,
184 	    const void    *v2)
185 {
186   const unsigned char *p1 = (const unsigned char *) v1;
187   const unsigned char *p2 = (const unsigned char *) v2;
188 
189   while (*p1 && *p1 == canon_map[*p2])
190     p1++, p2++;
191 
192   return *p1 == canon_map[*p2];
193 }
194 
195 #if 0
196 static unsigned int
197 lang_hash (const void *key)
198 {
199   const unsigned char *p = key;
200   unsigned int h = 0;
201   while (canon_map[*p])
202     {
203       h = (h << 5) - h + canon_map[*p];
204       p++;
205     }
206 
207   return h;
208 }
209 #endif
210 
211 
212 struct hb_language_item_t {
213 
214   struct hb_language_item_t *next;
215   hb_language_t lang;
216 
operator ==hb_language_item_t217   inline bool operator == (const char *s) const {
218     return lang_equal (lang, s);
219   }
220 
operator =hb_language_item_t221   inline hb_language_item_t & operator = (const char *s) {
222     lang = (hb_language_t) strdup (s);
223     for (unsigned char *p = (unsigned char *) lang; *p; p++)
224       *p = canon_map[*p];
225 
226     return *this;
227   }
228 
finishhb_language_item_t229   void finish (void) { free ((void *) lang); }
230 };
231 
232 
233 /* Thread-safe lock-free language list */
234 
235 static hb_language_item_t *langs;
236 
237 #ifdef HB_USE_ATEXIT
238 static
free_langs(void)239 void free_langs (void)
240 {
241   while (langs) {
242     hb_language_item_t *next = langs->next;
243     langs->finish ();
244     free (langs);
245     langs = next;
246   }
247 }
248 #endif
249 
250 static hb_language_item_t *
lang_find_or_insert(const char * key)251 lang_find_or_insert (const char *key)
252 {
253 retry:
254   hb_language_item_t *first_lang = (hb_language_item_t *) hb_atomic_ptr_get (&langs);
255 
256   for (hb_language_item_t *lang = first_lang; lang; lang = lang->next)
257     if (*lang == key)
258       return lang;
259 
260   /* Not found; allocate one. */
261   hb_language_item_t *lang = (hb_language_item_t *) calloc (1, sizeof (hb_language_item_t));
262   if (unlikely (!lang))
263     return NULL;
264   lang->next = first_lang;
265   *lang = key;
266 
267   if (!hb_atomic_ptr_cmpexch (&langs, first_lang, lang)) {
268     free (lang);
269     goto retry;
270   }
271 
272 #ifdef HB_USE_ATEXIT
273   if (!first_lang)
274     atexit (free_langs); /* First person registers atexit() callback. */
275 #endif
276 
277   return lang;
278 }
279 
280 
281 /**
282  * hb_language_from_string:
283  * @str: (array length=len):
284  * @len:
285  *
286  *
287  *
288  * Return value:
289  *
290  * Since: 1.0
291  **/
292 hb_language_t
hb_language_from_string(const char * str,int len)293 hb_language_from_string (const char *str, int len)
294 {
295   char strbuf[64];
296 
297   if (!str || !len || !*str)
298     return HB_LANGUAGE_INVALID;
299 
300   if (len >= 0)
301   {
302     /* NUL-terminate it. */
303     len = MIN (len, (int) sizeof (strbuf) - 1);
304     memcpy (strbuf, str, len);
305     strbuf[len] = '\0';
306     str = strbuf;
307   }
308 
309   hb_language_item_t *item = lang_find_or_insert (str);
310 
311   return likely (item) ? item->lang : HB_LANGUAGE_INVALID;
312 }
313 
314 /**
315  * hb_language_to_string:
316  * @language:
317  *
318  *
319  *
320  * Return value: (transfer none):
321  *
322  * Since: 1.0
323  **/
324 const char *
hb_language_to_string(hb_language_t language)325 hb_language_to_string (hb_language_t language)
326 {
327   /* This is actually NULL-safe! */
328   return language->s;
329 }
330 
331 /**
332  * hb_language_get_default:
333  *
334  *
335  *
336  * Return value:
337  *
338  * Since: 1.0
339  **/
340 hb_language_t
hb_language_get_default(void)341 hb_language_get_default (void)
342 {
343   static hb_language_t default_language = HB_LANGUAGE_INVALID;
344 
345   hb_language_t language = (hb_language_t) hb_atomic_ptr_get (&default_language);
346   if (unlikely (language == HB_LANGUAGE_INVALID)) {
347     language = hb_language_from_string (setlocale (LC_CTYPE, NULL), -1);
348     (void) hb_atomic_ptr_cmpexch (&default_language, HB_LANGUAGE_INVALID, language);
349   }
350 
351   return default_language;
352 }
353 
354 
355 /* hb_script_t */
356 
357 /**
358  * hb_script_from_iso15924_tag:
359  * @tag:
360  *
361  *
362  *
363  * Return value:
364  *
365  * Since: 1.0
366  **/
367 hb_script_t
hb_script_from_iso15924_tag(hb_tag_t tag)368 hb_script_from_iso15924_tag (hb_tag_t tag)
369 {
370   if (unlikely (tag == HB_TAG_NONE))
371     return HB_SCRIPT_INVALID;
372 
373   /* Be lenient, adjust case (one capital letter followed by three small letters) */
374   tag = (tag & 0xDFDFDFDFu) | 0x00202020u;
375 
376   switch (tag) {
377 
378     /* These graduated from the 'Q' private-area codes, but
379      * the old code is still aliased by Unicode, and the Qaai
380      * one in use by ICU. */
381     case HB_TAG('Q','a','a','i'): return HB_SCRIPT_INHERITED;
382     case HB_TAG('Q','a','a','c'): return HB_SCRIPT_COPTIC;
383 
384     /* Script variants from http://unicode.org/iso15924/ */
385     case HB_TAG('C','y','r','s'): return HB_SCRIPT_CYRILLIC;
386     case HB_TAG('L','a','t','f'): return HB_SCRIPT_LATIN;
387     case HB_TAG('L','a','t','g'): return HB_SCRIPT_LATIN;
388     case HB_TAG('S','y','r','e'): return HB_SCRIPT_SYRIAC;
389     case HB_TAG('S','y','r','j'): return HB_SCRIPT_SYRIAC;
390     case HB_TAG('S','y','r','n'): return HB_SCRIPT_SYRIAC;
391   }
392 
393   /* If it looks right, just use the tag as a script */
394   if (((uint32_t) tag & 0xE0E0E0E0u) == 0x40606060u)
395     return (hb_script_t) tag;
396 
397   /* Otherwise, return unknown */
398   return HB_SCRIPT_UNKNOWN;
399 }
400 
401 /**
402  * hb_script_from_string:
403  * @s: (array length=len):
404  * @len:
405  *
406  *
407  *
408  * Return value:
409  *
410  * Since: 1.0
411  **/
412 hb_script_t
hb_script_from_string(const char * s,int len)413 hb_script_from_string (const char *s, int len)
414 {
415   return hb_script_from_iso15924_tag (hb_tag_from_string (s, len));
416 }
417 
418 /**
419  * hb_script_to_iso15924_tag:
420  * @script:
421  *
422  *
423  *
424  * Return value:
425  *
426  * Since: 1.0
427  **/
428 hb_tag_t
hb_script_to_iso15924_tag(hb_script_t script)429 hb_script_to_iso15924_tag (hb_script_t script)
430 {
431   return (hb_tag_t) script;
432 }
433 
434 /**
435  * hb_script_get_horizontal_direction:
436  * @script:
437  *
438  *
439  *
440  * Return value:
441  *
442  * Since: 1.0
443  **/
444 hb_direction_t
hb_script_get_horizontal_direction(hb_script_t script)445 hb_script_get_horizontal_direction (hb_script_t script)
446 {
447   /* http://goo.gl/x9ilM */
448   switch ((hb_tag_t) script)
449   {
450     /* Unicode-1.1 additions */
451     case HB_SCRIPT_ARABIC:
452     case HB_SCRIPT_HEBREW:
453 
454     /* Unicode-3.0 additions */
455     case HB_SCRIPT_SYRIAC:
456     case HB_SCRIPT_THAANA:
457 
458     /* Unicode-4.0 additions */
459     case HB_SCRIPT_CYPRIOT:
460 
461     /* Unicode-4.1 additions */
462     case HB_SCRIPT_KHAROSHTHI:
463 
464     /* Unicode-5.0 additions */
465     case HB_SCRIPT_PHOENICIAN:
466     case HB_SCRIPT_NKO:
467 
468     /* Unicode-5.1 additions */
469     case HB_SCRIPT_LYDIAN:
470 
471     /* Unicode-5.2 additions */
472     case HB_SCRIPT_AVESTAN:
473     case HB_SCRIPT_IMPERIAL_ARAMAIC:
474     case HB_SCRIPT_INSCRIPTIONAL_PAHLAVI:
475     case HB_SCRIPT_INSCRIPTIONAL_PARTHIAN:
476     case HB_SCRIPT_OLD_SOUTH_ARABIAN:
477     case HB_SCRIPT_OLD_TURKIC:
478     case HB_SCRIPT_SAMARITAN:
479 
480     /* Unicode-6.0 additions */
481     case HB_SCRIPT_MANDAIC:
482 
483     /* Unicode-6.1 additions */
484     case HB_SCRIPT_MEROITIC_CURSIVE:
485     case HB_SCRIPT_MEROITIC_HIEROGLYPHS:
486 
487     /* Unicode-7.0 additions */
488     case HB_SCRIPT_MANICHAEAN:
489     case HB_SCRIPT_MENDE_KIKAKUI:
490     case HB_SCRIPT_NABATAEAN:
491     case HB_SCRIPT_OLD_NORTH_ARABIAN:
492     case HB_SCRIPT_PALMYRENE:
493     case HB_SCRIPT_PSALTER_PAHLAVI:
494 
495       return HB_DIRECTION_RTL;
496   }
497 
498   return HB_DIRECTION_LTR;
499 }
500 
501 
502 /* hb_user_data_array_t */
503 
504 bool
set(hb_user_data_key_t * key,void * data,hb_destroy_func_t destroy,hb_bool_t replace)505 hb_user_data_array_t::set (hb_user_data_key_t *key,
506 			   void *              data,
507 			   hb_destroy_func_t   destroy,
508 			   hb_bool_t           replace)
509 {
510   if (!key)
511     return false;
512 
513   if (replace) {
514     if (!data && !destroy) {
515       items.remove (key, lock);
516       return true;
517     }
518   }
519   hb_user_data_item_t item = {key, data, destroy};
520   bool ret = !!items.replace_or_insert (item, lock, replace);
521 
522   return ret;
523 }
524 
525 void *
get(hb_user_data_key_t * key)526 hb_user_data_array_t::get (hb_user_data_key_t *key)
527 {
528   hb_user_data_item_t item = {NULL };
529 
530   return items.find (key, &item, lock) ? item.data : NULL;
531 }
532 
533 
534 /* hb_version */
535 
536 /**
537  * hb_version:
538  * @major: (out): Library major version component.
539  * @minor: (out): Library minor version component.
540  * @micro: (out): Library micro version component.
541  *
542  * Returns library version as three integer components.
543  *
544  * Since: 1.0
545  **/
546 void
hb_version(unsigned int * major,unsigned int * minor,unsigned int * micro)547 hb_version (unsigned int *major,
548 	    unsigned int *minor,
549 	    unsigned int *micro)
550 {
551   *major = HB_VERSION_MAJOR;
552   *minor = HB_VERSION_MINOR;
553   *micro = HB_VERSION_MICRO;
554 }
555 
556 /**
557  * hb_version_string:
558  *
559  * Returns library version as a string with three components.
560  *
561  * Return value: library version string.
562  *
563  * Since: 1.0
564  **/
565 const char *
hb_version_string(void)566 hb_version_string (void)
567 {
568   return HB_VERSION_STRING;
569 }
570 
571 /**
572  * hb_version_atleast:
573  * @major:
574  * @minor:
575  * @micro:
576  *
577  *
578  *
579  * Return value:
580  *
581  * Since: 1.0
582  **/
583 hb_bool_t
hb_version_atleast(unsigned int major,unsigned int minor,unsigned int micro)584 hb_version_atleast (unsigned int major,
585 		    unsigned int minor,
586 		    unsigned int micro)
587 {
588   return HB_VERSION_ATLEAST (major, minor, micro);
589 }
590