tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

hb-common.cc (32249B)


      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.hh"
     30 #include "hb-machinery.hh"
     31 
     32 
     33 /**
     34 * SECTION:hb-common
     35 * @title: hb-common
     36 * @short_description: Common data types
     37 * @include: hb.h
     38 *
     39 * Common data types used across HarfBuzz are defined here.
     40 **/
     41 
     42 
     43 /* hb_tag_t */
     44 
     45 /**
     46 * hb_tag_from_string:
     47 * @str: (array length=len) (element-type uint8_t): String to convert
     48 * @len: Length of @str, or -1 if it is `NULL`-terminated
     49 *
     50 * Converts a string into an #hb_tag_t. Valid tags
     51 * are four characters. Shorter input strings will be
     52 * padded with spaces. Longer input strings will be
     53 * truncated.
     54 *
     55 * Return value: The #hb_tag_t corresponding to @str
     56 *
     57 * Since: 0.9.2
     58 **/
     59 hb_tag_t
     60 hb_tag_from_string (const char *str, int len)
     61 {
     62  char tag[4];
     63  unsigned int i;
     64 
     65  if (!str || !len || !*str)
     66    return HB_TAG_NONE;
     67 
     68  if (len < 0 || len > 4)
     69    len = 4;
     70  for (i = 0; i < (unsigned) len && str[i]; i++)
     71    tag[i] = str[i];
     72  for (; i < 4; i++)
     73    tag[i] = ' ';
     74 
     75  return HB_TAG (tag[0], tag[1], tag[2], tag[3]);
     76 }
     77 
     78 /**
     79 * hb_tag_to_string:
     80 * @tag: #hb_tag_t to convert
     81 * @buf: (out caller-allocates) (array fixed-size=4) (element-type uint8_t): Converted string
     82 *
     83 * Converts an #hb_tag_t to a string and returns it in @buf.
     84 * Strings will be four characters long.
     85 *
     86 * Since: 0.9.5
     87 **/
     88 void
     89 hb_tag_to_string (hb_tag_t tag, char *buf)
     90 {
     91  buf[0] = (char) (uint8_t) (tag >> 24);
     92  buf[1] = (char) (uint8_t) (tag >> 16);
     93  buf[2] = (char) (uint8_t) (tag >>  8);
     94  buf[3] = (char) (uint8_t) (tag >>  0);
     95 }
     96 
     97 
     98 /* hb_direction_t */
     99 
    100 static const char direction_strings[][4] = {
    101  "ltr",
    102  "rtl",
    103  "ttb",
    104  "btt"
    105 };
    106 
    107 /**
    108 * hb_direction_from_string:
    109 * @str: (array length=len) (element-type uint8_t): String to convert
    110 * @len: Length of @str, or -1 if it is `NULL`-terminated
    111 *
    112 * Converts a string to an #hb_direction_t.
    113 *
    114 * Matching is loose and applies only to the first letter. For
    115 * examples, "LTR" and "left-to-right" will both return #HB_DIRECTION_LTR.
    116 *
    117 * Unmatched strings will return #HB_DIRECTION_INVALID.
    118 *
    119 * Return value: The #hb_direction_t matching @str
    120 *
    121 * Since: 0.9.2
    122 **/
    123 hb_direction_t
    124 hb_direction_from_string (const char *str, int len)
    125 {
    126  if (unlikely (!str || !len || !*str))
    127    return HB_DIRECTION_INVALID;
    128 
    129  /* Lets match loosely: just match the first letter, such that
    130   * all of "ltr", "left-to-right", etc work!
    131   */
    132  char c = TOLOWER (str[0]);
    133  for (unsigned int i = 0; i < ARRAY_LENGTH (direction_strings); i++)
    134    if (c == direction_strings[i][0])
    135      return (hb_direction_t) (HB_DIRECTION_LTR + i);
    136 
    137  return HB_DIRECTION_INVALID;
    138 }
    139 
    140 /**
    141 * hb_direction_to_string:
    142 * @direction: The #hb_direction_t to convert
    143 *
    144 * Converts an #hb_direction_t to a string.
    145 *
    146 * Return value: (transfer none): The string corresponding to @direction
    147 *
    148 * Since: 0.9.2
    149 **/
    150 const char *
    151 hb_direction_to_string (hb_direction_t direction)
    152 {
    153  if (likely ((unsigned int) (direction - HB_DIRECTION_LTR)
    154       < ARRAY_LENGTH (direction_strings)))
    155    return direction_strings[direction - HB_DIRECTION_LTR];
    156 
    157  return "invalid";
    158 }
    159 
    160 
    161 /* hb_language_t */
    162 
    163 struct hb_language_impl_t {
    164  const char s[1];
    165 };
    166 
    167 static const char canon_map[256] = {
    168   0,   0,   0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,   0,   0,
    169   0,   0,   0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,   0,   0,
    170   0,   0,   0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,  '-',  0,   0,
    171  '0', '1', '2', '3', '4', '5', '6', '7',  '8', '9',  0,   0,   0,   0,   0,   0,
    172   0,  'a', 'b', 'c', 'd', 'e', 'f', 'g',  'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
    173  'p', 'q', 'r', 's', 't', 'u', 'v', 'w',  'x', 'y', 'z',  0,   0,   0,   0,  '-',
    174   0,  'a', 'b', 'c', 'd', 'e', 'f', 'g',  'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
    175  'p', 'q', 'r', 's', 't', 'u', 'v', 'w',  'x', 'y', 'z',  0,   0,   0,   0,   0
    176 };
    177 
    178 static bool
    179 lang_equal (hb_language_t  v1,
    180     const void    *v2)
    181 {
    182  const unsigned char *p1 = (const unsigned char *) v1;
    183  const unsigned char *p2 = (const unsigned char *) v2;
    184 
    185  while (*p1 && *p1 == canon_map[*p2]) {
    186    p1++;
    187    p2++;
    188  }
    189 
    190  return *p1 == canon_map[*p2];
    191 }
    192 
    193 #if 0
    194 static unsigned int
    195 lang_hash (const void *key)
    196 {
    197  const unsigned char *p = key;
    198  unsigned int h = 0;
    199  while (canon_map[*p])
    200    {
    201      h = (h << 5) - h + canon_map[*p];
    202      p++;
    203    }
    204 
    205  return h;
    206 }
    207 #endif
    208 
    209 
    210 struct hb_language_item_t {
    211 
    212  struct hb_language_item_t *next;
    213  hb_language_t lang;
    214 
    215  bool operator == (const char *s) const
    216  { return lang_equal (lang, s); }
    217 
    218  hb_language_item_t & operator = (const char *s)
    219  {
    220    /* We can't call strdup(), because we allow custom allocators. */
    221    size_t len = strlen(s) + 1;
    222    lang = (hb_language_t) hb_malloc(len);
    223    if (likely (lang))
    224    {
    225      hb_memcpy((unsigned char *) lang, s, len);
    226      for (unsigned char *p = (unsigned char *) lang; *p; p++)
    227 *p = canon_map[*p];
    228    }
    229 
    230    return *this;
    231  }
    232 
    233  void fini () { hb_free ((void *) lang); }
    234 };
    235 
    236 
    237 /* Thread-safe lockfree language list */
    238 
    239 static hb_atomic_t<hb_language_item_t *> langs;
    240 
    241 static inline void
    242 free_langs ()
    243 {
    244 retry:
    245  hb_language_item_t *first_lang = langs;
    246  if (unlikely (!langs.cmpexch (first_lang, nullptr)))
    247    goto retry;
    248 
    249  while (first_lang) {
    250    hb_language_item_t *next = first_lang->next;
    251    first_lang->fini ();
    252    hb_free (first_lang);
    253    first_lang = next;
    254  }
    255 }
    256 
    257 static hb_language_item_t *
    258 lang_find_or_insert (const char *key)
    259 {
    260 retry:
    261  hb_language_item_t *first_lang = langs;
    262 
    263  for (hb_language_item_t *lang = first_lang; lang; lang = lang->next)
    264    if (*lang == key)
    265      return lang;
    266 
    267  /* Not found; allocate one. */
    268  hb_language_item_t *lang = (hb_language_item_t *) hb_calloc (1, sizeof (hb_language_item_t));
    269  if (unlikely (!lang))
    270    return nullptr;
    271  lang->next = first_lang;
    272  *lang = key;
    273  if (unlikely (!lang->lang))
    274  {
    275    hb_free (lang);
    276    return nullptr;
    277  }
    278 
    279  if (unlikely (!langs.cmpexch (first_lang, lang)))
    280  {
    281    lang->fini ();
    282    hb_free (lang);
    283    goto retry;
    284  }
    285 
    286  if (!first_lang)
    287    hb_atexit (free_langs); /* First person registers atexit() callback. */
    288 
    289  return lang;
    290 }
    291 
    292 
    293 /**
    294 * hb_language_from_string:
    295 * @str: (array length=len) (element-type uint8_t): a string representing
    296 *       a BCP 47 language tag
    297 * @len: length of the @str, or -1 if it is `NULL`-terminated.
    298 *
    299 * Converts @str representing a BCP 47 language tag to the corresponding
    300 * #hb_language_t.
    301 *
    302 * Return value: (transfer none):
    303 * The #hb_language_t corresponding to the BCP 47 language tag.
    304 *
    305 * Since: 0.9.2
    306 **/
    307 hb_language_t
    308 hb_language_from_string (const char *str, int len)
    309 {
    310  if (!str || !len || !*str)
    311    return HB_LANGUAGE_INVALID;
    312 
    313  hb_language_item_t *item = nullptr;
    314  if (len >= 0)
    315  {
    316    /* NUL-terminate it. */
    317    char strbuf[64];
    318    len = hb_min (len, (int) sizeof (strbuf) - 1);
    319    hb_memcpy (strbuf, str, len);
    320    strbuf[len] = '\0';
    321    item = lang_find_or_insert (strbuf);
    322  }
    323  else
    324    item = lang_find_or_insert (str);
    325 
    326  return likely (item) ? item->lang : HB_LANGUAGE_INVALID;
    327 }
    328 
    329 /**
    330 * hb_language_to_string:
    331 * @language: The #hb_language_t to convert
    332 *
    333 * Converts an #hb_language_t to a string.
    334 *
    335 * Return value: (transfer none):
    336 * A `NULL`-terminated string representing the @language. Must not be freed by
    337 * the caller.
    338 *
    339 * Since: 0.9.2
    340 **/
    341 const char *
    342 hb_language_to_string (hb_language_t language)
    343 {
    344  if (unlikely (!language)) return nullptr;
    345 
    346  return language->s;
    347 }
    348 
    349 /**
    350 * hb_language_get_default:
    351 *
    352 * Fetch the default language from current locale.
    353 *
    354 * <note>Note that the first time this function is called, it calls
    355 * "setlocale (LC_CTYPE, nullptr)" to fetch current locale.  The underlying
    356 * setlocale function is, in many implementations, NOT threadsafe.  To avoid
    357 * problems, call this function once before multiple threads can call it.
    358 * This function is only used from hb_buffer_guess_segment_properties() by
    359 * HarfBuzz itself.</note>
    360 *
    361 * Return value: (transfer none): The default language of the locale as
    362 * an #hb_language_t
    363 *
    364 * Since: 0.9.2
    365 **/
    366 hb_language_t
    367 hb_language_get_default ()
    368 {
    369  static hb_atomic_t<hb_language_t> default_language;
    370 
    371  hb_language_t language = default_language;
    372  if (unlikely (language == HB_LANGUAGE_INVALID))
    373  {
    374    language = hb_language_from_string (hb_setlocale (LC_CTYPE, nullptr), -1);
    375    (void) default_language.cmpexch (HB_LANGUAGE_INVALID, language);
    376  }
    377 
    378  return language;
    379 }
    380 
    381 /**
    382 * hb_language_matches:
    383 * @language: The #hb_language_t to work on
    384 * @specific: Another #hb_language_t
    385 *
    386 * Check whether a second language tag is the same or a more
    387 * specific version of the provided language tag.  For example,
    388 * "fa_IR.utf8" is a more specific tag for "fa" or for "fa_IR".
    389 *
    390 * Return value: `true` if languages match, `false` otherwise.
    391 *
    392 * Since: 5.0.0
    393 **/
    394 hb_bool_t
    395 hb_language_matches (hb_language_t language,
    396 	     hb_language_t specific)
    397 {
    398  if (language == specific) return true;
    399  if (!language || !specific) return false;
    400 
    401  const char *l = language->s;
    402  const char *s = specific->s;
    403  unsigned ll = strlen (l);
    404  unsigned sl = strlen (s);
    405 
    406  if (ll > sl)
    407    return false;
    408 
    409  return strncmp (l, s, ll) == 0 &&
    410  (s[ll] == '\0' || s[ll] == '-');
    411 }
    412 
    413 
    414 /* hb_script_t */
    415 
    416 /**
    417 * hb_script_from_iso15924_tag:
    418 * @tag: an #hb_tag_t representing an ISO 15924 tag.
    419 *
    420 * Converts an ISO 15924 script tag to a corresponding #hb_script_t.
    421 *
    422 * Return value:
    423 * An #hb_script_t corresponding to the ISO 15924 tag.
    424 *
    425 * Since: 0.9.2
    426 **/
    427 hb_script_t
    428 hb_script_from_iso15924_tag (hb_tag_t tag)
    429 {
    430  if (unlikely (tag == HB_TAG_NONE))
    431    return HB_SCRIPT_INVALID;
    432 
    433  /* Be lenient, adjust case (one capital letter followed by three small letters) */
    434  tag = (tag & 0xDFDFDFDFu) | 0x00202020u;
    435 
    436  switch (tag) {
    437 
    438    /* These graduated from the 'Q' private-area codes, but
    439     * the old code is still aliased by Unicode, and the Qaai
    440     * one in use by ICU. */
    441    case HB_TAG('Q','a','a','i'): return HB_SCRIPT_INHERITED;
    442    case HB_TAG('Q','a','a','c'): return HB_SCRIPT_COPTIC;
    443 
    444    /* Script variants from https://unicode.org/iso15924/ */
    445    case HB_TAG('A','r','a','n'): return HB_SCRIPT_ARABIC;
    446    case HB_TAG('C','y','r','s'): return HB_SCRIPT_CYRILLIC;
    447    case HB_TAG('G','e','o','k'): return HB_SCRIPT_GEORGIAN;
    448    case HB_TAG('H','a','n','s'): return HB_SCRIPT_HAN;
    449    case HB_TAG('H','a','n','t'): return HB_SCRIPT_HAN;
    450    case HB_TAG('J','a','m','o'): return HB_SCRIPT_HANGUL;
    451    case HB_TAG('L','a','t','f'): return HB_SCRIPT_LATIN;
    452    case HB_TAG('L','a','t','g'): return HB_SCRIPT_LATIN;
    453    case HB_TAG('S','y','r','e'): return HB_SCRIPT_SYRIAC;
    454    case HB_TAG('S','y','r','j'): return HB_SCRIPT_SYRIAC;
    455    case HB_TAG('S','y','r','n'): return HB_SCRIPT_SYRIAC;
    456  }
    457 
    458  /* If it looks right, just use the tag as a script */
    459  if (((uint32_t) tag & 0xE0E0E0E0u) == 0x40606060u)
    460    return (hb_script_t) tag;
    461 
    462  /* Otherwise, return unknown */
    463  return HB_SCRIPT_UNKNOWN;
    464 }
    465 
    466 /**
    467 * hb_script_from_string:
    468 * @str: (array length=len) (element-type uint8_t): a string representing an
    469 *       ISO 15924 tag.
    470 * @len: length of the @str, or -1 if it is `NULL`-terminated.
    471 *
    472 * Converts a string @str representing an ISO 15924 script tag to a
    473 * corresponding #hb_script_t. Shorthand for hb_tag_from_string() then
    474 * hb_script_from_iso15924_tag().
    475 *
    476 * Return value:
    477 * An #hb_script_t corresponding to the ISO 15924 tag.
    478 *
    479 * Since: 0.9.2
    480 **/
    481 hb_script_t
    482 hb_script_from_string (const char *str, int len)
    483 {
    484  return hb_script_from_iso15924_tag (hb_tag_from_string (str, len));
    485 }
    486 
    487 /**
    488 * hb_script_to_iso15924_tag:
    489 * @script: an #hb_script_t to convert.
    490 *
    491 * Converts an #hb_script_t to a corresponding ISO 15924 script tag.
    492 *
    493 * Return value:
    494 * An #hb_tag_t representing an ISO 15924 script tag.
    495 *
    496 * Since: 0.9.2
    497 **/
    498 hb_tag_t
    499 hb_script_to_iso15924_tag (hb_script_t script)
    500 {
    501  return (hb_tag_t) script;
    502 }
    503 
    504 /**
    505 * hb_script_get_horizontal_direction:
    506 * @script: The #hb_script_t to query
    507 *
    508 * Fetches the #hb_direction_t of a script when it is
    509 * set horizontally. All right-to-left scripts will return
    510 * #HB_DIRECTION_RTL. All left-to-right scripts will return
    511 * #HB_DIRECTION_LTR.
    512 *
    513 * Scripts that can be written either right-to-left or
    514 * left-to-right will return #HB_DIRECTION_INVALID.
    515 *
    516 * Unknown scripts will return #HB_DIRECTION_LTR.
    517 *
    518 * Return value: The horizontal #hb_direction_t of @script
    519 *
    520 * Since: 0.9.2
    521 **/
    522 hb_direction_t
    523 hb_script_get_horizontal_direction (hb_script_t script)
    524 {
    525  /* https://docs.google.com/spreadsheets/d/1Y90M0Ie3MUJ6UVCRDOypOtijlMDLNNyyLk36T6iMu0o */
    526  switch ((hb_tag_t) script)
    527  {
    528    /* Unicode-1.1 additions */
    529    case HB_SCRIPT_ARABIC:
    530    case HB_SCRIPT_HEBREW:
    531 
    532    /* Unicode-3.0 additions */
    533    case HB_SCRIPT_SYRIAC:
    534    case HB_SCRIPT_THAANA:
    535 
    536    /* Unicode-4.0 additions */
    537    case HB_SCRIPT_CYPRIOT:
    538 
    539    /* Unicode-4.1 additions */
    540    case HB_SCRIPT_KHAROSHTHI:
    541 
    542    /* Unicode-5.0 additions */
    543    case HB_SCRIPT_PHOENICIAN:
    544    case HB_SCRIPT_NKO:
    545 
    546    /* Unicode-5.1 additions */
    547    case HB_SCRIPT_LYDIAN:
    548 
    549    /* Unicode-5.2 additions */
    550    case HB_SCRIPT_AVESTAN:
    551    case HB_SCRIPT_IMPERIAL_ARAMAIC:
    552    case HB_SCRIPT_INSCRIPTIONAL_PAHLAVI:
    553    case HB_SCRIPT_INSCRIPTIONAL_PARTHIAN:
    554    case HB_SCRIPT_OLD_SOUTH_ARABIAN:
    555    case HB_SCRIPT_OLD_TURKIC:
    556    case HB_SCRIPT_SAMARITAN:
    557 
    558    /* Unicode-6.0 additions */
    559    case HB_SCRIPT_MANDAIC:
    560 
    561    /* Unicode-6.1 additions */
    562    case HB_SCRIPT_MEROITIC_CURSIVE:
    563    case HB_SCRIPT_MEROITIC_HIEROGLYPHS:
    564 
    565    /* Unicode-7.0 additions */
    566    case HB_SCRIPT_MANICHAEAN:
    567    case HB_SCRIPT_MENDE_KIKAKUI:
    568    case HB_SCRIPT_NABATAEAN:
    569    case HB_SCRIPT_OLD_NORTH_ARABIAN:
    570    case HB_SCRIPT_PALMYRENE:
    571    case HB_SCRIPT_PSALTER_PAHLAVI:
    572 
    573    /* Unicode-8.0 additions */
    574    case HB_SCRIPT_HATRAN:
    575 
    576    /* Unicode-9.0 additions */
    577    case HB_SCRIPT_ADLAM:
    578 
    579    /* Unicode-11.0 additions */
    580    case HB_SCRIPT_HANIFI_ROHINGYA:
    581    case HB_SCRIPT_OLD_SOGDIAN:
    582    case HB_SCRIPT_SOGDIAN:
    583 
    584    /* Unicode-12.0 additions */
    585    case HB_SCRIPT_ELYMAIC:
    586 
    587    /* Unicode-13.0 additions */
    588    case HB_SCRIPT_CHORASMIAN:
    589    case HB_SCRIPT_YEZIDI:
    590 
    591    /* Unicode-14.0 additions */
    592    case HB_SCRIPT_OLD_UYGHUR:
    593 
    594    /* Unicode-16.0 additions */
    595    case HB_SCRIPT_GARAY:
    596 
    597    /* Unicode-17.0 additions */
    598    case HB_SCRIPT_SIDETIC:
    599 
    600      return HB_DIRECTION_RTL;
    601 
    602 
    603    /* https://github.com/harfbuzz/harfbuzz/issues/1000 */
    604    case HB_SCRIPT_OLD_HUNGARIAN:
    605    case HB_SCRIPT_OLD_ITALIC:
    606    case HB_SCRIPT_RUNIC:
    607    case HB_SCRIPT_TIFINAGH:
    608 
    609      return HB_DIRECTION_INVALID;
    610  }
    611 
    612  return HB_DIRECTION_LTR;
    613 }
    614 
    615 
    616 /* hb_version */
    617 
    618 
    619 /**
    620 * SECTION:hb-version
    621 * @title: hb-version
    622 * @short_description: Information about the version of HarfBuzz in use
    623 * @include: hb.h
    624 *
    625 * These functions and macros allow accessing version of the HarfBuzz
    626 * library used at compile- as well as run-time, and to direct code
    627 * conditionally based on those versions, again, at compile- or run-time.
    628 **/
    629 
    630 
    631 /**
    632 * hb_version:
    633 * @major: (out): Library major version component
    634 * @minor: (out): Library minor version component
    635 * @micro: (out): Library micro version component
    636 *
    637 * Returns library version as three integer components.
    638 *
    639 * Since: 0.9.2
    640 **/
    641 void
    642 hb_version (unsigned int *major,
    643     unsigned int *minor,
    644     unsigned int *micro)
    645 {
    646  *major = HB_VERSION_MAJOR;
    647  *minor = HB_VERSION_MINOR;
    648  *micro = HB_VERSION_MICRO;
    649 }
    650 
    651 /**
    652 * hb_version_string:
    653 *
    654 * Returns library version as a string with three components.
    655 *
    656 * Return value: Library version string
    657 *
    658 * Since: 0.9.2
    659 **/
    660 const char *
    661 hb_version_string ()
    662 {
    663  return HB_VERSION_STRING;
    664 }
    665 
    666 /**
    667 * hb_version_atleast:
    668 * @major: Library major version component
    669 * @minor: Library minor version component
    670 * @micro: Library micro version component
    671 *
    672 * Tests the library version against a minimum value,
    673 * as three integer components.
    674 *
    675 * Return value: `true` if the library is equal to or greater than
    676 * the test value, `false` otherwise
    677 *
    678 * Since: 0.9.30
    679 **/
    680 hb_bool_t
    681 hb_version_atleast (unsigned int major,
    682 	    unsigned int minor,
    683 	    unsigned int micro)
    684 {
    685  return HB_VERSION_ATLEAST (major, minor, micro);
    686 }
    687 
    688 
    689 
    690 /* hb_feature_t and hb_variation_t */
    691 
    692 static bool
    693 parse_space (const char **pp, const char *end)
    694 {
    695  while (*pp < end && ISSPACE (**pp))
    696    (*pp)++;
    697  return true;
    698 }
    699 
    700 static bool
    701 parse_char (const char **pp, const char *end, char c)
    702 {
    703  parse_space (pp, end);
    704 
    705  if (*pp == end || **pp != c)
    706    return false;
    707 
    708  (*pp)++;
    709  return true;
    710 }
    711 
    712 static bool
    713 parse_uint (const char **pp, const char *end, unsigned int *pv)
    714 {
    715  /* Intentionally use hb_parse_int inside instead of hb_parse_uint,
    716   * such that -1 turns into "big number"... */
    717  int v;
    718  if (unlikely (!hb_parse_int (pp, end, &v))) return false;
    719 
    720  *pv = v;
    721  return true;
    722 }
    723 
    724 static bool
    725 parse_uint32 (const char **pp, const char *end, uint32_t *pv)
    726 {
    727  /* Intentionally use hb_parse_int inside instead of hb_parse_uint,
    728   * such that -1 turns into "big number"... */
    729  int v;
    730  if (unlikely (!hb_parse_int (pp, end, &v))) return false;
    731 
    732  *pv = v;
    733  return true;
    734 }
    735 
    736 static bool
    737 parse_bool (const char **pp, const char *end, uint32_t *pv)
    738 {
    739  parse_space (pp, end);
    740 
    741  const char *p = *pp;
    742  while (*pp < end && ISALPHA(**pp))
    743    (*pp)++;
    744 
    745  /* CSS allows on/off as aliases 1/0. */
    746  if (*pp - p == 2
    747      && TOLOWER (p[0]) == 'o'
    748      && TOLOWER (p[1]) == 'n')
    749    *pv = 1;
    750  else if (*pp - p == 3
    751    && TOLOWER (p[0]) == 'o'
    752    && TOLOWER (p[1]) == 'f'
    753    && TOLOWER (p[2]) == 'f')
    754    *pv = 0;
    755  else
    756    return false;
    757 
    758  return true;
    759 }
    760 
    761 /* hb_feature_t */
    762 
    763 static bool
    764 parse_feature_value_prefix (const char **pp, const char *end, hb_feature_t *feature)
    765 {
    766  if (parse_char (pp, end, '-'))
    767    feature->value = 0;
    768  else {
    769    parse_char (pp, end, '+');
    770    feature->value = 1;
    771  }
    772 
    773  return true;
    774 }
    775 
    776 static bool
    777 parse_tag (const char **pp, const char *end, hb_tag_t *tag)
    778 {
    779  parse_space (pp, end);
    780 
    781  char quote = 0;
    782 
    783  if (*pp < end && (**pp == '\'' || **pp == '"'))
    784  {
    785    quote = **pp;
    786    (*pp)++;
    787  }
    788 
    789  const char *p = *pp;
    790  while (*pp < end && (**pp != ' ' && **pp != '=' && **pp != '[' && **pp != quote))
    791    (*pp)++;
    792 
    793  if (p == *pp || *pp - p > 4)
    794    return false;
    795 
    796  *tag = hb_tag_from_string (p, *pp - p);
    797 
    798  if (quote)
    799  {
    800    /* CSS expects exactly four bytes.  And we only allow quotations for
    801     * CSS compatibility.  So, enforce the length. */
    802     if (*pp - p != 4)
    803       return false;
    804    if (*pp == end || **pp != quote)
    805      return false;
    806    (*pp)++;
    807  }
    808 
    809  return true;
    810 }
    811 
    812 static bool
    813 parse_feature_indices (const char **pp, const char *end, hb_feature_t *feature)
    814 {
    815  parse_space (pp, end);
    816 
    817  bool has_start;
    818 
    819  feature->start = HB_FEATURE_GLOBAL_START;
    820  feature->end = HB_FEATURE_GLOBAL_END;
    821 
    822  if (!parse_char (pp, end, '['))
    823    return true;
    824 
    825  has_start = parse_uint (pp, end, &feature->start);
    826 
    827  if (parse_char (pp, end, ':') || parse_char (pp, end, ';')) {
    828    parse_uint (pp, end, &feature->end);
    829  } else {
    830    if (has_start)
    831      feature->end = feature->start + 1;
    832  }
    833 
    834  return parse_char (pp, end, ']');
    835 }
    836 
    837 static bool
    838 parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *feature)
    839 {
    840  bool had_equal = parse_char (pp, end, '=');
    841  bool had_value = parse_uint32 (pp, end, &feature->value) ||
    842 	   parse_bool (pp, end, &feature->value);
    843  /* CSS doesn't use equal-sign between tag and value.
    844   * If there was an equal-sign, then there *must* be a value.
    845   * A value without an equal-sign is ok, but not required. */
    846  return !had_equal || had_value;
    847 }
    848 
    849 static bool
    850 parse_one_feature (const char **pp, const char *end, hb_feature_t *feature)
    851 {
    852  return parse_feature_value_prefix (pp, end, feature) &&
    853  parse_tag (pp, end, &feature->tag) &&
    854  parse_feature_indices (pp, end, feature) &&
    855  parse_feature_value_postfix (pp, end, feature) &&
    856  parse_space (pp, end) &&
    857  *pp == end;
    858 }
    859 
    860 /**
    861 * hb_feature_from_string:
    862 * @str: (array length=len) (element-type uint8_t): a string to parse
    863 * @len: length of @str, or -1 if string is `NULL` terminated
    864 * @feature: (out): the #hb_feature_t to initialize with the parsed values
    865 *
    866 * Parses a string into a #hb_feature_t.
    867 *
    868 * The format for specifying feature strings follows. All valid CSS
    869 * font-feature-settings values other than 'normal' and the global values are
    870 * also accepted, though not documented below. CSS string escapes are not
    871 * supported.
    872 *
    873 * The range indices refer to the positions between Unicode characters. The
    874 * position before the first character is always 0.
    875 *
    876 * The format is Python-esque.  Here is how it all works:
    877 *
    878 * <informaltable pgwide='1' align='left' frame='none'>
    879 * <tgroup cols='5'>
    880 * <thead>
    881 * <row><entry>Syntax</entry>    <entry>Value</entry> <entry>Start</entry> <entry>End</entry></row>
    882 * </thead>
    883 * <tbody>
    884 * <row><entry>Setting value:</entry></row>
    885 * <row><entry>kern</entry>      <entry>1</entry>     <entry>0</entry>      <entry>∞</entry>   <entry>Turn feature on</entry></row>
    886 * <row><entry>+kern</entry>     <entry>1</entry>     <entry>0</entry>      <entry>∞</entry>   <entry>Turn feature on</entry></row>
    887 * <row><entry>-kern</entry>     <entry>0</entry>     <entry>0</entry>      <entry>∞</entry>   <entry>Turn feature off</entry></row>
    888 * <row><entry>kern=0</entry>    <entry>0</entry>     <entry>0</entry>      <entry>∞</entry>   <entry>Turn feature off</entry></row>
    889 * <row><entry>kern=1</entry>    <entry>1</entry>     <entry>0</entry>      <entry>∞</entry>   <entry>Turn feature on</entry></row>
    890 * <row><entry>aalt=2</entry>    <entry>2</entry>     <entry>0</entry>      <entry>∞</entry>   <entry>Choose 2nd alternate</entry></row>
    891 * <row><entry>Setting index:</entry></row>
    892 * <row><entry>kern[]</entry>    <entry>1</entry>     <entry>0</entry>      <entry>∞</entry>   <entry>Turn feature on</entry></row>
    893 * <row><entry>kern[:]</entry>   <entry>1</entry>     <entry>0</entry>      <entry>∞</entry>   <entry>Turn feature on</entry></row>
    894 * <row><entry>kern[5:]</entry>  <entry>1</entry>     <entry>5</entry>      <entry>∞</entry>   <entry>Turn feature on, partial</entry></row>
    895 * <row><entry>kern[:5]</entry>  <entry>1</entry>     <entry>0</entry>      <entry>5</entry>   <entry>Turn feature on, partial</entry></row>
    896 * <row><entry>kern[3:5]</entry> <entry>1</entry>     <entry>3</entry>      <entry>5</entry>   <entry>Turn feature on, range</entry></row>
    897 * <row><entry>kern[3]</entry>   <entry>1</entry>     <entry>3</entry>      <entry>3+1</entry> <entry>Turn feature on, single char</entry></row>
    898 * <row><entry>Mixing it all:</entry></row>
    899 * <row><entry>aalt[3:5]=2</entry> <entry>2</entry>   <entry>3</entry>      <entry>5</entry>   <entry>Turn 2nd alternate on for range</entry></row>
    900 * </tbody>
    901 * </tgroup>
    902 * </informaltable>
    903 *
    904 * Return value:
    905 * `true` if @str is successfully parsed, `false` otherwise
    906 *
    907 * Since: 0.9.5
    908 **/
    909 hb_bool_t
    910 hb_feature_from_string (const char *str, int len,
    911 		hb_feature_t *feature)
    912 {
    913  hb_feature_t feat;
    914 
    915  if (len < 0)
    916    len = strlen (str);
    917 
    918  if (likely (parse_one_feature (&str, str + len, &feat)))
    919  {
    920    if (feature)
    921      *feature = feat;
    922    return true;
    923  }
    924 
    925  if (feature)
    926    hb_memset (feature, 0, sizeof (*feature));
    927  return false;
    928 }
    929 
    930 /**
    931 * hb_feature_to_string:
    932 * @feature: an #hb_feature_t to convert
    933 * @buf: (array length=size) (out): output string
    934 * @size: the allocated size of @buf
    935 *
    936 * Converts a #hb_feature_t into a `NULL`-terminated string in the format
    937 * understood by hb_feature_from_string(). The client in responsible for
    938 * allocating big enough size for @buf, 128 bytes is more than enough.
    939 *
    940 * Note that the feature value will be omitted if it is '1', but the
    941 * string won't include any whitespace.
    942 *
    943 * Since: 0.9.5
    944 **/
    945 void
    946 hb_feature_to_string (hb_feature_t *feature,
    947 	      char *buf, unsigned int size)
    948 {
    949  if (unlikely (!size)) return;
    950 
    951  char s[128];
    952  unsigned int len = 0;
    953  if (feature->value == 0)
    954    s[len++] = '-';
    955  hb_tag_to_string (feature->tag, s + len);
    956  len += 4;
    957  while (len && s[len - 1] == ' ')
    958    len--;
    959  if (feature->start != HB_FEATURE_GLOBAL_START || feature->end != HB_FEATURE_GLOBAL_END)
    960  {
    961    s[len++] = '[';
    962    if (feature->start)
    963      len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->start));
    964    if (feature->end != feature->start + 1) {
    965      s[len++] = ':';
    966      if (feature->end != HB_FEATURE_GLOBAL_END)
    967 len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->end));
    968    }
    969    s[len++] = ']';
    970  }
    971  if (feature->value > 1)
    972  {
    973    s[len++] = '=';
    974    len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%" PRIu32, feature->value));
    975  }
    976  assert (len < ARRAY_LENGTH (s));
    977  len = hb_min (len, size - 1);
    978  hb_memcpy (buf, s, len);
    979  buf[len] = '\0';
    980 }
    981 
    982 /* hb_variation_t */
    983 
    984 static bool
    985 parse_variation_value (const char **pp, const char *end, hb_variation_t *variation)
    986 {
    987  parse_char (pp, end, '='); /* Optional. */
    988  double v;
    989  if (unlikely (!hb_parse_double (pp, end, &v))) return false;
    990 
    991  variation->value = v;
    992  return true;
    993 }
    994 
    995 static bool
    996 parse_one_variation (const char **pp, const char *end, hb_variation_t *variation)
    997 {
    998  return parse_tag (pp, end, &variation->tag) &&
    999  parse_variation_value (pp, end, variation) &&
   1000  parse_space (pp, end) &&
   1001  *pp == end;
   1002 }
   1003 
   1004 /**
   1005 * hb_variation_from_string:
   1006 * @str: (array length=len) (element-type uint8_t): a string to parse
   1007 * @len: length of @str, or -1 if string is `NULL` terminated
   1008 * @variation: (out): the #hb_variation_t to initialize with the parsed values
   1009 *
   1010 * Parses a string into a #hb_variation_t.
   1011 *
   1012 * The format for specifying variation settings follows. All valid CSS
   1013 * font-variation-settings values other than 'normal' and 'inherited' are also
   1014 * accepted, though, not documented below.
   1015 *
   1016 * The format is a tag, optionally followed by an equals sign, followed by a
   1017 * number. For example `wght=500`, or `slnt=-7.5`.
   1018 *
   1019 * Return value:
   1020 * `true` if @str is successfully parsed, `false` otherwise
   1021 *
   1022 * Since: 1.4.2
   1023 */
   1024 hb_bool_t
   1025 hb_variation_from_string (const char *str, int len,
   1026 		  hb_variation_t *variation)
   1027 {
   1028  hb_variation_t var;
   1029 
   1030  if (len < 0)
   1031    len = strlen (str);
   1032 
   1033  if (likely (parse_one_variation (&str, str + len, &var)))
   1034  {
   1035    if (variation)
   1036      *variation = var;
   1037    return true;
   1038  }
   1039 
   1040  if (variation)
   1041    hb_memset (variation, 0, sizeof (*variation));
   1042  return false;
   1043 }
   1044 
   1045 #ifndef HB_NO_SETLOCALE
   1046 
   1047 static inline void free_static_C_locale ();
   1048 
   1049 static struct hb_C_locale_lazy_loader_t : hb_lazy_loader_t<hb_remove_pointer<hb_locale_t>,
   1050 						   hb_C_locale_lazy_loader_t>
   1051 {
   1052  static hb_locale_t create ()
   1053  {
   1054    hb_locale_t l = newlocale (LC_ALL_MASK, "C", NULL);
   1055    if (!l)
   1056      return l;
   1057 
   1058    hb_atexit (free_static_C_locale);
   1059 
   1060    return l;
   1061  }
   1062  static void destroy (hb_locale_t l)
   1063  {
   1064    freelocale (l);
   1065  }
   1066  static hb_locale_t get_null ()
   1067  {
   1068    return (hb_locale_t) 0;
   1069  }
   1070 } static_C_locale;
   1071 
   1072 static inline
   1073 void free_static_C_locale ()
   1074 {
   1075  static_C_locale.free_instance ();
   1076 }
   1077 
   1078 static hb_locale_t
   1079 get_C_locale ()
   1080 {
   1081  return static_C_locale.get_unconst ();
   1082 }
   1083 
   1084 #endif
   1085 
   1086 /**
   1087 * hb_variation_to_string:
   1088 * @variation: an #hb_variation_t to convert
   1089 * @buf: (array length=size) (out caller-allocates): output string
   1090 * @size: the allocated size of @buf
   1091 *
   1092 * Converts an #hb_variation_t into a `NULL`-terminated string in the format
   1093 * understood by hb_variation_from_string(). The client in responsible for
   1094 * allocating big enough size for @buf, 128 bytes is more than enough.
   1095 *
   1096 * Note that the string won't include any whitespace.
   1097 *
   1098 * Since: 1.4.2
   1099 */
   1100 void
   1101 hb_variation_to_string (hb_variation_t *variation,
   1102 		char *buf, unsigned int size)
   1103 {
   1104  if (unlikely (!size)) return;
   1105 
   1106  char s[128];
   1107  unsigned int len = 0;
   1108  hb_tag_to_string (variation->tag, s + len);
   1109  len += 4;
   1110  while (len && s[len - 1] == ' ')
   1111    len--;
   1112  s[len++] = '=';
   1113 
   1114  hb_locale_t oldlocale HB_UNUSED;
   1115  oldlocale = hb_uselocale (get_C_locale ());
   1116  len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", (double) variation->value));
   1117  (void) hb_uselocale (oldlocale);
   1118 
   1119  assert (len < ARRAY_LENGTH (s));
   1120  len = hb_min (len, size - 1);
   1121  hb_memcpy (buf, s, len);
   1122  buf[len] = '\0';
   1123 }
   1124 
   1125 /**
   1126 * hb_color_get_alpha:
   1127 * @color: an #hb_color_t we are interested in its channels.
   1128 *
   1129 * Fetches the alpha channel of the given @color.
   1130 *
   1131 * Return value: Alpha channel value
   1132 *
   1133 * Since: 2.1.0
   1134 */
   1135 uint8_t
   1136 (hb_color_get_alpha) (hb_color_t color)
   1137 {
   1138  return hb_color_get_alpha (color);
   1139 }
   1140 
   1141 /**
   1142 * hb_color_get_red:
   1143 * @color: an #hb_color_t we are interested in its channels.
   1144 *
   1145 * Fetches the red channel of the given @color.
   1146 *
   1147 * Return value: Red channel value
   1148 *
   1149 * Since: 2.1.0
   1150 */
   1151 uint8_t
   1152 (hb_color_get_red) (hb_color_t color)
   1153 {
   1154  return hb_color_get_red (color);
   1155 }
   1156 
   1157 /**
   1158 * hb_color_get_green:
   1159 * @color: an #hb_color_t we are interested in its channels.
   1160 *
   1161 * Fetches the green channel of the given @color.
   1162 *
   1163 * Return value: Green channel value
   1164 *
   1165 * Since: 2.1.0
   1166 */
   1167 uint8_t
   1168 (hb_color_get_green) (hb_color_t color)
   1169 {
   1170  return hb_color_get_green (color);
   1171 }
   1172 
   1173 /**
   1174 * hb_color_get_blue:
   1175 * @color: an #hb_color_t we are interested in its channels.
   1176 *
   1177 * Fetches the blue channel of the given @color.
   1178 *
   1179 * Return value: Blue channel value
   1180 *
   1181 * Since: 2.1.0
   1182 */
   1183 uint8_t
   1184 (hb_color_get_blue) (hb_color_t color)
   1185 {
   1186  return hb_color_get_blue (color);
   1187 }
   1188 
   1189 /**
   1190 * hb_malloc:
   1191 * @size: The size of the memory to allocate.
   1192 *
   1193 * Allocates @size bytes of memory, using the allocator set at
   1194 * compile-time. Typically just malloc().
   1195 *
   1196 * Return value: A pointer to the allocated memory.
   1197 *
   1198 * Since: 11.0.0
   1199 **/
   1200 void* hb_malloc(size_t size) { return hb_malloc_impl (size); }
   1201 
   1202 /**
   1203 * hb_calloc:
   1204 * @nmemb: The number of elements to allocate.
   1205 * @size: The size of each element.
   1206 *
   1207 * Allocates @nmemb elements of @size bytes each, initialized to zero,
   1208 * using the allocator set at compile-time. Typically just calloc().
   1209 *
   1210 * Return value: A pointer to the allocated memory.
   1211 *
   1212 * Since: 11.0.0
   1213 **/
   1214 void* hb_calloc(size_t nmemb, size_t size) { return hb_calloc_impl (nmemb, size); }
   1215 
   1216 /**
   1217 * hb_realloc:
   1218 * @ptr: The pointer to the memory to reallocate.
   1219 * @size: The new size of the memory.
   1220 *
   1221 * Reallocates the memory pointed to by @ptr to @size bytes, using the
   1222 * allocator set at compile-time. Typically just realloc().
   1223 *
   1224 * Return value: A pointer to the reallocated memory.
   1225 *
   1226 * Since: 11.0.0
   1227 **/
   1228 void* hb_realloc(void *ptr, size_t size) { return hb_realloc_impl (ptr, size); }
   1229 
   1230 /**
   1231 * hb_free:
   1232 * @ptr: The pointer to the memory to free.
   1233 *
   1234 * Frees the memory pointed to by @ptr, using the allocator set at
   1235 * compile-time. Typically just free().
   1236 *
   1237 * Since: 11.0.0
   1238 **/
   1239 void  hb_free(void *ptr) { hb_free_impl (ptr); }
   1240 
   1241 
   1242 /* If there is no visibility control, then hb-static.cc will NOT
   1243 * define anything.  Instead, we get it to define one set in here
   1244 * only, so only libharfbuzz.so defines them, not other libs. */
   1245 #ifdef HB_NO_VISIBILITY
   1246 #undef HB_NO_VISIBILITY
   1247 #include "hb-static.cc"
   1248 #define HB_NO_VISIBILITY 1
   1249 #endif