Blame src/hb-common.cc

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