Initial commit
[charfbuzz.git] / hb-common.c
blobf5141f528597232bb1a0214b7f1469cc9af008b8
1 // C99 port from c++ is protected by a GNU Lesser GPLv3
2 // Copyright © 2013 Sylvain BERTRAND <sylvain.bertrand@gmail.com>
3 // <sylware@legeek.net>
4 #define _GNU_SOURCE
5 #include <string.h>
6 #include <stdlib.h>
8 #include "hb.h"
9 #include "hb-private.h"
10 #include "hb-atomic-private.h"
12 //this is actually hb_language_t type
13 struct hb_language_impl_t {
14 const char s[1];
17 static const char canon_map[256] = {
18 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
19 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
20 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0,
21 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 0, 0, 0, 0, 0, 0,
22 '-', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
23 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, '-',
24 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
25 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 0, 0, 0, 0, 0
28 static hb_bool_t
29 lang_equal(hb_language_t v1,
30 const void *v2)
32 const unsigned char *p1 = (const unsigned char*)v1;
33 const unsigned char *p2 = (const unsigned char*)v2;
35 while (*p1 && *p1 == canon_map[*p2])
36 p1++, p2++;
37 return *p1 == canon_map[*p2];
40 struct hb_language_item_t {
42 struct hb_language_item_t *next;
43 hb_language_t lang;
46 static hb_language_t
47 lang_assign(const char *s)
49 hb_language_t lang = (hb_language_t)strdup(s);
50 for (unsigned char *p = (unsigned char *)lang; *p; p++)
51 *p = canon_map[*p];
52 return lang;
55 /* Thread-safe lock-free language list */
57 static struct hb_language_item_t *lang_items;
59 static struct hb_language_item_t *
60 language_item_find_or_insert(const char *key)
62 struct hb_language_item_t *first_lang_item = hb_atomic_ptr_get(&lang_items);
64 while (1) {
65 for (struct hb_language_item_t *lang_item = first_lang_item; lang_item;
66 lang_item = lang_item->next)
67 if (lang_equal(lang_item->lang, key))
68 return lang_item;
70 //Not found; allocate one.
71 struct hb_language_item_t *lang_item = calloc(1, sizeof(*lang_item));
72 if (!lang_item)
73 return NULL;
74 lang_item->next = first_lang_item;
75 lang_item->lang = lang_assign(key);
77 if (hb_atomic_ptr_cmpexch(&lang_items, &first_lang_item, &lang_item))
78 return lang_item;
79 free (lang_item);
83 hb_language_t
84 hb_language_from_string(const char *str, int len)
86 if (!str || !len || !*str)
87 return HB_LANGUAGE_INVALID;
89 if (len >= 0) {
90 char strbuf[64];
91 len = MIN(len, (int)sizeof(strbuf) - 1);
92 str = (char*)memcpy(strbuf, str, len);
93 strbuf[len] = '\0';
96 struct hb_language_item_t *item = language_item_find_or_insert(str);
98 return item ? item->lang : HB_LANGUAGE_INVALID;
101 hb_tag_t
102 hb_tag_from_string(const char *str, int len)
104 char tag[4];
105 unsigned i;
107 if (!str || !len || !*str)
108 return HB_TAG_NONE;
110 if (len < 0 || len > 4)
111 len = 4;
112 for (i = 0; i < (unsigned)len && str[i]; ++i)
113 tag[i] = str[i];
114 for (; i < 4; ++i)
115 tag[i] = ' ';
116 return HB_TAG_CHAR4(tag);
119 const char *
120 hb_language_to_string(hb_language_t language)
122 //This is actually NULL-safe!
123 return language->s;