Blame src/hb-common.cc

Packit Service 5bcba8
/*
Packit Service 5bcba8
 * Copyright © 2009,2010  Red Hat, Inc.
Packit Service 5bcba8
 * Copyright © 2011,2012  Google, Inc.
Packit Service 5bcba8
 *
Packit Service 5bcba8
 *  This is part of HarfBuzz, a text shaping library.
Packit Service 5bcba8
 *
Packit Service 5bcba8
 * Permission is hereby granted, without written agreement and without
Packit Service 5bcba8
 * license or royalty fees, to use, copy, modify, and distribute this
Packit Service 5bcba8
 * software and its documentation for any purpose, provided that the
Packit Service 5bcba8
 * above copyright notice and the following two paragraphs appear in
Packit Service 5bcba8
 * all copies of this software.
Packit Service 5bcba8
 *
Packit Service 5bcba8
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
Packit Service 5bcba8
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
Packit Service 5bcba8
 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
Packit Service 5bcba8
 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
Packit Service 5bcba8
 * DAMAGE.
Packit Service 5bcba8
 *
Packit Service 5bcba8
 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
Packit Service 5bcba8
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
Packit Service 5bcba8
 * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
Packit Service 5bcba8
 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
Packit Service 5bcba8
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
Packit Service 5bcba8
 *
Packit Service 5bcba8
 * Red Hat Author(s): Behdad Esfahbod
Packit Service 5bcba8
 * Google Author(s): Behdad Esfahbod
Packit Service 5bcba8
 */
Packit Service 5bcba8
Packit Service 5bcba8
#include "hb-private.hh"
Packit Service 5bcba8
Packit Service 5bcba8
#include "hb-mutex-private.hh"
Packit Service 5bcba8
#include "hb-object-private.hh"
Packit Service 5bcba8
Packit Service 5bcba8
#include <locale.h>
Packit Service 5bcba8
Packit Service 5bcba8
Packit Service 5bcba8
/* hb_options_t */
Packit Service 5bcba8
Packit Service 5bcba8
hb_options_union_t _hb_options;
Packit Service 5bcba8
Packit Service 5bcba8
void
Packit Service 5bcba8
_hb_options_init (void)
Packit Service 5bcba8
{
Packit Service 5bcba8
  hb_options_union_t u;
Packit Service 5bcba8
  u.i = 0;
Packit Service 5bcba8
  u.opts.initialized = 1;
Packit Service 5bcba8
Packit Service 5bcba8
  char *c = getenv ("HB_OPTIONS");
Packit Service 5bcba8
  u.opts.uniscribe_bug_compatible = c && strstr (c, "uniscribe-bug-compatible");
Packit Service 5bcba8
Packit Service 5bcba8
  /* This is idempotent and threadsafe. */
Packit Service 5bcba8
  _hb_options = u;
Packit Service 5bcba8
}
Packit Service 5bcba8
Packit Service 5bcba8
Packit Service 5bcba8
/* hb_tag_t */
Packit Service 5bcba8
Packit Service 5bcba8
/**
Packit Service 5bcba8
 * hb_tag_from_string:
Packit Service 5bcba8
 * @str: (array length=len) (element-type uint8_t): 
Packit Service 5bcba8
 * @len: 
Packit Service 5bcba8
 *
Packit Service 5bcba8
 * 
Packit Service 5bcba8
 *
Packit Service 5bcba8
 * Return value: 
Packit Service 5bcba8
 *
Packit Service 5bcba8
 * Since: 0.9.2
Packit Service 5bcba8
 **/
Packit Service 5bcba8
hb_tag_t
Packit Service 5bcba8
hb_tag_from_string (const char *str, int len)
Packit Service 5bcba8
{
Packit Service 5bcba8
  char tag[4];
Packit Service 5bcba8
  unsigned int i;
Packit Service 5bcba8
Packit Service 5bcba8
  if (!str || !len || !*str)
Packit Service 5bcba8
    return HB_TAG_NONE;
Packit Service 5bcba8
Packit Service 5bcba8
  if (len < 0 || len > 4)
Packit Service 5bcba8
    len = 4;
Packit Service 5bcba8
  for (i = 0; i < (unsigned) len && str[i]; i++)
Packit Service 5bcba8
    tag[i] = str[i];
Packit Service 5bcba8
  for (; i < 4; i++)
Packit Service 5bcba8
    tag[i] = ' ';
Packit Service 5bcba8
Packit Service 5bcba8
  return HB_TAG_CHAR4 (tag);
Packit Service 5bcba8
}
Packit Service 5bcba8
Packit Service 5bcba8
/**
Packit Service 5bcba8
 * hb_tag_to_string:
Packit Service 5bcba8
 * @tag: 
Packit Service 5bcba8
 * @buf: (out caller-allocates) (array fixed-size=4) (element-type uint8_t): 
Packit Service 5bcba8
 *
Packit Service 5bcba8
 * 
Packit Service 5bcba8
 *
Packit Service 5bcba8
 * Since: 0.9.5
Packit Service 5bcba8
 **/
Packit Service 5bcba8
void
Packit Service 5bcba8
hb_tag_to_string (hb_tag_t tag, char *buf)
Packit Service 5bcba8
{
Packit Service 5bcba8
  buf[0] = (char) (uint8_t) (tag >> 24);
Packit Service 5bcba8
  buf[1] = (char) (uint8_t) (tag >> 16);
Packit Service 5bcba8
  buf[2] = (char) (uint8_t) (tag >>  8);
Packit Service 5bcba8
  buf[3] = (char) (uint8_t) (tag >>  0);
Packit Service 5bcba8
}
Packit Service 5bcba8
Packit Service 5bcba8
Packit Service 5bcba8
/* hb_direction_t */
Packit Service 5bcba8
Packit Service 5bcba8
const char direction_strings[][4] = {
Packit Service 5bcba8
  "ltr",
Packit Service 5bcba8
  "rtl",
Packit Service 5bcba8
  "ttb",
Packit Service 5bcba8
  "btt"
Packit Service 5bcba8
};
Packit Service 5bcba8
Packit Service 5bcba8
/**
Packit Service 5bcba8
 * hb_direction_from_string:
Packit Service 5bcba8
 * @str: (array length=len) (element-type uint8_t): 
Packit Service 5bcba8
 * @len: 
Packit Service 5bcba8
 *
Packit Service 5bcba8
 * 
Packit Service 5bcba8
 *
Packit Service 5bcba8
 * Return value: 
Packit Service 5bcba8
 *
Packit Service 5bcba8
 * Since: 0.9.2
Packit Service 5bcba8
 **/
Packit Service 5bcba8
hb_direction_t
Packit Service 5bcba8
hb_direction_from_string (const char *str, int len)
Packit Service 5bcba8
{
Packit Service 5bcba8
  if (unlikely (!str || !len || !*str))
Packit Service 5bcba8
    return HB_DIRECTION_INVALID;
Packit Service 5bcba8
Packit Service 5bcba8
  /* Lets match loosely: just match the first letter, such that
Packit Service 5bcba8
   * all of "ltr", "left-to-right", etc work!
Packit Service 5bcba8
   */
Packit Service 5bcba8
  char c = TOLOWER (str[0]);
Packit Service 5bcba8
  for (unsigned int i = 0; i < ARRAY_LENGTH (direction_strings); i++)
Packit Service 5bcba8
    if (c == direction_strings[i][0])
Packit Service 5bcba8
      return (hb_direction_t) (HB_DIRECTION_LTR + i);
Packit Service 5bcba8
Packit Service 5bcba8
  return HB_DIRECTION_INVALID;
Packit Service 5bcba8
}
Packit Service 5bcba8
Packit Service 5bcba8
/**
Packit Service 5bcba8
 * hb_direction_to_string:
Packit Service 5bcba8
 * @direction: 
Packit Service 5bcba8
 *
Packit Service 5bcba8
 * 
Packit Service 5bcba8
 *
Packit Service 5bcba8
 * Return value: (transfer none): 
Packit Service 5bcba8
 *
Packit Service 5bcba8
 * Since: 0.9.2
Packit Service 5bcba8
 **/
Packit Service 5bcba8
const char *
Packit Service 5bcba8
hb_direction_to_string (hb_direction_t direction)
Packit Service 5bcba8
{
Packit Service 5bcba8
  if (likely ((unsigned int) (direction - HB_DIRECTION_LTR)
Packit Service 5bcba8
	      < ARRAY_LENGTH (direction_strings)))
Packit Service 5bcba8
    return direction_strings[direction - HB_DIRECTION_LTR];
Packit Service 5bcba8
Packit Service 5bcba8
  return "invalid";
Packit Service 5bcba8
}
Packit Service 5bcba8
Packit Service 5bcba8
Packit Service 5bcba8
/* hb_language_t */
Packit Service 5bcba8
Packit Service 5bcba8
struct hb_language_impl_t {
Packit Service 5bcba8
  const char s[1];
Packit Service 5bcba8
};
Packit Service 5bcba8
Packit Service 5bcba8
static const char canon_map[256] = {
Packit Service 5bcba8
   0,   0,   0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,   0,   0,
Packit Service 5bcba8
   0,   0,   0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,   0,   0,
Packit Service 5bcba8
   0,   0,   0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,  '-',  0,   0,
Packit Service 5bcba8
  '0', '1', '2', '3', '4', '5', '6', '7',  '8', '9',  0,   0,   0,   0,   0,   0,
Packit Service 5bcba8
  '-', 'a', 'b', 'c', 'd', 'e', 'f', 'g',  'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
Packit Service 5bcba8
  'p', 'q', 'r', 's', 't', 'u', 'v', 'w',  'x', 'y', 'z',  0,   0,   0,   0,  '-',
Packit Service 5bcba8
   0,  'a', 'b', 'c', 'd', 'e', 'f', 'g',  'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
Packit Service 5bcba8
  'p', 'q', 'r', 's', 't', 'u', 'v', 'w',  'x', 'y', 'z',  0,   0,   0,   0,   0
Packit Service 5bcba8
};
Packit Service 5bcba8
Packit Service 5bcba8
static bool
Packit Service 5bcba8
lang_equal (hb_language_t  v1,
Packit Service 5bcba8
	    const void    *v2)
Packit Service 5bcba8
{
Packit Service 5bcba8
  const unsigned char *p1 = (const unsigned char *) v1;
Packit Service 5bcba8
  const unsigned char *p2 = (const unsigned char *) v2;
Packit Service 5bcba8
Packit Service 5bcba8
  while (*p1 && *p1 == canon_map[*p2]) {
Packit Service 5bcba8
    p1++;
Packit Service 5bcba8
    p2++;
Packit Service 5bcba8
  }
Packit Service 5bcba8
Packit Service 5bcba8
  return *p1 == canon_map[*p2];
Packit Service 5bcba8
}
Packit Service 5bcba8
Packit Service 5bcba8
#if 0
Packit Service 5bcba8
static unsigned int
Packit Service 5bcba8
lang_hash (const void *key)
Packit Service 5bcba8
{
Packit Service 5bcba8
  const unsigned char *p = key;
Packit Service 5bcba8
  unsigned int h = 0;
Packit Service 5bcba8
  while (canon_map[*p])
Packit Service 5bcba8
    {
Packit Service 5bcba8
      h = (h << 5) - h + canon_map[*p];
Packit Service 5bcba8
      p++;
Packit Service 5bcba8
    }
Packit Service 5bcba8
Packit Service 5bcba8
  return h;
Packit Service 5bcba8
}
Packit Service 5bcba8
#endif
Packit Service 5bcba8
Packit Service 5bcba8
Packit Service 5bcba8
struct hb_language_item_t {
Packit Service 5bcba8
Packit Service 5bcba8
  struct hb_language_item_t *next;
Packit Service 5bcba8
  hb_language_t lang;
Packit Service 5bcba8
Packit Service 5bcba8
  inline bool operator == (const char *s) const {
Packit Service 5bcba8
    return lang_equal (lang, s);
Packit Service 5bcba8
  }
Packit Service 5bcba8
Packit Service 5bcba8
  inline hb_language_item_t & operator = (const char *s) {
Packit Service 5bcba8
    /* If a custom allocated is used calling strdup() pairs
Packit Service 5bcba8
    badly with a call to the custom free() in finish() below.
Packit Service 5bcba8
    Therefore don't call strdup(), implement its behavior.
Packit Service 5bcba8
    */
Packit Service 5bcba8
    size_t len = strlen(s) + 1;
Packit Service 5bcba8
    lang = (hb_language_t) malloc(len);
Packit Service 5bcba8
    if (likely (lang))
Packit Service 5bcba8
    {
Packit Service 5bcba8
      memcpy((unsigned char *) lang, s, len);
Packit Service 5bcba8
      for (unsigned char *p = (unsigned char *) lang; *p; p++)
Packit Service 5bcba8
	*p = canon_map[*p];
Packit Service 5bcba8
    }
Packit Service 5bcba8
Packit Service 5bcba8
    return *this;
Packit Service 5bcba8
  }
Packit Service 5bcba8
Packit Service 5bcba8
  void finish (void) { free ((void *) lang); }
Packit Service 5bcba8
};
Packit Service 5bcba8
Packit Service 5bcba8
Packit Service 5bcba8
/* Thread-safe lock-free language list */
Packit Service 5bcba8
Packit Service 5bcba8
static hb_language_item_t *langs;
Packit Service 5bcba8
Packit Service 5bcba8
#ifdef HB_USE_ATEXIT
Packit Service 5bcba8
static
Packit Service 5bcba8
void free_langs (void)
Packit Service 5bcba8
{
Packit Service 5bcba8
  while (langs) {
Packit Service 5bcba8
    hb_language_item_t *next = langs->next;
Packit Service 5bcba8
    langs->finish ();
Packit Service 5bcba8
    free (langs);
Packit Service 5bcba8
    langs = next;
Packit Service 5bcba8
  }
Packit Service 5bcba8
}
Packit Service 5bcba8
#endif
Packit Service 5bcba8
Packit Service 5bcba8
static hb_language_item_t *
Packit Service 5bcba8
lang_find_or_insert (const char *key)
Packit Service 5bcba8
{
Packit Service 5bcba8
retry:
Packit Service 5bcba8
  hb_language_item_t *first_lang = (hb_language_item_t *) hb_atomic_ptr_get (&langs;;
Packit Service 5bcba8
Packit Service 5bcba8
  for (hb_language_item_t *lang = first_lang; lang; lang = lang->next)
Packit Service 5bcba8
    if (*lang == key)
Packit Service 5bcba8
      return lang;
Packit Service 5bcba8
Packit Service 5bcba8
  /* Not found; allocate one. */
Packit Service 5bcba8
  hb_language_item_t *lang = (hb_language_item_t *) calloc (1, sizeof (hb_language_item_t));
Packit Service 5bcba8
  if (unlikely (!lang))
Packit Service 5bcba8
    return NULL;
Packit Service 5bcba8
  lang->next = first_lang;
Packit Service 5bcba8
  *lang = key;
Packit Service 5bcba8
  if (unlikely (!lang->lang))
Packit Service 5bcba8
  {
Packit Service 5bcba8
    free (lang);
Packit Service 5bcba8
    return NULL;
Packit Service 5bcba8
  }
Packit Service 5bcba8
Packit Service 5bcba8
  if (!hb_atomic_ptr_cmpexch (&langs, first_lang, lang)) {
Packit Service 5bcba8
    lang->finish ();
Packit Service 5bcba8
    free (lang);
Packit Service 5bcba8
    goto retry;
Packit Service 5bcba8
  }
Packit Service 5bcba8
Packit Service 5bcba8
#ifdef HB_USE_ATEXIT
Packit Service 5bcba8
  if (!first_lang)
Packit Service 5bcba8
    atexit (free_langs); /* First person registers atexit() callback. */
Packit Service 5bcba8
#endif
Packit Service 5bcba8
Packit Service 5bcba8
  return lang;
Packit Service 5bcba8
}
Packit Service 5bcba8
Packit Service 5bcba8
Packit Service 5bcba8
/**
Packit Service 5bcba8
 * hb_language_from_string:
Packit Service 5bcba8
 * @str: (array length=len) (element-type uint8_t): a string representing
Packit Service 5bcba8
 *       ISO 639 language code
Packit Service 5bcba8
 * @len: length of the @str, or -1 if it is %NULL-terminated.
Packit Service 5bcba8
 *
Packit Service 5bcba8
 * Converts @str representing an ISO 639 language code to the corresponding
Packit Service 5bcba8
 * #hb_language_t.
Packit Service 5bcba8
 *
Packit Service 5bcba8
 * Return value: (transfer none):
Packit Service 5bcba8
 * The #hb_language_t corresponding to the ISO 639 language code.
Packit Service 5bcba8
 *
Packit Service 5bcba8
 * Since: 0.9.2
Packit Service 5bcba8
 **/
Packit Service 5bcba8
hb_language_t
Packit Service 5bcba8
hb_language_from_string (const char *str, int len)
Packit Service 5bcba8
{
Packit Service 5bcba8
  if (!str || !len || !*str)
Packit Service 5bcba8
    return HB_LANGUAGE_INVALID;
Packit Service 5bcba8
Packit Service 5bcba8
  hb_language_item_t *item = NULL;
Packit Service 5bcba8
  if (len >= 0)
Packit Service 5bcba8
  {
Packit Service 5bcba8
    /* NUL-terminate it. */
Packit Service 5bcba8
    char strbuf[64];
Packit Service 5bcba8
    len = MIN (len, (int) sizeof (strbuf) - 1);
Packit Service 5bcba8
    memcpy (strbuf, str, len);
Packit Service 5bcba8
    strbuf[len] = '\0';
Packit Service 5bcba8
    item = lang_find_or_insert (strbuf);
Packit Service 5bcba8
  }
Packit Service 5bcba8
  else
Packit Service 5bcba8
    item = lang_find_or_insert (str);
Packit Service 5bcba8
Packit Service 5bcba8
  return likely (item) ? item->lang : HB_LANGUAGE_INVALID;
Packit Service 5bcba8
}
Packit Service 5bcba8
Packit Service 5bcba8
/**
Packit Service 5bcba8
 * hb_language_to_string:
Packit Service 5bcba8
 * @language: an #hb_language_t to convert.
Packit Service 5bcba8
 *
Packit Service 5bcba8
 * See hb_language_from_string().
Packit Service 5bcba8
 *
Packit Service 5bcba8
 * Return value: (transfer none):
Packit Service 5bcba8
 * A %NULL-terminated string representing the @language. Must not be freed by
Packit Service 5bcba8
 * the caller.
Packit Service 5bcba8
 *
Packit Service 5bcba8
 * Since: 0.9.2
Packit Service 5bcba8
 **/
Packit Service 5bcba8
const char *
Packit Service 5bcba8
hb_language_to_string (hb_language_t language)
Packit Service 5bcba8
{
Packit Service 5bcba8
  /* This is actually NULL-safe! */
Packit Service 5bcba8
  return language->s;
Packit Service 5bcba8
}
Packit Service 5bcba8
Packit Service 5bcba8
/**
Packit Service 5bcba8
 * hb_language_get_default:
Packit Service 5bcba8
 *
Packit Service 5bcba8
 * 
Packit Service 5bcba8
 *
Packit Service 5bcba8
 * Return value: (transfer none):
Packit Service 5bcba8
 *
Packit Service 5bcba8
 * Since: 0.9.2
Packit Service 5bcba8
 **/
Packit Service 5bcba8
hb_language_t
Packit Service 5bcba8
hb_language_get_default (void)
Packit Service 5bcba8
{
Packit Service 5bcba8
  static hb_language_t default_language = HB_LANGUAGE_INVALID;
Packit Service 5bcba8
Packit Service 5bcba8
  hb_language_t language = (hb_language_t) hb_atomic_ptr_get (&default_language);
Packit Service 5bcba8
  if (unlikely (language == HB_LANGUAGE_INVALID)) {
Packit Service 5bcba8
    language = hb_language_from_string (setlocale (LC_CTYPE, NULL), -1);
Packit Service 5bcba8
    (void) hb_atomic_ptr_cmpexch (&default_language, HB_LANGUAGE_INVALID, language);
Packit Service 5bcba8
  }
Packit Service 5bcba8
Packit Service 5bcba8
  return default_language;
Packit Service 5bcba8
}
Packit Service 5bcba8
Packit Service 5bcba8
Packit Service 5bcba8
/* hb_script_t */
Packit Service 5bcba8
Packit Service 5bcba8
/**
Packit Service 5bcba8
 * hb_script_from_iso15924_tag:
Packit Service 5bcba8
 * @tag: an #hb_tag_t representing an ISO 15924 tag.
Packit Service 5bcba8
 *
Packit Service 5bcba8
 * Converts an ISO 15924 script tag to a corresponding #hb_script_t.
Packit Service 5bcba8
 *
Packit Service 5bcba8
 * Return value: 
Packit Service 5bcba8
 * An #hb_script_t corresponding to the ISO 15924 tag.
Packit Service 5bcba8
 *
Packit Service 5bcba8
 * Since: 0.9.2
Packit Service 5bcba8
 **/
Packit Service 5bcba8
hb_script_t
Packit Service 5bcba8
hb_script_from_iso15924_tag (hb_tag_t tag)
Packit Service 5bcba8
{
Packit Service 5bcba8
  if (unlikely (tag == HB_TAG_NONE))
Packit Service 5bcba8
    return HB_SCRIPT_INVALID;
Packit Service 5bcba8
Packit Service 5bcba8
  /* Be lenient, adjust case (one capital letter followed by three small letters) */
Packit Service 5bcba8
  tag = (tag & 0xDFDFDFDFu) | 0x00202020u;
Packit Service 5bcba8
Packit Service 5bcba8
  switch (tag) {
Packit Service 5bcba8
Packit Service 5bcba8
    /* These graduated from the 'Q' private-area codes, but
Packit Service 5bcba8
     * the old code is still aliased by Unicode, and the Qaai
Packit Service 5bcba8
     * one in use by ICU. */
Packit Service 5bcba8
    case HB_TAG('Q','a','a','i'): return HB_SCRIPT_INHERITED;
Packit Service 5bcba8
    case HB_TAG('Q','a','a','c'): return HB_SCRIPT_COPTIC;
Packit Service 5bcba8
Packit Service 5bcba8
    /* Script variants from http://unicode.org/iso15924/ */
Packit Service 5bcba8
    case HB_TAG('C','y','r','s'): return HB_SCRIPT_CYRILLIC;
Packit Service 5bcba8
    case HB_TAG('L','a','t','f'): return HB_SCRIPT_LATIN;
Packit Service 5bcba8
    case HB_TAG('L','a','t','g'): return HB_SCRIPT_LATIN;
Packit Service 5bcba8
    case HB_TAG('S','y','r','e'): return HB_SCRIPT_SYRIAC;
Packit Service 5bcba8
    case HB_TAG('S','y','r','j'): return HB_SCRIPT_SYRIAC;
Packit Service 5bcba8
    case HB_TAG('S','y','r','n'): return HB_SCRIPT_SYRIAC;
Packit Service 5bcba8
  }
Packit Service 5bcba8
Packit Service 5bcba8
  /* If it looks right, just use the tag as a script */
Packit Service 5bcba8
  if (((uint32_t) tag & 0xE0E0E0E0u) == 0x40606060u)
Packit Service 5bcba8
    return (hb_script_t) tag;
Packit Service 5bcba8
Packit Service 5bcba8
  /* Otherwise, return unknown */
Packit Service 5bcba8
  return HB_SCRIPT_UNKNOWN;
Packit Service 5bcba8
}
Packit Service 5bcba8
Packit Service 5bcba8
/**
Packit Service 5bcba8
 * hb_script_from_string:
Packit Service 5bcba8
 * @str: (array length=len) (element-type uint8_t): a string representing an
Packit Service 5bcba8
 *       ISO 15924 tag.
Packit Service 5bcba8
 * @len: length of the @str, or -1 if it is %NULL-terminated.
Packit Service 5bcba8
 *
Packit Service 5bcba8
 * Converts a string @str representing an ISO 15924 script tag to a
Packit Service 5bcba8
 * corresponding #hb_script_t. Shorthand for hb_tag_from_string() then
Packit Service 5bcba8
 * hb_script_from_iso15924_tag().
Packit Service 5bcba8
 *
Packit Service 5bcba8
 * Return value: 
Packit Service 5bcba8
 * An #hb_script_t corresponding to the ISO 15924 tag.
Packit Service 5bcba8
 *
Packit Service 5bcba8
 * Since: 0.9.2
Packit Service 5bcba8
 **/
Packit Service 5bcba8
hb_script_t
Packit Service 5bcba8
hb_script_from_string (const char *str, int len)
Packit Service 5bcba8
{
Packit Service 5bcba8
  return hb_script_from_iso15924_tag (hb_tag_from_string (str, len));
Packit Service 5bcba8
}
Packit Service 5bcba8
Packit Service 5bcba8
/**
Packit Service 5bcba8
 * hb_script_to_iso15924_tag:
Packit Service 5bcba8
 * @script: an #hb_script_ to convert.
Packit Service 5bcba8
 *
Packit Service 5bcba8
 * See hb_script_from_iso15924_tag().
Packit Service 5bcba8
 *
Packit Service 5bcba8
 * Return value:
Packit Service 5bcba8
 * An #hb_tag_t representing an ISO 15924 script tag.
Packit Service 5bcba8
 *
Packit Service 5bcba8
 * Since: 0.9.2
Packit Service 5bcba8
 **/
Packit Service 5bcba8
hb_tag_t
Packit Service 5bcba8
hb_script_to_iso15924_tag (hb_script_t script)
Packit Service 5bcba8
{
Packit Service 5bcba8
  return (hb_tag_t) script;
Packit Service 5bcba8
}
Packit Service 5bcba8
Packit Service 5bcba8
/**
Packit Service 5bcba8
 * hb_script_get_horizontal_direction:
Packit Service 5bcba8
 * @script: 
Packit Service 5bcba8
 *
Packit Service 5bcba8
 * 
Packit Service 5bcba8
 *
Packit Service 5bcba8
 * Return value: 
Packit Service 5bcba8
 *
Packit Service 5bcba8
 * Since: 0.9.2
Packit Service 5bcba8
 **/
Packit Service 5bcba8
hb_direction_t
Packit Service 5bcba8
hb_script_get_horizontal_direction (hb_script_t script)
Packit Service 5bcba8
{
Packit Service 5bcba8
  /* http://goo.gl/x9ilM */
Packit Service 5bcba8
  switch ((hb_tag_t) script)
Packit Service 5bcba8
  {
Packit Service 5bcba8
    /* Unicode-1.1 additions */
Packit Service 5bcba8
    case HB_SCRIPT_ARABIC:
Packit Service 5bcba8
    case HB_SCRIPT_HEBREW:
Packit Service 5bcba8
Packit Service 5bcba8
    /* Unicode-3.0 additions */
Packit Service 5bcba8
    case HB_SCRIPT_SYRIAC:
Packit Service 5bcba8
    case HB_SCRIPT_THAANA:
Packit Service 5bcba8
Packit Service 5bcba8
    /* Unicode-4.0 additions */
Packit Service 5bcba8
    case HB_SCRIPT_CYPRIOT:
Packit Service 5bcba8
Packit Service 5bcba8
    /* Unicode-4.1 additions */
Packit Service 5bcba8
    case HB_SCRIPT_KHAROSHTHI:
Packit Service 5bcba8
Packit Service 5bcba8
    /* Unicode-5.0 additions */
Packit Service 5bcba8
    case HB_SCRIPT_PHOENICIAN:
Packit Service 5bcba8
    case HB_SCRIPT_NKO:
Packit Service 5bcba8
Packit Service 5bcba8
    /* Unicode-5.1 additions */
Packit Service 5bcba8
    case HB_SCRIPT_LYDIAN:
Packit Service 5bcba8
Packit Service 5bcba8
    /* Unicode-5.2 additions */
Packit Service 5bcba8
    case HB_SCRIPT_AVESTAN:
Packit Service 5bcba8
    case HB_SCRIPT_IMPERIAL_ARAMAIC:
Packit Service 5bcba8
    case HB_SCRIPT_INSCRIPTIONAL_PAHLAVI:
Packit Service 5bcba8
    case HB_SCRIPT_INSCRIPTIONAL_PARTHIAN:
Packit Service 5bcba8
    case HB_SCRIPT_OLD_SOUTH_ARABIAN:
Packit Service 5bcba8
    case HB_SCRIPT_OLD_TURKIC:
Packit Service 5bcba8
    case HB_SCRIPT_SAMARITAN:
Packit Service 5bcba8
Packit Service 5bcba8
    /* Unicode-6.0 additions */
Packit Service 5bcba8
    case HB_SCRIPT_MANDAIC:
Packit Service 5bcba8
Packit Service 5bcba8
    /* Unicode-6.1 additions */
Packit Service 5bcba8
    case HB_SCRIPT_MEROITIC_CURSIVE:
Packit Service 5bcba8
    case HB_SCRIPT_MEROITIC_HIEROGLYPHS:
Packit Service 5bcba8
Packit Service 5bcba8
    /* Unicode-7.0 additions */
Packit Service 5bcba8
    case HB_SCRIPT_MANICHAEAN:
Packit Service 5bcba8
    case HB_SCRIPT_MENDE_KIKAKUI:
Packit Service 5bcba8
    case HB_SCRIPT_NABATAEAN:
Packit Service 5bcba8
    case HB_SCRIPT_OLD_NORTH_ARABIAN:
Packit Service 5bcba8
    case HB_SCRIPT_PALMYRENE:
Packit Service 5bcba8
    case HB_SCRIPT_PSALTER_PAHLAVI:
Packit Service 5bcba8
Packit Service 5bcba8
    /* Unicode-8.0 additions */
Packit Service 5bcba8
    case HB_SCRIPT_OLD_HUNGARIAN:
Packit Service 5bcba8
Packit Service 5bcba8
    /* Unicode-9.0 additions */
Packit Service 5bcba8
    case HB_SCRIPT_ADLAM:
Packit Service 5bcba8
Packit Service 5bcba8
      return HB_DIRECTION_RTL;
Packit Service 5bcba8
  }
Packit Service 5bcba8
Packit Service 5bcba8
  return HB_DIRECTION_LTR;
Packit Service 5bcba8
}
Packit Service 5bcba8
Packit Service 5bcba8
Packit Service 5bcba8
/* hb_user_data_array_t */
Packit Service 5bcba8
Packit Service 5bcba8
bool
Packit Service 5bcba8
hb_user_data_array_t::set (hb_user_data_key_t *key,
Packit Service 5bcba8
			   void *              data,
Packit Service 5bcba8
			   hb_destroy_func_t   destroy,
Packit Service 5bcba8
			   hb_bool_t           replace)
Packit Service 5bcba8
{
Packit Service 5bcba8
  if (!key)
Packit Service 5bcba8
    return false;
Packit Service 5bcba8
Packit Service 5bcba8
  if (replace) {
Packit Service 5bcba8
    if (!data && !destroy) {
Packit Service 5bcba8
      items.remove (key, lock);
Packit Service 5bcba8
      return true;
Packit Service 5bcba8
    }
Packit Service 5bcba8
  }
Packit Service 5bcba8
  hb_user_data_item_t item = {key, data, destroy};
Packit Service 5bcba8
  bool ret = !!items.replace_or_insert (item, lock, (bool) replace);
Packit Service 5bcba8
Packit Service 5bcba8
  return ret;
Packit Service 5bcba8
}
Packit Service 5bcba8
Packit Service 5bcba8
void *
Packit Service 5bcba8
hb_user_data_array_t::get (hb_user_data_key_t *key)
Packit Service 5bcba8
{
Packit Service 5bcba8
  hb_user_data_item_t item = {NULL, NULL, NULL};
Packit Service 5bcba8
Packit Service 5bcba8
  return items.find (key, &item, lock) ? item.data : NULL;
Packit Service 5bcba8
}
Packit Service 5bcba8
Packit Service 5bcba8
Packit Service 5bcba8
/* hb_version */
Packit Service 5bcba8
Packit Service 5bcba8
/**
Packit Service 5bcba8
 * hb_version:
Packit Service 5bcba8
 * @major: (out): Library major version component.
Packit Service 5bcba8
 * @minor: (out): Library minor version component.
Packit Service 5bcba8
 * @micro: (out): Library micro version component.
Packit Service 5bcba8
 *
Packit Service 5bcba8
 * Returns library version as three integer components.
Packit Service 5bcba8
 *
Packit Service 5bcba8
 * Since: 0.9.2
Packit Service 5bcba8
 **/
Packit Service 5bcba8
void
Packit Service 5bcba8
hb_version (unsigned int *major,
Packit Service 5bcba8
	    unsigned int *minor,
Packit Service 5bcba8
	    unsigned int *micro)
Packit Service 5bcba8
{
Packit Service 5bcba8
  *major = HB_VERSION_MAJOR;
Packit Service 5bcba8
  *minor = HB_VERSION_MINOR;
Packit Service 5bcba8
  *micro = HB_VERSION_MICRO;
Packit Service 5bcba8
}
Packit Service 5bcba8
Packit Service 5bcba8
/**
Packit Service 5bcba8
 * hb_version_string:
Packit Service 5bcba8
 *
Packit Service 5bcba8
 * Returns library version as a string with three components.
Packit Service 5bcba8
 *
Packit Service 5bcba8
 * Return value: library version string.
Packit Service 5bcba8
 *
Packit Service 5bcba8
 * Since: 0.9.2
Packit Service 5bcba8
 **/
Packit Service 5bcba8
const char *
Packit Service 5bcba8
hb_version_string (void)
Packit Service 5bcba8
{
Packit Service 5bcba8
  return HB_VERSION_STRING;
Packit Service 5bcba8
}
Packit Service 5bcba8
Packit Service 5bcba8
/**
Packit Service 5bcba8
 * hb_version_atleast:
Packit Service 5bcba8
 * @major: 
Packit Service 5bcba8
 * @minor: 
Packit Service 5bcba8
 * @micro: 
Packit Service 5bcba8
 *
Packit Service 5bcba8
 * 
Packit Service 5bcba8
 *
Packit Service 5bcba8
 * Return value: 
Packit Service 5bcba8
 *
Packit Service 5bcba8
 * Since: 0.9.30
Packit Service 5bcba8
 **/
Packit Service 5bcba8
hb_bool_t
Packit Service 5bcba8
hb_version_atleast (unsigned int major,
Packit Service 5bcba8
		    unsigned int minor,
Packit Service 5bcba8
		    unsigned int micro)
Packit Service 5bcba8
{
Packit Service 5bcba8
  return HB_VERSION_ATLEAST (major, minor, micro);
Packit Service 5bcba8
}
Packit Service 5bcba8
Packit Service 5bcba8
Packit Service 5bcba8
Packit Service 5bcba8
/* hb_feature_t and hb_variation_t */
Packit Service 5bcba8
Packit Service 5bcba8
static bool
Packit Service 5bcba8
parse_space (const char **pp, const char *end)
Packit Service 5bcba8
{
Packit Service 5bcba8
  while (*pp < end && ISSPACE (**pp))
Packit Service 5bcba8
    (*pp)++;
Packit Service 5bcba8
  return true;
Packit Service 5bcba8
}
Packit Service 5bcba8
Packit Service 5bcba8
static bool
Packit Service 5bcba8
parse_char (const char **pp, const char *end, char c)
Packit Service 5bcba8
{
Packit Service 5bcba8
  parse_space (pp, end);
Packit Service 5bcba8
Packit Service 5bcba8
  if (*pp == end || **pp != c)
Packit Service 5bcba8
    return false;
Packit Service 5bcba8
Packit Service 5bcba8
  (*pp)++;
Packit Service 5bcba8
  return true;
Packit Service 5bcba8
}
Packit Service 5bcba8
Packit Service 5bcba8
static bool
Packit Service 5bcba8
parse_uint (const char **pp, const char *end, unsigned int *pv)
Packit Service 5bcba8
{
Packit Service 5bcba8
  char buf[32];
Packit Service 5bcba8
  unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp));
Packit Service 5bcba8
  strncpy (buf, *pp, len);
Packit Service 5bcba8
  buf[len] = '\0';
Packit Service 5bcba8
Packit Service 5bcba8
  char *p = buf;
Packit Service 5bcba8
  char *pend = p;
Packit Service 5bcba8
  unsigned int v;
Packit Service 5bcba8
Packit Service 5bcba8
  /* Intentionally use strtol instead of strtoul, such that
Packit Service 5bcba8
   * -1 turns into "big number"... */
Packit Service 5bcba8
  errno = 0;
Packit Service 5bcba8
  v = strtol (p, &pend, 0);
Packit Service 5bcba8
  if (errno || p == pend)
Packit Service 5bcba8
    return false;
Packit Service 5bcba8
Packit Service 5bcba8
  *pv = v;
Packit Service 5bcba8
  *pp += pend - p;
Packit Service 5bcba8
  return true;
Packit Service 5bcba8
}
Packit Service 5bcba8
Packit Service 5bcba8
static bool
Packit Service 5bcba8
parse_uint32 (const char **pp, const char *end, uint32_t *pv)
Packit Service 5bcba8
{
Packit Service 5bcba8
  char buf[32];
Packit Service 5bcba8
  unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp));
Packit Service 5bcba8
  strncpy (buf, *pp, len);
Packit Service 5bcba8
  buf[len] = '\0';
Packit Service 5bcba8
Packit Service 5bcba8
  char *p = buf;
Packit Service 5bcba8
  char *pend = p;
Packit Service 5bcba8
  unsigned int v;
Packit Service 5bcba8
Packit Service 5bcba8
  /* Intentionally use strtol instead of strtoul, such that
Packit Service 5bcba8
   * -1 turns into "big number"... */
Packit Service 5bcba8
  errno = 0;
Packit Service 5bcba8
  v = strtol (p, &pend, 0);
Packit Service 5bcba8
  if (errno || p == pend)
Packit Service 5bcba8
    return false;
Packit Service 5bcba8
Packit Service 5bcba8
  *pv = v;
Packit Service 5bcba8
  *pp += pend - p;
Packit Service 5bcba8
  return true;
Packit Service 5bcba8
}
Packit Service 5bcba8
Packit Service 5bcba8
static bool
Packit Service 5bcba8
parse_float (const char **pp, const char *end, float *pv)
Packit Service 5bcba8
{
Packit Service 5bcba8
  char buf[32];
Packit Service 5bcba8
  unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp));
Packit Service 5bcba8
  strncpy (buf, *pp, len);
Packit Service 5bcba8
  buf[len] = '\0';
Packit Service 5bcba8
Packit Service 5bcba8
  char *p = buf;
Packit Service 5bcba8
  char *pend = p;
Packit Service 5bcba8
  float v;
Packit Service 5bcba8
Packit Service 5bcba8
  errno = 0;
Packit Service 5bcba8
  v = strtod (p, &pend);
Packit Service 5bcba8
  if (errno || p == pend)
Packit Service 5bcba8
    return false;
Packit Service 5bcba8
Packit Service 5bcba8
  *pv = v;
Packit Service 5bcba8
  *pp += pend - p;
Packit Service 5bcba8
  return true;
Packit Service 5bcba8
}
Packit Service 5bcba8
Packit Service 5bcba8
static bool
Packit Service 5bcba8
parse_bool (const char **pp, const char *end, uint32_t *pv)
Packit Service 5bcba8
{
Packit Service 5bcba8
  parse_space (pp, end);
Packit Service 5bcba8
Packit Service 5bcba8
  const char *p = *pp;
Packit Service 5bcba8
  while (*pp < end && ISALPHA(**pp))
Packit Service 5bcba8
    (*pp)++;
Packit Service 5bcba8
Packit Service 5bcba8
  /* CSS allows on/off as aliases 1/0. */
Packit Service 5bcba8
  if (*pp - p == 2 || 0 == strncmp (p, "on", 2))
Packit Service 5bcba8
    *pv = 1;
Packit Service 5bcba8
  else if (*pp - p == 3 || 0 == strncmp (p, "off", 2))
Packit Service 5bcba8
    *pv = 0;
Packit Service 5bcba8
  else
Packit Service 5bcba8
    return false;
Packit Service 5bcba8
Packit Service 5bcba8
  return true;
Packit Service 5bcba8
}
Packit Service 5bcba8
Packit Service 5bcba8
/* hb_feature_t */
Packit Service 5bcba8
Packit Service 5bcba8
static bool
Packit Service 5bcba8
parse_feature_value_prefix (const char **pp, const char *end, hb_feature_t *feature)
Packit Service 5bcba8
{
Packit Service 5bcba8
  if (parse_char (pp, end, '-'))
Packit Service 5bcba8
    feature->value = 0;
Packit Service 5bcba8
  else {
Packit Service 5bcba8
    parse_char (pp, end, '+');
Packit Service 5bcba8
    feature->value = 1;
Packit Service 5bcba8
  }
Packit Service 5bcba8
Packit Service 5bcba8
  return true;
Packit Service 5bcba8
}
Packit Service 5bcba8
Packit Service 5bcba8
static bool
Packit Service 5bcba8
parse_tag (const char **pp, const char *end, hb_tag_t *tag)
Packit Service 5bcba8
{
Packit Service 5bcba8
  parse_space (pp, end);
Packit Service 5bcba8
Packit Service 5bcba8
  char quote = 0;
Packit Service 5bcba8
Packit Service 5bcba8
  if (*pp < end && (**pp == '\'' || **pp == '"'))
Packit Service 5bcba8
  {
Packit Service 5bcba8
    quote = **pp;
Packit Service 5bcba8
    (*pp)++;
Packit Service 5bcba8
  }
Packit Service 5bcba8
Packit Service 5bcba8
  const char *p = *pp;
Packit Service 5bcba8
  while (*pp < end && ISALNUM(**pp))
Packit Service 5bcba8
    (*pp)++;
Packit Service 5bcba8
Packit Service 5bcba8
  if (p == *pp || *pp - p > 4)
Packit Service 5bcba8
    return false;
Packit Service 5bcba8
Packit Service 5bcba8
  *tag = hb_tag_from_string (p, *pp - p);
Packit Service 5bcba8
Packit Service 5bcba8
  if (quote)
Packit Service 5bcba8
  {
Packit Service 5bcba8
    /* CSS expects exactly four bytes.  And we only allow quotations for
Packit Service 5bcba8
     * CSS compatibility.  So, enforce the length. */
Packit Service 5bcba8
     if (*pp - p != 4)
Packit Service 5bcba8
       return false;
Packit Service 5bcba8
    if (*pp == end || **pp != quote)
Packit Service 5bcba8
      return false;
Packit Service 5bcba8
    (*pp)++;
Packit Service 5bcba8
  }
Packit Service 5bcba8
Packit Service 5bcba8
  return true;
Packit Service 5bcba8
}
Packit Service 5bcba8
Packit Service 5bcba8
static bool
Packit Service 5bcba8
parse_feature_indices (const char **pp, const char *end, hb_feature_t *feature)
Packit Service 5bcba8
{
Packit Service 5bcba8
  parse_space (pp, end);
Packit Service 5bcba8
Packit Service 5bcba8
  bool has_start;
Packit Service 5bcba8
Packit Service 5bcba8
  feature->start = 0;
Packit Service 5bcba8
  feature->end = (unsigned int) -1;
Packit Service 5bcba8
Packit Service 5bcba8
  if (!parse_char (pp, end, '['))
Packit Service 5bcba8
    return true;
Packit Service 5bcba8
Packit Service 5bcba8
  has_start = parse_uint (pp, end, &feature->start);
Packit Service 5bcba8
Packit Service 5bcba8
  if (parse_char (pp, end, ':')) {
Packit Service 5bcba8
    parse_uint (pp, end, &feature->end);
Packit Service 5bcba8
  } else {
Packit Service 5bcba8
    if (has_start)
Packit Service 5bcba8
      feature->end = feature->start + 1;
Packit Service 5bcba8
  }
Packit Service 5bcba8
Packit Service 5bcba8
  return parse_char (pp, end, ']');
Packit Service 5bcba8
}
Packit Service 5bcba8
Packit Service 5bcba8
static bool
Packit Service 5bcba8
parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *feature)
Packit Service 5bcba8
{
Packit Service 5bcba8
  bool had_equal = parse_char (pp, end, '=');
Packit Service 5bcba8
  bool had_value = parse_uint32 (pp, end, &feature->value) ||
Packit Service 5bcba8
                   parse_bool (pp, end, &feature->value);
Packit Service 5bcba8
  /* CSS doesn't use equal-sign between tag and value.
Packit Service 5bcba8
   * If there was an equal-sign, then there *must* be a value.
Packit Service 5bcba8
   * A value without an eqaul-sign is ok, but not required. */
Packit Service 5bcba8
  return !had_equal || had_value;
Packit Service 5bcba8
}
Packit Service 5bcba8
Packit Service 5bcba8
static bool
Packit Service 5bcba8
parse_one_feature (const char **pp, const char *end, hb_feature_t *feature)
Packit Service 5bcba8
{
Packit Service 5bcba8
  return parse_feature_value_prefix (pp, end, feature) &&
Packit Service 5bcba8
	 parse_tag (pp, end, &feature->tag) &&
Packit Service 5bcba8
	 parse_feature_indices (pp, end, feature) &&
Packit Service 5bcba8
	 parse_feature_value_postfix (pp, end, feature) &&
Packit Service 5bcba8
	 parse_space (pp, end) &&
Packit Service 5bcba8
	 *pp == end;
Packit Service 5bcba8
}
Packit Service 5bcba8
Packit Service 5bcba8
/**
Packit Service 5bcba8
 * hb_feature_from_string:
Packit Service 5bcba8
 * @str: (array length=len) (element-type uint8_t): a string to parse
Packit Service 5bcba8
 * @len: length of @str, or -1 if string is %NULL terminated
Packit Service 5bcba8
 * @feature: (out): the #hb_feature_t to initialize with the parsed values
Packit Service 5bcba8
 *
Packit Service 5bcba8
 * Parses a string into a #hb_feature_t.
Packit Service 5bcba8
 *
Packit Service 5bcba8
 * TODO: document the syntax here.
Packit Service 5bcba8
 *
Packit Service 5bcba8
 * Return value:
Packit Service 5bcba8
 * %true if @str is successfully parsed, %false otherwise.
Packit Service 5bcba8
 *
Packit Service 5bcba8
 * Since: 0.9.5
Packit Service 5bcba8
 **/
Packit Service 5bcba8
hb_bool_t
Packit Service 5bcba8
hb_feature_from_string (const char *str, int len,
Packit Service 5bcba8
			hb_feature_t *feature)
Packit Service 5bcba8
{
Packit Service 5bcba8
  hb_feature_t feat;
Packit Service 5bcba8
Packit Service 5bcba8
  if (len < 0)
Packit Service 5bcba8
    len = strlen (str);
Packit Service 5bcba8
Packit Service 5bcba8
  if (likely (parse_one_feature (&str, str + len, &feat)))
Packit Service 5bcba8
  {
Packit Service 5bcba8
    if (feature)
Packit Service 5bcba8
      *feature = feat;
Packit Service 5bcba8
    return true;
Packit Service 5bcba8
  }
Packit Service 5bcba8
Packit Service 5bcba8
  if (feature)
Packit Service 5bcba8
    memset (feature, 0, sizeof (*feature));
Packit Service 5bcba8
  return false;
Packit Service 5bcba8
}
Packit Service 5bcba8
Packit Service 5bcba8
/**
Packit Service 5bcba8
 * hb_feature_to_string:
Packit Service 5bcba8
 * @feature: an #hb_feature_t to convert
Packit Service 5bcba8
 * @buf: (array length=size) (out): output string
Packit Service 5bcba8
 * @size: the allocated size of @buf
Packit Service 5bcba8
 *
Packit Service 5bcba8
 * Converts a #hb_feature_t into a %NULL-terminated string in the format
Packit Service 5bcba8
 * understood by hb_feature_from_string(). The client in responsible for
Packit Service 5bcba8
 * allocating big enough size for @buf, 128 bytes is more than enough.
Packit Service 5bcba8
 *
Packit Service 5bcba8
 * Since: 0.9.5
Packit Service 5bcba8
 **/
Packit Service 5bcba8
void
Packit Service 5bcba8
hb_feature_to_string (hb_feature_t *feature,
Packit Service 5bcba8
		      char *buf, unsigned int size)
Packit Service 5bcba8
{
Packit Service 5bcba8
  if (unlikely (!size)) return;
Packit Service 5bcba8
Packit Service 5bcba8
  char s[128];
Packit Service 5bcba8
  unsigned int len = 0;
Packit Service 5bcba8
  if (feature->value == 0)
Packit Service 5bcba8
    s[len++] = '-';
Packit Service 5bcba8
  hb_tag_to_string (feature->tag, s + len);
Packit Service 5bcba8
  len += 4;
Packit Service 5bcba8
  while (len && s[len - 1] == ' ')
Packit Service 5bcba8
    len--;
Packit Service 5bcba8
  if (feature->start != 0 || feature->end != (unsigned int) -1)
Packit Service 5bcba8
  {
Packit Service 5bcba8
    s[len++] = '[';
Packit Service 5bcba8
    if (feature->start)
Packit Service 5bcba8
      len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->start));
Packit Service 5bcba8
    if (feature->end != feature->start + 1) {
Packit Service 5bcba8
      s[len++] = ':';
Packit Service 5bcba8
      if (feature->end != (unsigned int) -1)
Packit Service 5bcba8
	len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->end));
Packit Service 5bcba8
    }
Packit Service 5bcba8
    s[len++] = ']';
Packit Service 5bcba8
  }
Packit Service 5bcba8
  if (feature->value > 1)
Packit Service 5bcba8
  {
Packit Service 5bcba8
    s[len++] = '=';
Packit Service 5bcba8
    len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value));
Packit Service 5bcba8
  }
Packit Service 5bcba8
  assert (len < ARRAY_LENGTH (s));
Packit Service 5bcba8
  len = MIN (len, size - 1);
Packit Service 5bcba8
  memcpy (buf, s, len);
Packit Service 5bcba8
  buf[len] = '\0';
Packit Service 5bcba8
}
Packit Service 5bcba8
Packit Service 5bcba8
/* hb_variation_t */
Packit Service 5bcba8
Packit Service 5bcba8
static bool
Packit Service 5bcba8
parse_variation_value (const char **pp, const char *end, hb_variation_t *variation)
Packit Service 5bcba8
{
Packit Service 5bcba8
  parse_char (pp, end, '='); /* Optional. */
Packit Service 5bcba8
  return parse_float (pp, end, &variation->value);
Packit Service 5bcba8
}
Packit Service 5bcba8
Packit Service 5bcba8
static bool
Packit Service 5bcba8
parse_one_variation (const char **pp, const char *end, hb_variation_t *variation)
Packit Service 5bcba8
{
Packit Service 5bcba8
  return parse_tag (pp, end, &variation->tag) &&
Packit Service 5bcba8
	 parse_variation_value (pp, end, variation) &&
Packit Service 5bcba8
	 parse_space (pp, end) &&
Packit Service 5bcba8
	 *pp == end;
Packit Service 5bcba8
}
Packit Service 5bcba8
Packit Service 5bcba8
/**
Packit Service 5bcba8
 * hb_variation_from_string:
Packit Service 5bcba8
 *
Packit Service 5bcba8
 * Since: 1.4.2
Packit Service 5bcba8
 */
Packit Service 5bcba8
hb_bool_t
Packit Service 5bcba8
hb_variation_from_string (const char *str, int len,
Packit Service 5bcba8
			  hb_variation_t *variation)
Packit Service 5bcba8
{
Packit Service 5bcba8
  hb_variation_t var;
Packit Service 5bcba8
Packit Service 5bcba8
  if (len < 0)
Packit Service 5bcba8
    len = strlen (str);
Packit Service 5bcba8
Packit Service 5bcba8
  if (likely (parse_one_variation (&str, str + len, &var)))
Packit Service 5bcba8
  {
Packit Service 5bcba8
    if (variation)
Packit Service 5bcba8
      *variation = var;
Packit Service 5bcba8
    return true;
Packit Service 5bcba8
  }
Packit Service 5bcba8
Packit Service 5bcba8
  if (variation)
Packit Service 5bcba8
    memset (variation, 0, sizeof (*variation));
Packit Service 5bcba8
  return false;
Packit Service 5bcba8
}
Packit Service 5bcba8
Packit Service 5bcba8
/**
Packit Service 5bcba8
 * hb_variation_to_string:
Packit Service 5bcba8
 *
Packit Service 5bcba8
 * Since: 1.4.2
Packit Service 5bcba8
 */
Packit Service 5bcba8
void
Packit Service 5bcba8
hb_variation_to_string (hb_variation_t *variation,
Packit Service 5bcba8
			char *buf, unsigned int size)
Packit Service 5bcba8
{
Packit Service 5bcba8
  if (unlikely (!size)) return;
Packit Service 5bcba8
Packit Service 5bcba8
  char s[128];
Packit Service 5bcba8
  unsigned int len = 0;
Packit Service 5bcba8
  hb_tag_to_string (variation->tag, s + len);
Packit Service 5bcba8
  len += 4;
Packit Service 5bcba8
  while (len && s[len - 1] == ' ')
Packit Service 5bcba8
    len--;
Packit Service 5bcba8
  s[len++] = '=';
Packit Service 5bcba8
  len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", variation->value));
Packit Service 5bcba8
Packit Service 5bcba8
  assert (len < ARRAY_LENGTH (s));
Packit Service 5bcba8
  len = MIN (len, size - 1);
Packit Service 5bcba8
  memcpy (buf, s, len);
Packit Service 5bcba8
  buf[len] = '\0';
Packit Service 5bcba8
}