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