39006237a0c8fa164e792cdefcb3d0c4925a775f
[platform/upstream/harfbuzz.git] / src / hb-common.cc
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
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) (element-type uint8_t): 
61  * @len: 
62  *
63  * 
64  *
65  * Return value: 
66  *
67  * Since: 0.9.2
68  **/
69 hb_tag_t
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: (out caller-allocates) (array fixed-size=4) (element-type uint8_t): 
92  *
93  * 
94  *
95  * Since: 0.9.5
96  **/
97 void
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) (element-type uint8_t): 
119  * @len: 
120  *
121  * 
122  *
123  * Return value: 
124  *
125  * Since: 0.9.2
126  **/
127 hb_direction_t
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: 0.9.2
153  **/
154 const char *
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 bool
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++;
191     p2++;
192   }
193
194   return *p1 == canon_map[*p2];
195 }
196
197 #if 0
198 static unsigned int
199 lang_hash (const void *key)
200 {
201   const unsigned char *p = key;
202   unsigned int h = 0;
203   while (canon_map[*p])
204     {
205       h = (h << 5) - h + canon_map[*p];
206       p++;
207     }
208
209   return h;
210 }
211 #endif
212
213
214 struct hb_language_item_t {
215
216   struct hb_language_item_t *next;
217   hb_language_t lang;
218
219   inline bool operator == (const char *s) const {
220     return lang_equal (lang, s);
221   }
222
223   inline hb_language_item_t & operator = (const char *s) {
224     lang = (hb_language_t) strdup (s);
225     for (unsigned char *p = (unsigned char *) lang; *p; p++)
226       *p = canon_map[*p];
227
228     return *this;
229   }
230
231   void finish (void) { free ((void *) lang); }
232 };
233
234
235 /* Thread-safe lock-free language list */
236
237 static hb_language_item_t *langs;
238
239 #ifdef HB_USE_ATEXIT
240 static
241 void free_langs (void)
242 {
243   while (langs) {
244     hb_language_item_t *next = langs->next;
245     langs->finish ();
246     free (langs);
247     langs = next;
248   }
249 }
250 #endif
251
252 static hb_language_item_t *
253 lang_find_or_insert (const char *key)
254 {
255 retry:
256   hb_language_item_t *first_lang = (hb_language_item_t *) hb_atomic_ptr_get (&langs);
257
258   for (hb_language_item_t *lang = first_lang; lang; lang = lang->next)
259     if (*lang == key)
260       return lang;
261
262   /* Not found; allocate one. */
263   hb_language_item_t *lang = (hb_language_item_t *) calloc (1, sizeof (hb_language_item_t));
264   if (unlikely (!lang))
265     return NULL;
266   lang->next = first_lang;
267   *lang = key;
268
269   if (!hb_atomic_ptr_cmpexch (&langs, first_lang, lang)) {
270     lang->finish ();
271     free (lang);
272     goto retry;
273   }
274
275 #ifdef HB_USE_ATEXIT
276   if (!first_lang)
277     atexit (free_langs); /* First person registers atexit() callback. */
278 #endif
279
280   return lang;
281 }
282
283
284 /**
285  * hb_language_from_string:
286  * @str: (array length=len) (element-type uint8_t): a string representing
287  *       ISO 639 language code
288  * @len: length of the @str, or -1 if it is %NULL-terminated.
289  *
290  * Converts @str representing an ISO 639 language code to the corresponding
291  * #hb_language_t.
292  *
293  * Return value: (transfer none):
294  * The #hb_language_t corresponding to the ISO 639 language code.
295  *
296  * Since: 0.9.2
297  **/
298 hb_language_t
299 hb_language_from_string (const char *str, int len)
300 {
301   if (!str || !len || !*str)
302     return HB_LANGUAGE_INVALID;
303
304   hb_language_item_t *item = NULL;
305   if (len >= 0)
306   {
307     /* NUL-terminate it. */
308     char strbuf[64];
309     len = MIN (len, (int) sizeof (strbuf) - 1);
310     memcpy (strbuf, str, len);
311     strbuf[len] = '\0';
312     item = lang_find_or_insert (strbuf);
313   }
314   else
315     item = lang_find_or_insert (str);
316
317   return likely (item) ? item->lang : HB_LANGUAGE_INVALID;
318 }
319
320 /**
321  * hb_language_to_string:
322  * @language: an #hb_language_t to convert.
323  *
324  * See hb_language_from_string().
325  *
326  * Return value: (transfer none):
327  * A %NULL-terminated string representing the @language. Must not be freed by
328  * the caller.
329  *
330  * Since: 0.9.2
331  **/
332 const char *
333 hb_language_to_string (hb_language_t language)
334 {
335   /* This is actually NULL-safe! */
336   return language->s;
337 }
338
339 /**
340  * hb_language_get_default:
341  *
342  * 
343  *
344  * Return value: (transfer none):
345  *
346  * Since: 0.9.2
347  **/
348 hb_language_t
349 hb_language_get_default (void)
350 {
351   static hb_language_t default_language = HB_LANGUAGE_INVALID;
352
353   hb_language_t language = (hb_language_t) hb_atomic_ptr_get (&default_language);
354   if (unlikely (language == HB_LANGUAGE_INVALID)) {
355     language = hb_language_from_string (setlocale (LC_CTYPE, NULL), -1);
356     (void) hb_atomic_ptr_cmpexch (&default_language, HB_LANGUAGE_INVALID, language);
357   }
358
359   return default_language;
360 }
361
362
363 /* hb_script_t */
364
365 /**
366  * hb_script_from_iso15924_tag:
367  * @tag: an #hb_tag_t representing an ISO 15924 tag.
368  *
369  * Converts an ISO 15924 script tag to a corresponding #hb_script_t.
370  *
371  * Return value: 
372  * An #hb_script_t corresponding to the ISO 15924 tag.
373  *
374  * Since: 0.9.2
375  **/
376 hb_script_t
377 hb_script_from_iso15924_tag (hb_tag_t tag)
378 {
379   if (unlikely (tag == HB_TAG_NONE))
380     return HB_SCRIPT_INVALID;
381
382   /* Be lenient, adjust case (one capital letter followed by three small letters) */
383   tag = (tag & 0xDFDFDFDFu) | 0x00202020u;
384
385   switch (tag) {
386
387     /* These graduated from the 'Q' private-area codes, but
388      * the old code is still aliased by Unicode, and the Qaai
389      * one in use by ICU. */
390     case HB_TAG('Q','a','a','i'): return HB_SCRIPT_INHERITED;
391     case HB_TAG('Q','a','a','c'): return HB_SCRIPT_COPTIC;
392
393     /* Script variants from http://unicode.org/iso15924/ */
394     case HB_TAG('C','y','r','s'): return HB_SCRIPT_CYRILLIC;
395     case HB_TAG('L','a','t','f'): return HB_SCRIPT_LATIN;
396     case HB_TAG('L','a','t','g'): return HB_SCRIPT_LATIN;
397     case HB_TAG('S','y','r','e'): return HB_SCRIPT_SYRIAC;
398     case HB_TAG('S','y','r','j'): return HB_SCRIPT_SYRIAC;
399     case HB_TAG('S','y','r','n'): return HB_SCRIPT_SYRIAC;
400   }
401
402   /* If it looks right, just use the tag as a script */
403   if (((uint32_t) tag & 0xE0E0E0E0u) == 0x40606060u)
404     return (hb_script_t) tag;
405
406   /* Otherwise, return unknown */
407   return HB_SCRIPT_UNKNOWN;
408 }
409
410 /**
411  * hb_script_from_string:
412  * @str: (array length=len) (element-type uint8_t): a string representing an
413  *       ISO 15924 tag.
414  * @len: length of the @str, or -1 if it is %NULL-terminated.
415  *
416  * Converts a string @str representing an ISO 15924 script tag to a
417  * corresponding #hb_script_t. Shorthand for hb_tag_from_string() then
418  * hb_script_from_iso15924_tag().
419  *
420  * Return value: 
421  * An #hb_script_t corresponding to the ISO 15924 tag.
422  *
423  * Since: 0.9.2
424  **/
425 hb_script_t
426 hb_script_from_string (const char *str, int len)
427 {
428   return hb_script_from_iso15924_tag (hb_tag_from_string (str, len));
429 }
430
431 /**
432  * hb_script_to_iso15924_tag:
433  * @script: an #hb_script_ to convert.
434  *
435  * See hb_script_from_iso15924_tag().
436  *
437  * Return value:
438  * An #hb_tag_t representing an ISO 15924 script tag.
439  *
440  * Since: 0.9.2
441  **/
442 hb_tag_t
443 hb_script_to_iso15924_tag (hb_script_t script)
444 {
445   return (hb_tag_t) script;
446 }
447
448 /**
449  * hb_script_get_horizontal_direction:
450  * @script: 
451  *
452  * 
453  *
454  * Return value: 
455  *
456  * Since: 0.9.2
457  **/
458 hb_direction_t
459 hb_script_get_horizontal_direction (hb_script_t script)
460 {
461   /* http://goo.gl/x9ilM */
462   switch ((hb_tag_t) script)
463   {
464     /* Unicode-1.1 additions */
465     case HB_SCRIPT_ARABIC:
466     case HB_SCRIPT_HEBREW:
467
468     /* Unicode-3.0 additions */
469     case HB_SCRIPT_SYRIAC:
470     case HB_SCRIPT_THAANA:
471
472     /* Unicode-4.0 additions */
473     case HB_SCRIPT_CYPRIOT:
474
475     /* Unicode-4.1 additions */
476     case HB_SCRIPT_KHAROSHTHI:
477
478     /* Unicode-5.0 additions */
479     case HB_SCRIPT_PHOENICIAN:
480     case HB_SCRIPT_NKO:
481
482     /* Unicode-5.1 additions */
483     case HB_SCRIPT_LYDIAN:
484
485     /* Unicode-5.2 additions */
486     case HB_SCRIPT_AVESTAN:
487     case HB_SCRIPT_IMPERIAL_ARAMAIC:
488     case HB_SCRIPT_INSCRIPTIONAL_PAHLAVI:
489     case HB_SCRIPT_INSCRIPTIONAL_PARTHIAN:
490     case HB_SCRIPT_OLD_SOUTH_ARABIAN:
491     case HB_SCRIPT_OLD_TURKIC:
492     case HB_SCRIPT_SAMARITAN:
493
494     /* Unicode-6.0 additions */
495     case HB_SCRIPT_MANDAIC:
496
497     /* Unicode-6.1 additions */
498     case HB_SCRIPT_MEROITIC_CURSIVE:
499     case HB_SCRIPT_MEROITIC_HIEROGLYPHS:
500
501     /* Unicode-7.0 additions */
502     case HB_SCRIPT_MANICHAEAN:
503     case HB_SCRIPT_MENDE_KIKAKUI:
504     case HB_SCRIPT_NABATAEAN:
505     case HB_SCRIPT_OLD_NORTH_ARABIAN:
506     case HB_SCRIPT_PALMYRENE:
507     case HB_SCRIPT_PSALTER_PAHLAVI:
508
509     /* Unicode-8.0 additions */
510     case HB_SCRIPT_OLD_HUNGARIAN:
511
512     /* Unicode-9.0 additions */
513     case HB_SCRIPT_ADLAM:
514
515       return HB_DIRECTION_RTL;
516   }
517
518   return HB_DIRECTION_LTR;
519 }
520
521
522 /* hb_user_data_array_t */
523
524 bool
525 hb_user_data_array_t::set (hb_user_data_key_t *key,
526                            void *              data,
527                            hb_destroy_func_t   destroy,
528                            hb_bool_t           replace)
529 {
530   if (!key)
531     return false;
532
533   if (replace) {
534     if (!data && !destroy) {
535       items.remove (key, lock);
536       return true;
537     }
538   }
539   hb_user_data_item_t item = {key, data, destroy};
540   bool ret = !!items.replace_or_insert (item, lock, (bool) replace);
541
542   return ret;
543 }
544
545 void *
546 hb_user_data_array_t::get (hb_user_data_key_t *key)
547 {
548   hb_user_data_item_t item = {NULL, NULL, NULL};
549
550   return items.find (key, &item, lock) ? item.data : NULL;
551 }
552
553
554 /* hb_version */
555
556 /**
557  * hb_version:
558  * @major: (out): Library major version component.
559  * @minor: (out): Library minor version component.
560  * @micro: (out): Library micro version component.
561  *
562  * Returns library version as three integer components.
563  *
564  * Since: 0.9.2
565  **/
566 void
567 hb_version (unsigned int *major,
568             unsigned int *minor,
569             unsigned int *micro)
570 {
571   *major = HB_VERSION_MAJOR;
572   *minor = HB_VERSION_MINOR;
573   *micro = HB_VERSION_MICRO;
574 }
575
576 /**
577  * hb_version_string:
578  *
579  * Returns library version as a string with three components.
580  *
581  * Return value: library version string.
582  *
583  * Since: 0.9.2
584  **/
585 const char *
586 hb_version_string (void)
587 {
588   return HB_VERSION_STRING;
589 }
590
591 /**
592  * hb_version_atleast:
593  * @major: 
594  * @minor: 
595  * @micro: 
596  *
597  * 
598  *
599  * Return value: 
600  *
601  * Since: 0.9.30
602  **/
603 hb_bool_t
604 hb_version_atleast (unsigned int major,
605                     unsigned int minor,
606                     unsigned int micro)
607 {
608   return HB_VERSION_ATLEAST (major, minor, micro);
609 }
610
611
612
613 /* hb_feature_t and hb_variation_t */
614
615 static bool
616 parse_space (const char **pp, const char *end)
617 {
618   while (*pp < end && ISSPACE (**pp))
619     (*pp)++;
620   return true;
621 }
622
623 static bool
624 parse_char (const char **pp, const char *end, char c)
625 {
626   parse_space (pp, end);
627
628   if (*pp == end || **pp != c)
629     return false;
630
631   (*pp)++;
632   return true;
633 }
634
635 static bool
636 parse_uint (const char **pp, const char *end, unsigned int *pv)
637 {
638   char buf[32];
639   unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp));
640   strncpy (buf, *pp, len);
641   buf[len] = '\0';
642
643   char *p = buf;
644   char *pend = p;
645   unsigned int v;
646
647   /* Intentionally use strtol instead of strtoul, such that
648    * -1 turns into "big number"... */
649   errno = 0;
650   v = strtol (p, &pend, 0);
651   if (errno || p == pend)
652     return false;
653
654   *pv = v;
655   *pp += pend - p;
656   return true;
657 }
658
659 static bool
660 parse_float (const char **pp, const char *end, float *pv)
661 {
662   char buf[32];
663   unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp));
664   strncpy (buf, *pp, len);
665   buf[len] = '\0';
666
667   char *p = buf;
668   char *pend = p;
669   float v;
670
671   errno = 0;
672   v = strtod (p, &pend);
673   if (errno || p == pend)
674     return false;
675
676   *pv = v;
677   *pp += pend - p;
678   return true;
679 }
680
681 static bool
682 parse_bool (const char **pp, const char *end, unsigned int *pv)
683 {
684   parse_space (pp, end);
685
686   const char *p = *pp;
687   while (*pp < end && ISALPHA(**pp))
688     (*pp)++;
689
690   /* CSS allows on/off as aliases 1/0. */
691   if (*pp - p == 2 || 0 == strncmp (p, "on", 2))
692     *pv = 1;
693   else if (*pp - p == 3 || 0 == strncmp (p, "off", 2))
694     *pv = 0;
695   else
696     return false;
697
698   return true;
699 }
700
701 /* hb_feature_t */
702
703 static bool
704 parse_feature_value_prefix (const char **pp, const char *end, hb_feature_t *feature)
705 {
706   if (parse_char (pp, end, '-'))
707     feature->value = 0;
708   else {
709     parse_char (pp, end, '+');
710     feature->value = 1;
711   }
712
713   return true;
714 }
715
716 static bool
717 parse_tag (const char **pp, const char *end, hb_tag_t *tag)
718 {
719   parse_space (pp, end);
720
721   char quote = 0;
722
723   if (*pp < end && (**pp == '\'' || **pp == '"'))
724   {
725     quote = **pp;
726     (*pp)++;
727   }
728
729   const char *p = *pp;
730   while (*pp < end && ISALNUM(**pp))
731     (*pp)++;
732
733   if (p == *pp || *pp - p > 4)
734     return false;
735
736   *tag = hb_tag_from_string (p, *pp - p);
737
738   if (quote)
739   {
740     /* CSS expects exactly four bytes.  And we only allow quotations for
741      * CSS compatibility.  So, enforce the length. */
742      if (*pp - p != 4)
743        return false;
744     if (*pp == end || **pp != quote)
745       return false;
746     (*pp)++;
747   }
748
749   return true;
750 }
751
752 static bool
753 parse_feature_indices (const char **pp, const char *end, hb_feature_t *feature)
754 {
755   parse_space (pp, end);
756
757   bool has_start;
758
759   feature->start = 0;
760   feature->end = (unsigned int) -1;
761
762   if (!parse_char (pp, end, '['))
763     return true;
764
765   has_start = parse_uint (pp, end, &feature->start);
766
767   if (parse_char (pp, end, ':')) {
768     parse_uint (pp, end, &feature->end);
769   } else {
770     if (has_start)
771       feature->end = feature->start + 1;
772   }
773
774   return parse_char (pp, end, ']');
775 }
776
777 static bool
778 parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *feature)
779 {
780   bool had_equal = parse_char (pp, end, '=');
781   bool had_value = parse_uint (pp, end, &feature->value) ||
782                    parse_bool (pp, end, &feature->value);
783   /* CSS doesn't use equal-sign between tag and value.
784    * If there was an equal-sign, then there *must* be a value.
785    * A value without an eqaul-sign is ok, but not required. */
786   return !had_equal || had_value;
787 }
788
789 static bool
790 parse_one_feature (const char **pp, const char *end, hb_feature_t *feature)
791 {
792   return parse_feature_value_prefix (pp, end, feature) &&
793          parse_tag (pp, end, &feature->tag) &&
794          parse_feature_indices (pp, end, feature) &&
795          parse_feature_value_postfix (pp, end, feature) &&
796          parse_space (pp, end) &&
797          *pp == end;
798 }
799
800 /**
801  * hb_feature_from_string:
802  * @str: (array length=len) (element-type uint8_t): a string to parse
803  * @len: length of @str, or -1 if string is %NULL terminated
804  * @feature: (out): the #hb_feature_t to initialize with the parsed values
805  *
806  * Parses a string into a #hb_feature_t.
807  *
808  * TODO: document the syntax here.
809  *
810  * Return value:
811  * %true if @str is successfully parsed, %false otherwise.
812  *
813  * Since: 0.9.5
814  **/
815 hb_bool_t
816 hb_feature_from_string (const char *str, int len,
817                         hb_feature_t *feature)
818 {
819   hb_feature_t feat;
820
821   if (len < 0)
822     len = strlen (str);
823
824   if (likely (parse_one_feature (&str, str + len, &feat)))
825   {
826     if (feature)
827       *feature = feat;
828     return true;
829   }
830
831   if (feature)
832     memset (feature, 0, sizeof (*feature));
833   return false;
834 }
835
836 /**
837  * hb_feature_to_string:
838  * @feature: an #hb_feature_t to convert
839  * @buf: (array length=size) (out): output string
840  * @size: the allocated size of @buf
841  *
842  * Converts a #hb_feature_t into a %NULL-terminated string in the format
843  * understood by hb_feature_from_string(). The client in responsible for
844  * allocating big enough size for @buf, 128 bytes is more than enough.
845  *
846  * Since: 0.9.5
847  **/
848 void
849 hb_feature_to_string (hb_feature_t *feature,
850                       char *buf, unsigned int size)
851 {
852   if (unlikely (!size)) return;
853
854   char s[128];
855   unsigned int len = 0;
856   if (feature->value == 0)
857     s[len++] = '-';
858   hb_tag_to_string (feature->tag, s + len);
859   len += 4;
860   while (len && s[len - 1] == ' ')
861     len--;
862   if (feature->start != 0 || feature->end != (unsigned int) -1)
863   {
864     s[len++] = '[';
865     if (feature->start)
866       len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->start));
867     if (feature->end != feature->start + 1) {
868       s[len++] = ':';
869       if (feature->end != (unsigned int) -1)
870         len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->end));
871     }
872     s[len++] = ']';
873   }
874   if (feature->value > 1)
875   {
876     s[len++] = '=';
877     len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value));
878   }
879   assert (len < ARRAY_LENGTH (s));
880   len = MIN (len, size - 1);
881   memcpy (buf, s, len);
882   buf[len] = '\0';
883 }
884
885 /* hb_variation_t */
886
887 static bool
888 parse_variation_value (const char **pp, const char *end, hb_variation_t *variation)
889 {
890   parse_char (pp, end, '='); /* Optional. */
891   return parse_float (pp, end, &variation->value);
892 }
893
894 static bool
895 parse_one_variation (const char **pp, const char *end, hb_variation_t *variation)
896 {
897   return parse_tag (pp, end, &variation->tag) &&
898          parse_variation_value (pp, end, variation) &&
899          parse_space (pp, end) &&
900          *pp == end;
901 }
902
903 /**
904  * hb_variation_from_string:
905  *
906  * Since: 1.4.2
907  */
908 hb_bool_t
909 hb_variation_from_string (const char *str, int len,
910                           hb_variation_t *variation)
911 {
912   hb_variation_t var;
913
914   if (len < 0)
915     len = strlen (str);
916
917   if (likely (parse_one_variation (&str, str + len, &var)))
918   {
919     if (variation)
920       *variation = var;
921     return true;
922   }
923
924   if (variation)
925     memset (variation, 0, sizeof (*variation));
926   return false;
927 }
928
929 /**
930  * hb_variation_to_string:
931  *
932  * Since: 1.4.2
933  */
934 void
935 hb_variation_to_string (hb_variation_t *variation,
936                         char *buf, unsigned int size)
937 {
938   if (unlikely (!size)) return;
939
940   char s[128];
941   unsigned int len = 0;
942   hb_tag_to_string (variation->tag, s + len);
943   len += 4;
944   while (len && s[len - 1] == ' ')
945     len--;
946   s[len++] = '=';
947   len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", variation->value));
948
949   assert (len < ARRAY_LENGTH (s));
950   len = MIN (len, size - 1);
951   memcpy (buf, s, len);
952   buf[len] = '\0';
953 }