< prev index next >

src/java.desktop/share/native/libfontmanager/harfbuzz/hb-common.cc

Print this page




  15  * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
  16  * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
  17  * DAMAGE.
  18  *
  19  * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
  20  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  21  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
  22  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
  23  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  24  *
  25  * Red Hat Author(s): Behdad Esfahbod
  26  * Google Author(s): Behdad Esfahbod
  27  */
  28 
  29 #include "hb-private.hh"
  30 
  31 #include "hb-mutex-private.hh"
  32 #include "hb-object-private.hh"
  33 
  34 #include <locale.h>



  35 
  36 
  37 /* hb_options_t */
  38 
  39 hb_options_union_t _hb_options;
  40 
  41 void
  42 _hb_options_init (void)
  43 {
  44   hb_options_union_t u;
  45   u.i = 0;
  46   u.opts.initialized = 1;
  47 
  48   char *c = getenv ("HB_OPTIONS");
  49   u.opts.uniscribe_bug_compatible = c && strstr (c, "uniscribe-bug-compatible");
  50 
  51   /* This is idempotent and threadsafe. */
  52   _hb_options = u;
  53 }
  54 


  65  * Return value: 
  66  *
  67  * Since: 0.9.2
  68  **/
  69 hb_tag_t
  70 hb_tag_from_string (const char *str, int len)
  71 {
  72   char tag[4];
  73   unsigned int i;
  74 
  75   if (!str || !len || !*str)
  76     return HB_TAG_NONE;
  77 
  78   if (len < 0 || len > 4)
  79     len = 4;
  80   for (i = 0; i < (unsigned) len && str[i]; i++)
  81     tag[i] = str[i];
  82   for (; i < 4; i++)
  83     tag[i] = ' ';
  84 
  85   return HB_TAG_CHAR4 (tag);
  86 }
  87 
  88 /**
  89  * hb_tag_to_string:
  90  * @tag: 
  91  * @buf: (out caller-allocates) (array fixed-size=4) (element-type uint8_t): 
  92  *
  93  * 
  94  *
  95  * Since: 0.9.5
  96  **/
  97 void
  98 hb_tag_to_string (hb_tag_t tag, char *buf)
  99 {
 100   buf[0] = (char) (uint8_t) (tag >> 24);
 101   buf[1] = (char) (uint8_t) (tag >> 16);
 102   buf[2] = (char) (uint8_t) (tag >>  8);
 103   buf[3] = (char) (uint8_t) (tag >>  0);
 104 }
 105 


 169 };
 170 
 171 static const char canon_map[256] = {
 172    0,   0,   0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,   0,   0,
 173    0,   0,   0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,   0,   0,
 174    0,   0,   0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,  '-',  0,   0,
 175   '0', '1', '2', '3', '4', '5', '6', '7',  '8', '9',  0,   0,   0,   0,   0,   0,
 176   '-', 'a', 'b', 'c', 'd', 'e', 'f', 'g',  'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
 177   'p', 'q', 'r', 's', 't', 'u', 'v', 'w',  'x', 'y', 'z',  0,   0,   0,   0,  '-',
 178    0,  'a', 'b', 'c', 'd', 'e', 'f', 'g',  'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
 179   'p', 'q', 'r', 's', 't', 'u', 'v', 'w',  'x', 'y', 'z',  0,   0,   0,   0,   0
 180 };
 181 
 182 static bool
 183 lang_equal (hb_language_t  v1,
 184             const void    *v2)
 185 {
 186   const unsigned char *p1 = (const unsigned char *) v1;
 187   const unsigned char *p2 = (const unsigned char *) v2;
 188 
 189   while (*p1 && *p1 == canon_map[*p2])
 190     p1++, p2++;


 191 
 192   return *p1 == canon_map[*p2];
 193 }
 194 
 195 #if 0
 196 static unsigned int
 197 lang_hash (const void *key)
 198 {
 199   const unsigned char *p = key;
 200   unsigned int h = 0;
 201   while (canon_map[*p])
 202     {
 203       h = (h << 5) - h + canon_map[*p];
 204       p++;
 205     }
 206 
 207   return h;
 208 }
 209 #endif
 210 
 211 
 212 struct hb_language_item_t {
 213 
 214   struct hb_language_item_t *next;
 215   hb_language_t lang;
 216 
 217   inline bool operator == (const char *s) const {
 218     return lang_equal (lang, s);
 219   }
 220 
 221   inline hb_language_item_t & operator = (const char *s) {
 222     lang = (hb_language_t) strdup (s);








 223     for (unsigned char *p = (unsigned char *) lang; *p; p++)
 224       *p = canon_map[*p];

 225 
 226     return *this;
 227   }
 228 
 229   void finish (void) { free ((void *) lang); }
 230 };
 231 
 232 
 233 /* Thread-safe lock-free language list */
 234 
 235 static hb_language_item_t *langs;
 236 
 237 #ifdef HB_USE_ATEXIT
 238 static
 239 void free_langs (void)
 240 {
 241   while (langs) {
 242     hb_language_item_t *next = langs->next;
 243     langs->finish ();
 244     free (langs);
 245     langs = next;
 246   }
 247 }
 248 #endif
 249 
 250 static hb_language_item_t *
 251 lang_find_or_insert (const char *key)
 252 {
 253 retry:
 254   hb_language_item_t *first_lang = (hb_language_item_t *) hb_atomic_ptr_get (&langs);
 255 
 256   for (hb_language_item_t *lang = first_lang; lang; lang = lang->next)
 257     if (*lang == key)
 258       return lang;
 259 
 260   /* Not found; allocate one. */
 261   hb_language_item_t *lang = (hb_language_item_t *) calloc (1, sizeof (hb_language_item_t));
 262   if (unlikely (!lang))
 263     return NULL;
 264   lang->next = first_lang;
 265   *lang = key;





 266 
 267   if (!hb_atomic_ptr_cmpexch (&langs, first_lang, lang)) {
 268     lang->finish ();
 269     free (lang);
 270     goto retry;
 271   }
 272 
 273 #ifdef HB_USE_ATEXIT
 274   if (!first_lang)
 275     atexit (free_langs); /* First person registers atexit() callback. */
 276 #endif
 277 
 278   return lang;
 279 }
 280 
 281 
 282 /**
 283  * hb_language_from_string:
 284  * @str: (array length=len) (element-type uint8_t): a string representing
 285  *       ISO 639 language code
 286  * @len: length of the @str, or -1 if it is %NULL-terminated.
 287  *
 288  * Converts @str representing an ISO 639 language code to the corresponding
 289  * #hb_language_t.
 290  *
 291  * Return value: (transfer none):
 292  * The #hb_language_t corresponding to the ISO 639 language code.
 293  *
 294  * Since: 0.9.2
 295  **/
 296 hb_language_t
 297 hb_language_from_string (const char *str, int len)
 298 {
 299   if (!str || !len || !*str)
 300     return HB_LANGUAGE_INVALID;
 301 
 302   hb_language_item_t *item = NULL;
 303   if (len >= 0)
 304   {
 305     /* NUL-terminate it. */
 306     char strbuf[64];
 307     len = MIN (len, (int) sizeof (strbuf) - 1);
 308     memcpy (strbuf, str, len);
 309     strbuf[len] = '\0';
 310     item = lang_find_or_insert (strbuf);
 311   }
 312   else
 313     item = lang_find_or_insert (str);
 314 
 315   return likely (item) ? item->lang : HB_LANGUAGE_INVALID;
 316 }
 317 
 318 /**
 319  * hb_language_to_string:
 320  * @language: an #hb_language_t to convert.
 321  *
 322  * See hb_language_from_string().
 323  *
 324  * Return value: (transfer none):
 325  * A %NULL-terminated string representing the @language. Must not be freed by
 326  * the caller.
 327  *
 328  * Since: 0.9.2
 329  **/
 330 const char *
 331 hb_language_to_string (hb_language_t language)
 332 {
 333   /* This is actually NULL-safe! */
 334   return language->s;
 335 }
 336 
 337 /**
 338  * hb_language_get_default:
 339  *
 340  * 
 341  *
 342  * Return value: (transfer none):
 343  *
 344  * Since: 0.9.2
 345  **/
 346 hb_language_t
 347 hb_language_get_default (void)
 348 {
 349   static hb_language_t default_language = HB_LANGUAGE_INVALID;
 350 
 351   hb_language_t language = (hb_language_t) hb_atomic_ptr_get (&default_language);
 352   if (unlikely (language == HB_LANGUAGE_INVALID)) {
 353     language = hb_language_from_string (setlocale (LC_CTYPE, NULL), -1);
 354     (void) hb_atomic_ptr_cmpexch (&default_language, HB_LANGUAGE_INVALID, language);
 355   }
 356 
 357   return default_language;
 358 }
 359 
 360 
 361 /* hb_script_t */
 362 
 363 /**
 364  * hb_script_from_iso15924_tag:
 365  * @tag: an #hb_tag_t representing an ISO 15924 tag.
 366  *
 367  * Converts an ISO 15924 script tag to a corresponding #hb_script_t.
 368  *
 369  * Return value: 
 370  * An #hb_script_t corresponding to the ISO 15924 tag.
 371  *
 372  * Since: 0.9.2
 373  **/


 526                            hb_bool_t           replace)
 527 {
 528   if (!key)
 529     return false;
 530 
 531   if (replace) {
 532     if (!data && !destroy) {
 533       items.remove (key, lock);
 534       return true;
 535     }
 536   }
 537   hb_user_data_item_t item = {key, data, destroy};
 538   bool ret = !!items.replace_or_insert (item, lock, (bool) replace);
 539 
 540   return ret;
 541 }
 542 
 543 void *
 544 hb_user_data_array_t::get (hb_user_data_key_t *key)
 545 {
 546   hb_user_data_item_t item = {NULL, NULL, NULL};
 547 
 548   return items.find (key, &item, lock) ? item.data : NULL;
 549 }
 550 
 551 
 552 /* hb_version */
 553 
 554 /**
 555  * hb_version:
 556  * @major: (out): Library major version component.
 557  * @minor: (out): Library minor version component.
 558  * @micro: (out): Library micro version component.
 559  *
 560  * Returns library version as three integer components.
 561  *
 562  * Since: 0.9.2
 563  **/
 564 void
 565 hb_version (unsigned int *major,
 566             unsigned int *minor,
 567             unsigned int *micro)
 568 {


 587 }
 588 
 589 /**
 590  * hb_version_atleast:
 591  * @major: 
 592  * @minor: 
 593  * @micro: 
 594  *
 595  * 
 596  *
 597  * Return value: 
 598  *
 599  * Since: 0.9.30
 600  **/
 601 hb_bool_t
 602 hb_version_atleast (unsigned int major,
 603                     unsigned int minor,
 604                     unsigned int micro)
 605 {
 606   return HB_VERSION_ATLEAST (major, minor, micro);







































































































































































































































































































































































































































 607 }


  15  * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
  16  * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
  17  * DAMAGE.
  18  *
  19  * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
  20  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  21  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
  22  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
  23  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  24  *
  25  * Red Hat Author(s): Behdad Esfahbod
  26  * Google Author(s): Behdad Esfahbod
  27  */
  28 
  29 #include "hb-private.hh"
  30 
  31 #include "hb-mutex-private.hh"
  32 #include "hb-object-private.hh"
  33 
  34 #include <locale.h>
  35 #ifdef HAVE_XLOCALE_H
  36 #include <xlocale.h>
  37 #endif
  38 
  39 
  40 /* hb_options_t */
  41 
  42 hb_options_union_t _hb_options;
  43 
  44 void
  45 _hb_options_init (void)
  46 {
  47   hb_options_union_t u;
  48   u.i = 0;
  49   u.opts.initialized = 1;
  50 
  51   char *c = getenv ("HB_OPTIONS");
  52   u.opts.uniscribe_bug_compatible = c && strstr (c, "uniscribe-bug-compatible");
  53 
  54   /* This is idempotent and threadsafe. */
  55   _hb_options = u;
  56 }
  57 


  68  * Return value:
  69  *
  70  * Since: 0.9.2
  71  **/
  72 hb_tag_t
  73 hb_tag_from_string (const char *str, int len)
  74 {
  75   char tag[4];
  76   unsigned int i;
  77 
  78   if (!str || !len || !*str)
  79     return HB_TAG_NONE;
  80 
  81   if (len < 0 || len > 4)
  82     len = 4;
  83   for (i = 0; i < (unsigned) len && str[i]; i++)
  84     tag[i] = str[i];
  85   for (; i < 4; i++)
  86     tag[i] = ' ';
  87 
  88   return HB_TAG (tag[0], tag[1], tag[2], tag[3]);
  89 }
  90 
  91 /**
  92  * hb_tag_to_string:
  93  * @tag:
  94  * @buf: (out caller-allocates) (array fixed-size=4) (element-type uint8_t):
  95  *
  96  *
  97  *
  98  * Since: 0.9.5
  99  **/
 100 void
 101 hb_tag_to_string (hb_tag_t tag, char *buf)
 102 {
 103   buf[0] = (char) (uint8_t) (tag >> 24);
 104   buf[1] = (char) (uint8_t) (tag >> 16);
 105   buf[2] = (char) (uint8_t) (tag >>  8);
 106   buf[3] = (char) (uint8_t) (tag >>  0);
 107 }
 108 


 172 };
 173 
 174 static const char canon_map[256] = {
 175    0,   0,   0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,   0,   0,
 176    0,   0,   0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,   0,   0,
 177    0,   0,   0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,  '-',  0,   0,
 178   '0', '1', '2', '3', '4', '5', '6', '7',  '8', '9',  0,   0,   0,   0,   0,   0,
 179   '-', 'a', 'b', 'c', 'd', 'e', 'f', 'g',  'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
 180   'p', 'q', 'r', 's', 't', 'u', 'v', 'w',  'x', 'y', 'z',  0,   0,   0,   0,  '-',
 181    0,  'a', 'b', 'c', 'd', 'e', 'f', 'g',  'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
 182   'p', 'q', 'r', 's', 't', 'u', 'v', 'w',  'x', 'y', 'z',  0,   0,   0,   0,   0
 183 };
 184 
 185 static bool
 186 lang_equal (hb_language_t  v1,
 187             const void    *v2)
 188 {
 189   const unsigned char *p1 = (const unsigned char *) v1;
 190   const unsigned char *p2 = (const unsigned char *) v2;
 191 
 192   while (*p1 && *p1 == canon_map[*p2]) {
 193     p1++;
 194     p2++;
 195   }
 196 
 197   return *p1 == canon_map[*p2];
 198 }
 199 
 200 #if 0
 201 static unsigned int
 202 lang_hash (const void *key)
 203 {
 204   const unsigned char *p = key;
 205   unsigned int h = 0;
 206   while (canon_map[*p])
 207     {
 208       h = (h << 5) - h + canon_map[*p];
 209       p++;
 210     }
 211 
 212   return h;
 213 }
 214 #endif
 215 
 216 
 217 struct hb_language_item_t {
 218 
 219   struct hb_language_item_t *next;
 220   hb_language_t lang;
 221 
 222   inline bool operator == (const char *s) const {
 223     return lang_equal (lang, s);
 224   }
 225 
 226   inline hb_language_item_t & operator = (const char *s) {
 227     /* If a custom allocated is used calling strdup() pairs
 228     badly with a call to the custom free() in finish() below.
 229     Therefore don't call strdup(), implement its behavior.
 230     */
 231     size_t len = strlen(s) + 1;
 232     lang = (hb_language_t) malloc(len);
 233     if (likely (lang))
 234     {
 235       memcpy((unsigned char *) lang, s, len);
 236       for (unsigned char *p = (unsigned char *) lang; *p; p++)
 237         *p = canon_map[*p];
 238     }
 239 
 240     return *this;
 241   }
 242 
 243   void finish (void) { free ((void *) lang); }
 244 };
 245 
 246 
 247 /* Thread-safe lock-free language list */
 248 
 249 static hb_language_item_t *langs;
 250 
 251 #ifdef HB_USE_ATEXIT
 252 static void
 253 free_langs (void)
 254 {
 255   while (langs) {
 256     hb_language_item_t *next = langs->next;
 257     langs->finish ();
 258     free (langs);
 259     langs = next;
 260   }
 261 }
 262 #endif
 263 
 264 static hb_language_item_t *
 265 lang_find_or_insert (const char *key)
 266 {
 267 retry:
 268   hb_language_item_t *first_lang = (hb_language_item_t *) hb_atomic_ptr_get (&langs);
 269 
 270   for (hb_language_item_t *lang = first_lang; lang; lang = lang->next)
 271     if (*lang == key)
 272       return lang;
 273 
 274   /* Not found; allocate one. */
 275   hb_language_item_t *lang = (hb_language_item_t *) calloc (1, sizeof (hb_language_item_t));
 276   if (unlikely (!lang))
 277     return nullptr;
 278   lang->next = first_lang;
 279   *lang = key;
 280   if (unlikely (!lang->lang))
 281   {
 282     free (lang);
 283     return nullptr;
 284   }
 285 
 286   if (!hb_atomic_ptr_cmpexch (&langs, first_lang, lang)) {
 287     lang->finish ();
 288     free (lang);
 289     goto retry;
 290   }
 291 
 292 #ifdef HB_USE_ATEXIT
 293   if (!first_lang)
 294     atexit (free_langs); /* First person registers atexit() callback. */
 295 #endif
 296 
 297   return lang;
 298 }
 299 
 300 
 301 /**
 302  * hb_language_from_string:
 303  * @str: (array length=len) (element-type uint8_t): a string representing
 304  *       ISO 639 language code
 305  * @len: length of the @str, or -1 if it is %NULL-terminated.
 306  *
 307  * Converts @str representing an ISO 639 language code to the corresponding
 308  * #hb_language_t.
 309  *
 310  * Return value: (transfer none):
 311  * The #hb_language_t corresponding to the ISO 639 language code.
 312  *
 313  * Since: 0.9.2
 314  **/
 315 hb_language_t
 316 hb_language_from_string (const char *str, int len)
 317 {
 318   if (!str || !len || !*str)
 319     return HB_LANGUAGE_INVALID;
 320 
 321   hb_language_item_t *item = nullptr;
 322   if (len >= 0)
 323   {
 324     /* NUL-terminate it. */
 325     char strbuf[64];
 326     len = MIN (len, (int) sizeof (strbuf) - 1);
 327     memcpy (strbuf, str, len);
 328     strbuf[len] = '\0';
 329     item = lang_find_or_insert (strbuf);
 330   }
 331   else
 332     item = lang_find_or_insert (str);
 333 
 334   return likely (item) ? item->lang : HB_LANGUAGE_INVALID;
 335 }
 336 
 337 /**
 338  * hb_language_to_string:
 339  * @language: an #hb_language_t to convert.
 340  *
 341  * See hb_language_from_string().
 342  *
 343  * Return value: (transfer none):
 344  * A %NULL-terminated string representing the @language. Must not be freed by
 345  * the caller.
 346  *
 347  * Since: 0.9.2
 348  **/
 349 const char *
 350 hb_language_to_string (hb_language_t language)
 351 {
 352   /* This is actually nullptr-safe! */
 353   return language->s;
 354 }
 355 
 356 /**
 357  * hb_language_get_default:
 358  *
 359  *
 360  *
 361  * Return value: (transfer none):
 362  *
 363  * Since: 0.9.2
 364  **/
 365 hb_language_t
 366 hb_language_get_default (void)
 367 {
 368   static hb_language_t default_language = HB_LANGUAGE_INVALID;
 369 
 370   hb_language_t language = (hb_language_t) hb_atomic_ptr_get (&default_language);
 371   if (unlikely (language == HB_LANGUAGE_INVALID)) {
 372     language = hb_language_from_string (setlocale (LC_CTYPE, nullptr), -1);
 373     (void) hb_atomic_ptr_cmpexch (&default_language, HB_LANGUAGE_INVALID, language);
 374   }
 375 
 376   return default_language;
 377 }
 378 
 379 
 380 /* hb_script_t */
 381 
 382 /**
 383  * hb_script_from_iso15924_tag:
 384  * @tag: an #hb_tag_t representing an ISO 15924 tag.
 385  *
 386  * Converts an ISO 15924 script tag to a corresponding #hb_script_t.
 387  *
 388  * Return value:
 389  * An #hb_script_t corresponding to the ISO 15924 tag.
 390  *
 391  * Since: 0.9.2
 392  **/


 545                            hb_bool_t           replace)
 546 {
 547   if (!key)
 548     return false;
 549 
 550   if (replace) {
 551     if (!data && !destroy) {
 552       items.remove (key, lock);
 553       return true;
 554     }
 555   }
 556   hb_user_data_item_t item = {key, data, destroy};
 557   bool ret = !!items.replace_or_insert (item, lock, (bool) replace);
 558 
 559   return ret;
 560 }
 561 
 562 void *
 563 hb_user_data_array_t::get (hb_user_data_key_t *key)
 564 {
 565   hb_user_data_item_t item = {nullptr, nullptr, nullptr};
 566 
 567   return items.find (key, &item, lock) ? item.data : nullptr;
 568 }
 569 
 570 
 571 /* hb_version */
 572 
 573 /**
 574  * hb_version:
 575  * @major: (out): Library major version component.
 576  * @minor: (out): Library minor version component.
 577  * @micro: (out): Library micro version component.
 578  *
 579  * Returns library version as three integer components.
 580  *
 581  * Since: 0.9.2
 582  **/
 583 void
 584 hb_version (unsigned int *major,
 585             unsigned int *minor,
 586             unsigned int *micro)
 587 {


 606 }
 607 
 608 /**
 609  * hb_version_atleast:
 610  * @major:
 611  * @minor:
 612  * @micro:
 613  *
 614  *
 615  *
 616  * Return value:
 617  *
 618  * Since: 0.9.30
 619  **/
 620 hb_bool_t
 621 hb_version_atleast (unsigned int major,
 622                     unsigned int minor,
 623                     unsigned int micro)
 624 {
 625   return HB_VERSION_ATLEAST (major, minor, micro);
 626 }
 627 
 628 
 629 
 630 /* hb_feature_t and hb_variation_t */
 631 
 632 static bool
 633 parse_space (const char **pp, const char *end)
 634 {
 635   while (*pp < end && ISSPACE (**pp))
 636     (*pp)++;
 637   return true;
 638 }
 639 
 640 static bool
 641 parse_char (const char **pp, const char *end, char c)
 642 {
 643   parse_space (pp, end);
 644 
 645   if (*pp == end || **pp != c)
 646     return false;
 647 
 648   (*pp)++;
 649   return true;
 650 }
 651 
 652 static bool
 653 parse_uint (const char **pp, const char *end, unsigned int *pv)
 654 {
 655   char buf[32];
 656   unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp));
 657   strncpy (buf, *pp, len);
 658   buf[len] = '\0';
 659 
 660   char *p = buf;
 661   char *pend = p;
 662   unsigned int v;
 663 
 664   /* Intentionally use strtol instead of strtoul, such that
 665    * -1 turns into "big number"... */
 666   errno = 0;
 667   v = strtol (p, &pend, 0);
 668   if (errno || p == pend)
 669     return false;
 670 
 671   *pv = v;
 672   *pp += pend - p;
 673   return true;
 674 }
 675 
 676 static bool
 677 parse_uint32 (const char **pp, const char *end, uint32_t *pv)
 678 {
 679   char buf[32];
 680   unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp));
 681   strncpy (buf, *pp, len);
 682   buf[len] = '\0';
 683 
 684   char *p = buf;
 685   char *pend = p;
 686   unsigned int v;
 687 
 688   /* Intentionally use strtol instead of strtoul, such that
 689    * -1 turns into "big number"... */
 690   errno = 0;
 691   v = strtol (p, &pend, 0);
 692   if (errno || p == pend)
 693     return false;
 694 
 695   *pv = v;
 696   *pp += pend - p;
 697   return true;
 698 }
 699 
 700 #if defined (HAVE_NEWLOCALE) && defined (HAVE_STRTOD_L)
 701 #define USE_XLOCALE 1
 702 #define HB_LOCALE_T locale_t
 703 #define HB_CREATE_LOCALE(locName) newlocale (LC_ALL_MASK, locName, nullptr)
 704 #define HB_FREE_LOCALE(loc) freelocale (loc)
 705 #elif defined(_MSC_VER)
 706 #define USE_XLOCALE 1
 707 #define HB_LOCALE_T _locale_t
 708 #define HB_CREATE_LOCALE(locName) _create_locale (LC_ALL, locName)
 709 #define HB_FREE_LOCALE(loc) _free_locale (loc)
 710 #define strtod_l(a, b, c) _strtod_l ((a), (b), (c))
 711 #endif
 712 
 713 #ifdef USE_XLOCALE
 714 
 715 static HB_LOCALE_T C_locale;
 716 
 717 #ifdef HB_USE_ATEXIT
 718 static void
 719 free_C_locale (void)
 720 {
 721   if (C_locale)
 722     HB_FREE_LOCALE (C_locale);
 723 }
 724 #endif
 725 
 726 static HB_LOCALE_T
 727 get_C_locale (void)
 728 {
 729 retry:
 730   HB_LOCALE_T C = (HB_LOCALE_T) hb_atomic_ptr_get (&C_locale);
 731 
 732   if (unlikely (!C))
 733   {
 734     C = HB_CREATE_LOCALE ("C");
 735 
 736     if (!hb_atomic_ptr_cmpexch (&C_locale, nullptr, C))
 737     {
 738       HB_FREE_LOCALE (C_locale);
 739       goto retry;
 740     }
 741 
 742 #ifdef HB_USE_ATEXIT
 743     atexit (free_C_locale); /* First person registers atexit() callback. */
 744 #endif
 745   }
 746 
 747   return C;
 748 }
 749 #endif
 750 
 751 static bool
 752 parse_float (const char **pp, const char *end, float *pv)
 753 {
 754   char buf[32];
 755   unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp));
 756   strncpy (buf, *pp, len);
 757   buf[len] = '\0';
 758 
 759   char *p = buf;
 760   char *pend = p;
 761   float v;
 762 
 763   errno = 0;
 764 #ifdef USE_XLOCALE
 765   v = strtod_l (p, &pend, get_C_locale ());
 766 #else
 767   v = strtod (p, &pend);
 768 #endif
 769   if (errno || p == pend)
 770     return false;
 771 
 772   *pv = v;
 773   *pp += pend - p;
 774   return true;
 775 }
 776 
 777 static bool
 778 parse_bool (const char **pp, const char *end, uint32_t *pv)
 779 {
 780   parse_space (pp, end);
 781 
 782   const char *p = *pp;
 783   while (*pp < end && ISALPHA(**pp))
 784     (*pp)++;
 785 
 786   /* CSS allows on/off as aliases 1/0. */
 787   if (*pp - p == 2 || 0 == strncmp (p, "on", 2))
 788     *pv = 1;
 789   else if (*pp - p == 3 || 0 == strncmp (p, "off", 2))
 790     *pv = 0;
 791   else
 792     return false;
 793 
 794   return true;
 795 }
 796 
 797 /* hb_feature_t */
 798 
 799 static bool
 800 parse_feature_value_prefix (const char **pp, const char *end, hb_feature_t *feature)
 801 {
 802   if (parse_char (pp, end, '-'))
 803     feature->value = 0;
 804   else {
 805     parse_char (pp, end, '+');
 806     feature->value = 1;
 807   }
 808 
 809   return true;
 810 }
 811 
 812 static bool
 813 parse_tag (const char **pp, const char *end, hb_tag_t *tag)
 814 {
 815   parse_space (pp, end);
 816 
 817   char quote = 0;
 818 
 819   if (*pp < end && (**pp == '\'' || **pp == '"'))
 820   {
 821     quote = **pp;
 822     (*pp)++;
 823   }
 824 
 825   const char *p = *pp;
 826   while (*pp < end && ISALNUM(**pp))
 827     (*pp)++;
 828 
 829   if (p == *pp || *pp - p > 4)
 830     return false;
 831 
 832   *tag = hb_tag_from_string (p, *pp - p);
 833 
 834   if (quote)
 835   {
 836     /* CSS expects exactly four bytes.  And we only allow quotations for
 837      * CSS compatibility.  So, enforce the length. */
 838      if (*pp - p != 4)
 839        return false;
 840     if (*pp == end || **pp != quote)
 841       return false;
 842     (*pp)++;
 843   }
 844 
 845   return true;
 846 }
 847 
 848 static bool
 849 parse_feature_indices (const char **pp, const char *end, hb_feature_t *feature)
 850 {
 851   parse_space (pp, end);
 852 
 853   bool has_start;
 854 
 855   feature->start = 0;
 856   feature->end = (unsigned int) -1;
 857 
 858   if (!parse_char (pp, end, '['))
 859     return true;
 860 
 861   has_start = parse_uint (pp, end, &feature->start);
 862 
 863   if (parse_char (pp, end, ':')) {
 864     parse_uint (pp, end, &feature->end);
 865   } else {
 866     if (has_start)
 867       feature->end = feature->start + 1;
 868   }
 869 
 870   return parse_char (pp, end, ']');
 871 }
 872 
 873 static bool
 874 parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *feature)
 875 {
 876   bool had_equal = parse_char (pp, end, '=');
 877   bool had_value = parse_uint32 (pp, end, &feature->value) ||
 878                    parse_bool (pp, end, &feature->value);
 879   /* CSS doesn't use equal-sign between tag and value.
 880    * If there was an equal-sign, then there *must* be a value.
 881    * A value without an eqaul-sign is ok, but not required. */
 882   return !had_equal || had_value;
 883 }
 884 
 885 static bool
 886 parse_one_feature (const char **pp, const char *end, hb_feature_t *feature)
 887 {
 888   return parse_feature_value_prefix (pp, end, feature) &&
 889          parse_tag (pp, end, &feature->tag) &&
 890          parse_feature_indices (pp, end, feature) &&
 891          parse_feature_value_postfix (pp, end, feature) &&
 892          parse_space (pp, end) &&
 893          *pp == end;
 894 }
 895 
 896 /**
 897  * hb_feature_from_string:
 898  * @str: (array length=len) (element-type uint8_t): a string to parse
 899  * @len: length of @str, or -1 if string is %NULL terminated
 900  * @feature: (out): the #hb_feature_t to initialize with the parsed values
 901  *
 902  * Parses a string into a #hb_feature_t.
 903  *
 904  * TODO: document the syntax here.
 905  *
 906  * Return value:
 907  * %true if @str is successfully parsed, %false otherwise.
 908  *
 909  * Since: 0.9.5
 910  **/
 911 hb_bool_t
 912 hb_feature_from_string (const char *str, int len,
 913                         hb_feature_t *feature)
 914 {
 915   hb_feature_t feat;
 916 
 917   if (len < 0)
 918     len = strlen (str);
 919 
 920   if (likely (parse_one_feature (&str, str + len, &feat)))
 921   {
 922     if (feature)
 923       *feature = feat;
 924     return true;
 925   }
 926 
 927   if (feature)
 928     memset (feature, 0, sizeof (*feature));
 929   return false;
 930 }
 931 
 932 /**
 933  * hb_feature_to_string:
 934  * @feature: an #hb_feature_t to convert
 935  * @buf: (array length=size) (out): output string
 936  * @size: the allocated size of @buf
 937  *
 938  * Converts a #hb_feature_t into a %NULL-terminated string in the format
 939  * understood by hb_feature_from_string(). The client in responsible for
 940  * allocating big enough size for @buf, 128 bytes is more than enough.
 941  *
 942  * Since: 0.9.5
 943  **/
 944 void
 945 hb_feature_to_string (hb_feature_t *feature,
 946                       char *buf, unsigned int size)
 947 {
 948   if (unlikely (!size)) return;
 949 
 950   char s[128];
 951   unsigned int len = 0;
 952   if (feature->value == 0)
 953     s[len++] = '-';
 954   hb_tag_to_string (feature->tag, s + len);
 955   len += 4;
 956   while (len && s[len - 1] == ' ')
 957     len--;
 958   if (feature->start != 0 || feature->end != (unsigned int) -1)
 959   {
 960     s[len++] = '[';
 961     if (feature->start)
 962       len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->start));
 963     if (feature->end != feature->start + 1) {
 964       s[len++] = ':';
 965       if (feature->end != (unsigned int) -1)
 966         len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->end));
 967     }
 968     s[len++] = ']';
 969   }
 970   if (feature->value > 1)
 971   {
 972     s[len++] = '=';
 973     len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value));
 974   }
 975   assert (len < ARRAY_LENGTH (s));
 976   len = MIN (len, size - 1);
 977   memcpy (buf, s, len);
 978   buf[len] = '\0';
 979 }
 980 
 981 /* hb_variation_t */
 982 
 983 static bool
 984 parse_variation_value (const char **pp, const char *end, hb_variation_t *variation)
 985 {
 986   parse_char (pp, end, '='); /* Optional. */
 987   return parse_float (pp, end, &variation->value);
 988 }
 989 
 990 static bool
 991 parse_one_variation (const char **pp, const char *end, hb_variation_t *variation)
 992 {
 993   return parse_tag (pp, end, &variation->tag) &&
 994          parse_variation_value (pp, end, variation) &&
 995          parse_space (pp, end) &&
 996          *pp == end;
 997 }
 998 
 999 /**
1000  * hb_variation_from_string:
1001  *
1002  * Since: 1.4.2
1003  */
1004 hb_bool_t
1005 hb_variation_from_string (const char *str, int len,
1006                           hb_variation_t *variation)
1007 {
1008   hb_variation_t var;
1009 
1010   if (len < 0)
1011     len = strlen (str);
1012 
1013   if (likely (parse_one_variation (&str, str + len, &var)))
1014   {
1015     if (variation)
1016       *variation = var;
1017     return true;
1018   }
1019 
1020   if (variation)
1021     memset (variation, 0, sizeof (*variation));
1022   return false;
1023 }
1024 
1025 /**
1026  * hb_variation_to_string:
1027  *
1028  * Since: 1.4.2
1029  */
1030 void
1031 hb_variation_to_string (hb_variation_t *variation,
1032                         char *buf, unsigned int size)
1033 {
1034   if (unlikely (!size)) return;
1035 
1036   char s[128];
1037   unsigned int len = 0;
1038   hb_tag_to_string (variation->tag, s + len);
1039   len += 4;
1040   while (len && s[len - 1] == ' ')
1041     len--;
1042   s[len++] = '=';
1043   len += MAX (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%g", variation->value));
1044 
1045   assert (len < ARRAY_LENGTH (s));
1046   len = MIN (len, size - 1);
1047   memcpy (buf, s, len);
1048   buf[len] = '\0';
1049 }
< prev index next >