1
2/*
3 * Copyright 2006 The Android Open Source Project
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9
10#include "SkParse.h"
11
12static inline bool is_between(int c, int min, int max)
13{
14    return (unsigned)(c - min) <= (unsigned)(max - min);
15}
16
17static inline bool is_ws(int c)
18{
19    return is_between(c, 1, 32);
20}
21
22static inline bool is_digit(int c)
23{
24    return is_between(c, '0', '9');
25}
26
27static inline bool is_sep(int c)
28{
29    return is_ws(c) || c == ',' || c == ';';
30}
31
32static int to_hex(int c)
33{
34    if (is_digit(c))
35        return c - '0';
36
37    c |= 0x20;  // make us lower-case
38    if (is_between(c, 'a', 'f'))
39        return c + 10 - 'a';
40    else
41        return -1;
42}
43
44static inline bool is_hex(int c)
45{
46    return to_hex(c) >= 0;
47}
48
49static const char* skip_ws(const char str[])
50{
51    SkASSERT(str);
52    while (is_ws(*str))
53        str++;
54    return str;
55}
56
57static const char* skip_sep(const char str[])
58{
59    SkASSERT(str);
60    while (is_sep(*str))
61        str++;
62    return str;
63}
64
65int SkParse::Count(const char str[])
66{
67    char c;
68    int count = 0;
69    goto skipLeading;
70    do {
71        count++;
72        do {
73            if ((c = *str++) == '\0')
74                goto goHome;
75        } while (is_sep(c) == false);
76skipLeading:
77        do {
78            if ((c = *str++) == '\0')
79                goto goHome;
80        } while (is_sep(c));
81    } while (true);
82goHome:
83    return count;
84}
85
86int SkParse::Count(const char str[], char separator)
87{
88    char c;
89    int count = 0;
90    goto skipLeading;
91    do {
92        count++;
93        do {
94            if ((c = *str++) == '\0')
95                goto goHome;
96        } while (c != separator);
97skipLeading:
98        do {
99            if ((c = *str++) == '\0')
100                goto goHome;
101        } while (c == separator);
102    } while (true);
103goHome:
104    return count;
105}
106
107const char* SkParse::FindHex(const char str[], uint32_t* value)
108{
109    SkASSERT(str);
110    str = skip_ws(str);
111
112    if (!is_hex(*str))
113        return NULL;
114
115    uint32_t n = 0;
116    int max_digits = 8;
117    int digit;
118
119    while ((digit = to_hex(*str)) >= 0)
120    {
121        if (--max_digits < 0)
122            return NULL;
123        n = (n << 4) | digit;
124        str += 1;
125    }
126
127    if (*str == 0 || is_ws(*str))
128    {
129        if (value)
130            *value = n;
131        return str;
132    }
133    return NULL;
134}
135
136const char* SkParse::FindS32(const char str[], int32_t* value)
137{
138    SkASSERT(str);
139    str = skip_ws(str);
140
141    int sign = 0;
142    if (*str == '-')
143    {
144        sign = -1;
145        str += 1;
146    }
147
148    if (!is_digit(*str))
149        return NULL;
150
151    int n = 0;
152    while (is_digit(*str))
153    {
154        n = 10*n + *str - '0';
155        str += 1;
156    }
157    if (value)
158        *value = (n ^ sign) - sign;
159    return str;
160}
161
162const char* SkParse::FindMSec(const char str[], SkMSec* value)
163{
164    SkASSERT(str);
165    str = skip_ws(str);
166
167    int sign = 0;
168    if (*str == '-')
169    {
170        sign = -1;
171        str += 1;
172    }
173
174    if (!is_digit(*str))
175        return NULL;
176
177    int n = 0;
178    while (is_digit(*str))
179    {
180        n = 10*n + *str - '0';
181        str += 1;
182    }
183    int remaining10s = 3;
184    if (*str == '.') {
185        str++;
186        while (is_digit(*str))
187        {
188            n = 10*n + *str - '0';
189            str += 1;
190            if (--remaining10s == 0)
191                break;
192        }
193    }
194    while (--remaining10s >= 0)
195        n *= 10;
196    if (value)
197        *value = (n ^ sign) - sign;
198    return str;
199}
200
201const char* SkParse::FindScalar(const char str[], SkScalar* value) {
202    SkASSERT(str);
203    str = skip_ws(str);
204#ifdef SK_SCALAR_IS_FLOAT
205    char* stop;
206    float v = (float)strtod(str, &stop);
207    if (str == stop) {
208        return NULL;
209    }
210    if (value) {
211        *value = v;
212    }
213    return stop;
214#else
215    int sign = 0;
216    if (*str == '-')
217    {
218        sign = -1;
219        str += 1;
220    }
221
222    if (!is_digit(*str) && *str != '.')
223        return NULL;
224
225    int n = 0;
226    while (is_digit(*str))
227    {
228        n = 10*n + *str - '0';
229        if (n > 0x7FFF)
230            return NULL;
231        str += 1;
232    }
233    n <<= 16;
234
235    if (*str == '.')
236    {
237        static const int gFractions[] = { (1 << 24)  / 10, (1 << 24)  / 100, (1 << 24)  / 1000,
238            (1 << 24)  / 10000, (1 << 24)  / 100000 };
239        str += 1;
240        int d = 0;
241        const int* fraction = gFractions;
242        const int* end = &fraction[SK_ARRAY_COUNT(gFractions)];
243        while (is_digit(*str) && fraction < end)
244            d += (*str++ - '0') * *fraction++;
245        d += 0x80; // round
246        n += d >> 8;
247    }
248    while (is_digit(*str))
249        str += 1;
250    if (value)
251    {
252        n = (n ^ sign) - sign;  // apply the sign
253        *value = SkFixedToScalar(n);
254    }
255#endif
256    return str;
257}
258
259const char* SkParse::FindScalars(const char str[], SkScalar value[], int count)
260{
261    SkASSERT(count >= 0);
262
263    if (count > 0)
264    {
265        for (;;)
266        {
267            str = SkParse::FindScalar(str, value);
268            if (--count == 0 || str == NULL)
269                break;
270
271            // keep going
272            str = skip_sep(str);
273            if (value)
274                value += 1;
275        }
276    }
277    return str;
278}
279
280static bool lookup_str(const char str[], const char** table, int count)
281{
282    while (--count >= 0)
283        if (!strcmp(str, table[count]))
284            return true;
285    return false;
286}
287
288bool SkParse::FindBool(const char str[], bool* value)
289{
290    static const char* gYes[] = { "yes", "1", "true" };
291    static const char* gNo[] = { "no", "0", "false" };
292
293    if (lookup_str(str, gYes, SK_ARRAY_COUNT(gYes)))
294    {
295        if (value) *value = true;
296        return true;
297    }
298    else if (lookup_str(str, gNo, SK_ARRAY_COUNT(gNo)))
299    {
300        if (value) *value = false;
301        return true;
302    }
303    return false;
304}
305
306int SkParse::FindList(const char target[], const char list[])
307{
308    size_t  len = strlen(target);
309    int     index = 0;
310
311    for (;;)
312    {
313        const char* end = strchr(list, ',');
314        size_t      entryLen;
315
316        if (end == NULL) // last entry
317            entryLen = strlen(list);
318        else
319            entryLen = end - list;
320
321        if (entryLen == len && memcmp(target, list, len) == 0)
322            return index;
323        if (end == NULL)
324            break;
325
326        list = end + 1; // skip the ','
327        index += 1;
328    }
329    return -1;
330}
331
332#ifdef SK_SUPPORT_UNITTEST
333void SkParse::UnitTest()
334{
335    // !!! additional parse tests go here
336    SkParse::TestColor();
337}
338#endif
339