Coverage Report

Created: 2026-09-28 10:59

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