LCOV - code coverage report
Current view: top level - gfx/harfbuzz/src - hb-common.cc (source / functions) Hit Total Coverage
Test: output.info Lines: 3 292 1.0 %
Date: 2017-07-14 16:53:18 Functions: 1 38 2.6 %
Legend: Lines: hit not hit

          Line data    Source code
       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           0 : _hb_options_init (void)
      43             : {
      44             :   hb_options_union_t u;
      45           0 :   u.i = 0;
      46           0 :   u.opts.initialized = 1;
      47             : 
      48           0 :   char *c = getenv ("HB_OPTIONS");
      49           0 :   u.opts.uniscribe_bug_compatible = c && strstr (c, "uniscribe-bug-compatible");
      50             : 
      51             :   /* This is idempotent and threadsafe. */
      52           0 :   _hb_options = u;
      53           0 : }
      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           0 : hb_tag_from_string (const char *str, int len)
      71             : {
      72             :   char tag[4];
      73             :   unsigned int i;
      74             : 
      75           0 :   if (!str || !len || !*str)
      76           0 :     return HB_TAG_NONE;
      77             : 
      78           0 :   if (len < 0 || len > 4)
      79           0 :     len = 4;
      80           0 :   for (i = 0; i < (unsigned) len && str[i]; i++)
      81           0 :     tag[i] = str[i];
      82           0 :   for (; i < 4; i++)
      83           0 :     tag[i] = ' ';
      84             : 
      85           0 :   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           0 : hb_tag_to_string (hb_tag_t tag, char *buf)
      99             : {
     100           0 :   buf[0] = (char) (uint8_t) (tag >> 24);
     101           0 :   buf[1] = (char) (uint8_t) (tag >> 16);
     102           0 :   buf[2] = (char) (uint8_t) (tag >>  8);
     103           0 :   buf[3] = (char) (uint8_t) (tag >>  0);
     104           0 : }
     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           0 : hb_direction_from_string (const char *str, int len)
     129             : {
     130           0 :   if (unlikely (!str || !len || !*str))
     131           0 :     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           0 :   char c = TOLOWER (str[0]);
     137           0 :   for (unsigned int i = 0; i < ARRAY_LENGTH (direction_strings); i++)
     138           0 :     if (c == direction_strings[i][0])
     139           0 :       return (hb_direction_t) (HB_DIRECTION_LTR + i);
     140             : 
     141           0 :   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           0 : hb_direction_to_string (hb_direction_t direction)
     156             : {
     157           0 :   if (likely ((unsigned int) (direction - HB_DIRECTION_LTR)
     158             :               < ARRAY_LENGTH (direction_strings)))
     159           0 :     return direction_strings[direction - HB_DIRECTION_LTR];
     160             : 
     161           0 :   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           0 : lang_equal (hb_language_t  v1,
     184             :             const void    *v2)
     185             : {
     186           0 :   const unsigned char *p1 = (const unsigned char *) v1;
     187           0 :   const unsigned char *p2 = (const unsigned char *) v2;
     188             : 
     189           0 :   while (*p1 && *p1 == canon_map[*p2]) {
     190           0 :     p1++;
     191           0 :     p2++;
     192             :   }
     193             : 
     194           0 :   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           0 :   inline bool operator == (const char *s) const {
     220           0 :     return lang_equal (lang, s);
     221             :   }
     222             : 
     223           0 :   inline hb_language_item_t & operator = (const char *s) {
     224           0 :     lang = (hb_language_t) strdup (s);
     225           0 :     for (unsigned char *p = (unsigned char *) lang; *p; p++)
     226           0 :       *p = canon_map[*p];
     227             : 
     228           0 :     return *this;
     229             :   }
     230             : 
     231           0 :   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           0 : lang_find_or_insert (const char *key)
     254             : {
     255             : retry:
     256           0 :   hb_language_item_t *first_lang = (hb_language_item_t *) hb_atomic_ptr_get (&langs);
     257             : 
     258           0 :   for (hb_language_item_t *lang = first_lang; lang; lang = lang->next)
     259           0 :     if (*lang == key)
     260           0 :       return lang;
     261             : 
     262             :   /* Not found; allocate one. */
     263           0 :   hb_language_item_t *lang = (hb_language_item_t *) calloc (1, sizeof (hb_language_item_t));
     264           0 :   if (unlikely (!lang))
     265           0 :     return NULL;
     266           0 :   lang->next = first_lang;
     267           0 :   *lang = key;
     268             : 
     269           0 :   if (!hb_atomic_ptr_cmpexch (&langs, first_lang, lang)) {
     270           0 :     lang->finish ();
     271           0 :     free (lang);
     272           0 :     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           0 :   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           0 : hb_language_from_string (const char *str, int len)
     300             : {
     301           0 :   if (!str || !len || !*str)
     302           0 :     return HB_LANGUAGE_INVALID;
     303             : 
     304           0 :   hb_language_item_t *item = NULL;
     305           0 :   if (len >= 0)
     306             :   {
     307             :     /* NUL-terminate it. */
     308             :     char strbuf[64];
     309           0 :     len = MIN (len, (int) sizeof (strbuf) - 1);
     310           0 :     memcpy (strbuf, str, len);
     311           0 :     strbuf[len] = '\0';
     312           0 :     item = lang_find_or_insert (strbuf);
     313             :   }
     314             :   else
     315           0 :     item = lang_find_or_insert (str);
     316             : 
     317           0 :   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           0 : hb_language_to_string (hb_language_t language)
     334             : {
     335             :   /* This is actually NULL-safe! */
     336           0 :   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           0 : hb_language_get_default (void)
     350             : {
     351             :   static hb_language_t default_language = HB_LANGUAGE_INVALID;
     352             : 
     353           0 :   hb_language_t language = (hb_language_t) hb_atomic_ptr_get (&default_language);
     354           0 :   if (unlikely (language == HB_LANGUAGE_INVALID)) {
     355           0 :     language = hb_language_from_string (setlocale (LC_CTYPE, NULL), -1);
     356           0 :     (void) hb_atomic_ptr_cmpexch (&default_language, HB_LANGUAGE_INVALID, language);
     357             :   }
     358             : 
     359           0 :   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           0 : hb_script_from_iso15924_tag (hb_tag_t tag)
     378             : {
     379           0 :   if (unlikely (tag == HB_TAG_NONE))
     380           0 :     return HB_SCRIPT_INVALID;
     381             : 
     382             :   /* Be lenient, adjust case (one capital letter followed by three small letters) */
     383           0 :   tag = (tag & 0xDFDFDFDFu) | 0x00202020u;
     384             : 
     385           0 :   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           0 :     case HB_TAG('Q','a','a','i'): return HB_SCRIPT_INHERITED;
     391           0 :     case HB_TAG('Q','a','a','c'): return HB_SCRIPT_COPTIC;
     392             : 
     393             :     /* Script variants from http://unicode.org/iso15924/ */
     394           0 :     case HB_TAG('C','y','r','s'): return HB_SCRIPT_CYRILLIC;
     395           0 :     case HB_TAG('L','a','t','f'): return HB_SCRIPT_LATIN;
     396           0 :     case HB_TAG('L','a','t','g'): return HB_SCRIPT_LATIN;
     397           0 :     case HB_TAG('S','y','r','e'): return HB_SCRIPT_SYRIAC;
     398           0 :     case HB_TAG('S','y','r','j'): return HB_SCRIPT_SYRIAC;
     399           0 :     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           0 :   if (((uint32_t) tag & 0xE0E0E0E0u) == 0x40606060u)
     404           0 :     return (hb_script_t) tag;
     405             : 
     406             :   /* Otherwise, return unknown */
     407           0 :   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           0 : hb_script_from_string (const char *str, int len)
     427             : {
     428           0 :   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           0 : hb_script_to_iso15924_tag (hb_script_t script)
     444             : {
     445           0 :   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          34 : hb_script_get_horizontal_direction (hb_script_t script)
     460             : {
     461             :   /* http://goo.gl/x9ilM */
     462          34 :   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           0 :       return HB_DIRECTION_RTL;
     516             :   }
     517             : 
     518          34 :   return HB_DIRECTION_LTR;
     519             : }
     520             : 
     521             : 
     522             : /* hb_user_data_array_t */
     523             : 
     524             : bool
     525           0 : 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           0 :   if (!key)
     531           0 :     return false;
     532             : 
     533           0 :   if (replace) {
     534           0 :     if (!data && !destroy) {
     535           0 :       items.remove (key, lock);
     536           0 :       return true;
     537             :     }
     538             :   }
     539           0 :   hb_user_data_item_t item = {key, data, destroy};
     540           0 :   bool ret = !!items.replace_or_insert (item, lock, (bool) replace);
     541             : 
     542           0 :   return ret;
     543             : }
     544             : 
     545             : void *
     546           0 : hb_user_data_array_t::get (hb_user_data_key_t *key)
     547             : {
     548           0 :   hb_user_data_item_t item = {NULL, NULL, NULL};
     549             : 
     550           0 :   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           0 : hb_version (unsigned int *major,
     568             :             unsigned int *minor,
     569             :             unsigned int *micro)
     570             : {
     571           0 :   *major = HB_VERSION_MAJOR;
     572           0 :   *minor = HB_VERSION_MINOR;
     573           0 :   *micro = HB_VERSION_MICRO;
     574           0 : }
     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           0 : hb_version_string (void)
     587             : {
     588           0 :   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           0 : hb_version_atleast (unsigned int major,
     605             :                     unsigned int minor,
     606             :                     unsigned int micro)
     607             : {
     608           0 :   return HB_VERSION_ATLEAST (major, minor, micro);
     609             : }
     610             : 
     611             : 
     612             : 
     613             : /* hb_feature_t and hb_variation_t */
     614             : 
     615             : static bool
     616           0 : parse_space (const char **pp, const char *end)
     617             : {
     618           0 :   while (*pp < end && ISSPACE (**pp))
     619           0 :     (*pp)++;
     620           0 :   return true;
     621             : }
     622             : 
     623             : static bool
     624           0 : parse_char (const char **pp, const char *end, char c)
     625             : {
     626           0 :   parse_space (pp, end);
     627             : 
     628           0 :   if (*pp == end || **pp != c)
     629           0 :     return false;
     630             : 
     631           0 :   (*pp)++;
     632           0 :   return true;
     633             : }
     634             : 
     635             : static bool
     636           0 : parse_uint (const char **pp, const char *end, unsigned int *pv)
     637             : {
     638             :   char buf[32];
     639           0 :   unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp));
     640           0 :   strncpy (buf, *pp, len);
     641           0 :   buf[len] = '\0';
     642             : 
     643           0 :   char *p = buf;
     644           0 :   char *pend = p;
     645             :   unsigned int v;
     646             : 
     647             :   /* Intentionally use strtol instead of strtoul, such that
     648             :    * -1 turns into "big number"... */
     649           0 :   errno = 0;
     650           0 :   v = strtol (p, &pend, 0);
     651           0 :   if (errno || p == pend)
     652           0 :     return false;
     653             : 
     654           0 :   *pv = v;
     655           0 :   *pp += pend - p;
     656           0 :   return true;
     657             : }
     658             : 
     659             : static bool
     660           0 : parse_float (const char **pp, const char *end, float *pv)
     661             : {
     662             :   char buf[32];
     663           0 :   unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp));
     664           0 :   strncpy (buf, *pp, len);
     665           0 :   buf[len] = '\0';
     666             : 
     667           0 :   char *p = buf;
     668           0 :   char *pend = p;
     669             :   float v;
     670             : 
     671           0 :   errno = 0;
     672           0 :   v = strtod (p, &pend);
     673           0 :   if (errno || p == pend)
     674           0 :     return false;
     675             : 
     676           0 :   *pv = v;
     677           0 :   *pp += pend - p;
     678           0 :   return true;
     679             : }
     680             : 
     681             : static bool
     682           0 : parse_bool (const char **pp, const char *end, unsigned int *pv)
     683             : {
     684           0 :   parse_space (pp, end);
     685             : 
     686           0 :   const char *p = *pp;
     687           0 :   while (*pp < end && ISALPHA(**pp))
     688           0 :     (*pp)++;
     689             : 
     690             :   /* CSS allows on/off as aliases 1/0. */
     691           0 :   if (*pp - p == 2 || 0 == strncmp (p, "on", 2))
     692           0 :     *pv = 1;
     693           0 :   else if (*pp - p == 3 || 0 == strncmp (p, "off", 2))
     694           0 :     *pv = 0;
     695             :   else
     696           0 :     return false;
     697             : 
     698           0 :   return true;
     699             : }
     700             : 
     701             : /* hb_feature_t */
     702             : 
     703             : static bool
     704           0 : parse_feature_value_prefix (const char **pp, const char *end, hb_feature_t *feature)
     705             : {
     706           0 :   if (parse_char (pp, end, '-'))
     707           0 :     feature->value = 0;
     708             :   else {
     709           0 :     parse_char (pp, end, '+');
     710           0 :     feature->value = 1;
     711             :   }
     712             : 
     713           0 :   return true;
     714             : }
     715             : 
     716             : static bool
     717           0 : parse_tag (const char **pp, const char *end, hb_tag_t *tag)
     718             : {
     719           0 :   parse_space (pp, end);
     720             : 
     721           0 :   char quote = 0;
     722             : 
     723           0 :   if (*pp < end && (**pp == '\'' || **pp == '"'))
     724             :   {
     725           0 :     quote = **pp;
     726           0 :     (*pp)++;
     727             :   }
     728             : 
     729           0 :   const char *p = *pp;
     730           0 :   while (*pp < end && ISALNUM(**pp))
     731           0 :     (*pp)++;
     732             : 
     733           0 :   if (p == *pp || *pp - p > 4)
     734           0 :     return false;
     735             : 
     736           0 :   *tag = hb_tag_from_string (p, *pp - p);
     737             : 
     738           0 :   if (quote)
     739             :   {
     740             :     /* CSS expects exactly four bytes.  And we only allow quotations for
     741             :      * CSS compatibility.  So, enforce the length. */
     742           0 :      if (*pp - p != 4)
     743           0 :        return false;
     744           0 :     if (*pp == end || **pp != quote)
     745           0 :       return false;
     746           0 :     (*pp)++;
     747             :   }
     748             : 
     749           0 :   return true;
     750             : }
     751             : 
     752             : static bool
     753           0 : parse_feature_indices (const char **pp, const char *end, hb_feature_t *feature)
     754             : {
     755           0 :   parse_space (pp, end);
     756             : 
     757             :   bool has_start;
     758             : 
     759           0 :   feature->start = 0;
     760           0 :   feature->end = (unsigned int) -1;
     761             : 
     762           0 :   if (!parse_char (pp, end, '['))
     763           0 :     return true;
     764             : 
     765           0 :   has_start = parse_uint (pp, end, &feature->start);
     766             : 
     767           0 :   if (parse_char (pp, end, ':')) {
     768           0 :     parse_uint (pp, end, &feature->end);
     769             :   } else {
     770           0 :     if (has_start)
     771           0 :       feature->end = feature->start + 1;
     772             :   }
     773             : 
     774           0 :   return parse_char (pp, end, ']');
     775             : }
     776             : 
     777             : static bool
     778           0 : parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *feature)
     779             : {
     780           0 :   bool had_equal = parse_char (pp, end, '=');
     781           0 :   bool had_value = parse_uint (pp, end, &feature->value) ||
     782           0 :                    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           0 :   return !had_equal || had_value;
     787             : }
     788             : 
     789             : static bool
     790           0 : parse_one_feature (const char **pp, const char *end, hb_feature_t *feature)
     791             : {
     792           0 :   return parse_feature_value_prefix (pp, end, feature) &&
     793           0 :          parse_tag (pp, end, &feature->tag) &&
     794           0 :          parse_feature_indices (pp, end, feature) &&
     795           0 :          parse_feature_value_postfix (pp, end, feature) &&
     796           0 :          parse_space (pp, end) &&
     797           0 :          *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           0 : hb_feature_from_string (const char *str, int len,
     817             :                         hb_feature_t *feature)
     818             : {
     819             :   hb_feature_t feat;
     820             : 
     821           0 :   if (len < 0)
     822           0 :     len = strlen (str);
     823             : 
     824           0 :   if (likely (parse_one_feature (&str, str + len, &feat)))
     825             :   {
     826           0 :     if (feature)
     827           0 :       *feature = feat;
     828           0 :     return true;
     829             :   }
     830             : 
     831           0 :   if (feature)
     832           0 :     memset (feature, 0, sizeof (*feature));
     833           0 :   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           0 : hb_feature_to_string (hb_feature_t *feature,
     850             :                       char *buf, unsigned int size)
     851             : {
     852           0 :   if (unlikely (!size)) return;
     853             : 
     854             :   char s[128];
     855           0 :   unsigned int len = 0;
     856           0 :   if (feature->value == 0)
     857           0 :     s[len++] = '-';
     858           0 :   hb_tag_to_string (feature->tag, s + len);
     859           0 :   len += 4;
     860           0 :   while (len && s[len - 1] == ' ')
     861           0 :     len--;
     862           0 :   if (feature->start != 0 || feature->end != (unsigned int) -1)
     863             :   {
     864           0 :     s[len++] = '[';
     865           0 :     if (feature->start)
     866           0 :       len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->start));
     867           0 :     if (feature->end != feature->start + 1) {
     868           0 :       s[len++] = ':';
     869           0 :       if (feature->end != (unsigned int) -1)
     870           0 :         len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->end));
     871             :     }
     872           0 :     s[len++] = ']';
     873             :   }
     874           0 :   if (feature->value > 1)
     875             :   {
     876           0 :     s[len++] = '=';
     877           0 :     len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value));
     878             :   }
     879           0 :   assert (len < ARRAY_LENGTH (s));
     880           0 :   len = MIN (len, size - 1);
     881           0 :   memcpy (buf, s, len);
     882           0 :   buf[len] = '\0';
     883             : }
     884             : 
     885             : /* hb_variation_t */
     886             : 
     887             : static bool
     888           0 : parse_variation_value (const char **pp, const char *end, hb_variation_t *variation)
     889             : {
     890           0 :   parse_char (pp, end, '='); /* Optional. */
     891           0 :   return parse_float (pp, end, &variation->value);
     892             : }
     893             : 
     894             : static bool
     895           0 : parse_one_variation (const char **pp, const char *end, hb_variation_t *variation)
     896             : {
     897           0 :   return parse_tag (pp, end, &variation->tag) &&
     898           0 :          parse_variation_value (pp, end, variation) &&
     899           0 :          parse_space (pp, end) &&
     900           0 :          *pp == end;
     901             : }
     902             : 
     903             : /**
     904             :  * hb_variation_from_string:
     905             :  *
     906             :  * Since: 1.4.2
     907             :  */
     908             : hb_bool_t
     909           0 : hb_variation_from_string (const char *str, int len,
     910             :                           hb_variation_t *variation)
     911             : {
     912             :   hb_variation_t var;
     913             : 
     914           0 :   if (len < 0)
     915           0 :     len = strlen (str);
     916             : 
     917           0 :   if (likely (parse_one_variation (&str, str + len, &var)))
     918             :   {
     919           0 :     if (variation)
     920           0 :       *variation = var;
     921           0 :     return true;
     922             :   }
     923             : 
     924           0 :   if (variation)
     925           0 :     memset (variation, 0, sizeof (*variation));
     926           0 :   return false;
     927             : }
     928             : 
     929             : /**
     930             :  * hb_variation_to_string:
     931             :  *
     932             :  * Since: 1.4.2
     933             :  */
     934             : void
     935           0 : hb_variation_to_string (hb_variation_t *variation,
     936             :                         char *buf, unsigned int size)
     937             : {
     938           0 :   if (unlikely (!size)) return;
     939             : 
     940             :   char s[128];
     941           0 :   unsigned int len = 0;
     942           0 :   hb_tag_to_string (variation->tag, s + len);
     943           0 :   len += 4;
     944           0 :   while (len && s[len - 1] == ' ')
     945           0 :     len--;
     946           0 :   s[len++] = '=';
     947           0 :   len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", variation->value));
     948             : 
     949           0 :   assert (len < ARRAY_LENGTH (s));
     950           0 :   len = MIN (len, size - 1);
     951           0 :   memcpy (buf, s, len);
     952           0 :   buf[len] = '\0';
     953             : }

Generated by: LCOV version 1.13