• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// This file is generated by Parser_cpp.template.
2
3// Copyright 2016 The Chromium Authors. All rights reserved.
4// Use of this source code is governed by a BSD-style license that can be
5// found in the LICENSE file.
6
7{% for namespace in config.protocol.namespace %}
8namespace {{namespace}} {
9{% endfor %}
10
11namespace {
12
13const int stackLimit = 1000;
14
15enum Token {
16    ObjectBegin,
17    ObjectEnd,
18    ArrayBegin,
19    ArrayEnd,
20    StringLiteral,
21    Number,
22    BoolTrue,
23    BoolFalse,
24    NullToken,
25    ListSeparator,
26    ObjectPairSeparator,
27    InvalidToken,
28};
29
30const char* const nullString = "null";
31const char* const trueString = "true";
32const char* const falseString = "false";
33
34bool isASCII(uint16_t c)
35{
36    return !(c & ~0x7F);
37}
38
39bool isSpaceOrNewLine(uint16_t c)
40{
41    return isASCII(c) && c <= ' ' && (c == ' ' || (c <= 0xD && c >= 0x9));
42}
43
44double charactersToDouble(const uint16_t* characters, size_t length, bool* ok)
45{
46    std::vector<char> buffer;
47    buffer.reserve(length + 1);
48    for (size_t i = 0; i < length; ++i) {
49        if (!isASCII(characters[i])) {
50            *ok = false;
51            return 0;
52        }
53        buffer.push_back(static_cast<char>(characters[i]));
54    }
55    buffer.push_back('\0');
56    return StringUtil::toDouble(buffer.data(), length, ok);
57}
58
59double charactersToDouble(const uint8_t* characters, size_t length, bool* ok)
60{
61    std::string buffer(reinterpret_cast<const char*>(characters), length);
62    return StringUtil::toDouble(buffer.data(), length, ok);
63}
64
65template<typename Char>
66bool parseConstToken(const Char* start, const Char* end, const Char** tokenEnd, const char* token)
67{
68    while (start < end && *token != '\0' && *start++ == *token++) { }
69    if (*token != '\0')
70        return false;
71    *tokenEnd = start;
72    return true;
73}
74
75template<typename Char>
76bool readInt(const Char* start, const Char* end, const Char** tokenEnd, bool canHaveLeadingZeros)
77{
78    if (start == end)
79        return false;
80    bool haveLeadingZero = '0' == *start;
81    int length = 0;
82    while (start < end && '0' <= *start && *start <= '9') {
83        ++start;
84        ++length;
85    }
86    if (!length)
87        return false;
88    if (!canHaveLeadingZeros && length > 1 && haveLeadingZero)
89        return false;
90    *tokenEnd = start;
91    return true;
92}
93
94template<typename Char>
95bool parseNumberToken(const Char* start, const Char* end, const Char** tokenEnd)
96{
97    // We just grab the number here. We validate the size in DecodeNumber.
98    // According to RFC4627, a valid number is: [minus] int [frac] [exp]
99    if (start == end)
100        return false;
101    Char c = *start;
102    if ('-' == c)
103        ++start;
104
105    if (!readInt(start, end, &start, false))
106        return false;
107    if (start == end) {
108        *tokenEnd = start;
109        return true;
110    }
111
112    // Optional fraction part
113    c = *start;
114    if ('.' == c) {
115        ++start;
116        if (!readInt(start, end, &start, true))
117            return false;
118        if (start == end) {
119            *tokenEnd = start;
120            return true;
121        }
122        c = *start;
123    }
124
125    // Optional exponent part
126    if ('e' == c || 'E' == c) {
127        ++start;
128        if (start == end)
129            return false;
130        c = *start;
131        if ('-' == c || '+' == c) {
132            ++start;
133            if (start == end)
134                return false;
135        }
136        if (!readInt(start, end, &start, true))
137            return false;
138    }
139
140    *tokenEnd = start;
141    return true;
142}
143
144template<typename Char>
145bool readHexDigits(const Char* start, const Char* end, const Char** tokenEnd, int digits)
146{
147    if (end - start < digits)
148        return false;
149    for (int i = 0; i < digits; ++i) {
150        Char c = *start++;
151        if (!(('0' <= c && c <= '9') || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F')))
152            return false;
153    }
154    *tokenEnd = start;
155    return true;
156}
157
158template<typename Char>
159bool parseStringToken(const Char* start, const Char* end, const Char** tokenEnd)
160{
161    while (start < end) {
162        Char c = *start++;
163        if ('\\' == c) {
164	    if (start == end)
165	        return false;
166            c = *start++;
167            // Make sure the escaped char is valid.
168            switch (c) {
169            case 'x':
170                if (!readHexDigits(start, end, &start, 2))
171                    return false;
172                break;
173            case 'u':
174                if (!readHexDigits(start, end, &start, 4))
175                    return false;
176                break;
177            case '\\':
178            case '/':
179            case 'b':
180            case 'f':
181            case 'n':
182            case 'r':
183            case 't':
184            case 'v':
185            case '"':
186                break;
187            default:
188                return false;
189            }
190        } else if ('"' == c) {
191            *tokenEnd = start;
192            return true;
193        }
194    }
195    return false;
196}
197
198template<typename Char>
199bool skipComment(const Char* start, const Char* end, const Char** commentEnd)
200{
201    if (start == end)
202        return false;
203
204    if (*start != '/' || start + 1 >= end)
205        return false;
206    ++start;
207
208    if (*start == '/') {
209        // Single line comment, read to newline.
210        for (++start; start < end; ++start) {
211            if (*start == '\n' || *start == '\r') {
212                *commentEnd = start + 1;
213                return true;
214            }
215        }
216        *commentEnd = end;
217        // Comment reaches end-of-input, which is fine.
218        return true;
219    }
220
221    if (*start == '*') {
222        Char previous = '\0';
223        // Block comment, read until end marker.
224        for (++start; start < end; previous = *start++) {
225            if (previous == '*' && *start == '/') {
226                *commentEnd = start + 1;
227                return true;
228            }
229        }
230        // Block comment must close before end-of-input.
231        return false;
232    }
233
234    return false;
235}
236
237template<typename Char>
238void skipWhitespaceAndComments(const Char* start, const Char* end, const Char** whitespaceEnd)
239{
240    while (start < end) {
241        if (isSpaceOrNewLine(*start)) {
242            ++start;
243        } else if (*start == '/') {
244            const Char* commentEnd;
245            if (!skipComment(start, end, &commentEnd))
246                break;
247            start = commentEnd;
248        } else {
249            break;
250        }
251    }
252    *whitespaceEnd = start;
253}
254
255template<typename Char>
256Token parseToken(const Char* start, const Char* end, const Char** tokenStart, const Char** tokenEnd)
257{
258    skipWhitespaceAndComments(start, end, tokenStart);
259    start = *tokenStart;
260
261    if (start == end)
262        return InvalidToken;
263
264    switch (*start) {
265    case 'n':
266        if (parseConstToken(start, end, tokenEnd, nullString))
267            return NullToken;
268        break;
269    case 't':
270        if (parseConstToken(start, end, tokenEnd, trueString))
271            return BoolTrue;
272        break;
273    case 'f':
274        if (parseConstToken(start, end, tokenEnd, falseString))
275            return BoolFalse;
276        break;
277    case '[':
278        *tokenEnd = start + 1;
279        return ArrayBegin;
280    case ']':
281        *tokenEnd = start + 1;
282        return ArrayEnd;
283    case ',':
284        *tokenEnd = start + 1;
285        return ListSeparator;
286    case '{':
287        *tokenEnd = start + 1;
288        return ObjectBegin;
289    case '}':
290        *tokenEnd = start + 1;
291        return ObjectEnd;
292    case ':':
293        *tokenEnd = start + 1;
294        return ObjectPairSeparator;
295    case '0':
296    case '1':
297    case '2':
298    case '3':
299    case '4':
300    case '5':
301    case '6':
302    case '7':
303    case '8':
304    case '9':
305    case '-':
306        if (parseNumberToken(start, end, tokenEnd))
307            return Number;
308        break;
309    case '"':
310        if (parseStringToken(start + 1, end, tokenEnd))
311            return StringLiteral;
312        break;
313    }
314    return InvalidToken;
315}
316
317template<typename Char>
318int hexToInt(Char c)
319{
320    if ('0' <= c && c <= '9')
321        return c - '0';
322    if ('A' <= c && c <= 'F')
323        return c - 'A' + 10;
324    if ('a' <= c && c <= 'f')
325        return c - 'a' + 10;
326    DCHECK(false);
327    return 0;
328}
329
330template<typename Char>
331bool decodeString(const Char* start, const Char* end, StringBuilder* output)
332{
333    while (start < end) {
334        uint16_t c = *start++;
335        if ('\\' != c) {
336            StringUtil::builderAppend(*output, c);
337            continue;
338        }
339	if (start == end)
340	    return false;
341        c = *start++;
342
343        if (c == 'x') {
344            // \x is not supported.
345            return false;
346        }
347
348        switch (c) {
349        case '"':
350        case '/':
351        case '\\':
352            break;
353        case 'b':
354            c = '\b';
355            break;
356        case 'f':
357            c = '\f';
358            break;
359        case 'n':
360            c = '\n';
361            break;
362        case 'r':
363            c = '\r';
364            break;
365        case 't':
366            c = '\t';
367            break;
368        case 'v':
369            c = '\v';
370            break;
371        case 'u':
372            c = (hexToInt(*start) << 12) +
373                (hexToInt(*(start + 1)) << 8) +
374                (hexToInt(*(start + 2)) << 4) +
375                hexToInt(*(start + 3));
376            start += 4;
377            break;
378        default:
379            return false;
380        }
381        StringUtil::builderAppend(*output, c);
382    }
383    return true;
384}
385
386template<typename Char>
387bool decodeString(const Char* start, const Char* end, String* output)
388{
389    if (start == end) {
390        *output = "";
391        return true;
392    }
393    if (start > end)
394        return false;
395    StringBuilder buffer;
396    StringUtil::builderReserve(buffer, end - start);
397    if (!decodeString(start, end, &buffer))
398        return false;
399    *output = StringUtil::builderToString(buffer);
400    return true;
401}
402
403template<typename Char>
404std::unique_ptr<Value> buildValue(const Char* start, const Char* end, const Char** valueTokenEnd, int depth)
405{
406    if (depth > stackLimit)
407        return nullptr;
408
409    std::unique_ptr<Value> result;
410    const Char* tokenStart;
411    const Char* tokenEnd;
412    Token token = parseToken(start, end, &tokenStart, &tokenEnd);
413    switch (token) {
414    case InvalidToken:
415        return nullptr;
416    case NullToken:
417        result = Value::null();
418        break;
419    case BoolTrue:
420        result = FundamentalValue::create(true);
421        break;
422    case BoolFalse:
423        result = FundamentalValue::create(false);
424        break;
425    case Number: {
426        bool ok;
427        double value = charactersToDouble(tokenStart, tokenEnd - tokenStart, &ok);
428        if (!ok)
429            return nullptr;
430        if (value >= INT_MIN && value <= INT_MAX && static_cast<int>(value) == value)
431            result = FundamentalValue::create(static_cast<int>(value));
432        else
433            result = FundamentalValue::create(value);
434        break;
435    }
436    case StringLiteral: {
437        String value;
438        bool ok = decodeString(tokenStart + 1, tokenEnd - 1, &value);
439        if (!ok)
440            return nullptr;
441        result = StringValue::create(value);
442        break;
443    }
444    case ArrayBegin: {
445        std::unique_ptr<ListValue> array = ListValue::create();
446        start = tokenEnd;
447        token = parseToken(start, end, &tokenStart, &tokenEnd);
448        while (token != ArrayEnd) {
449            std::unique_ptr<Value> arrayNode = buildValue(start, end, &tokenEnd, depth + 1);
450            if (!arrayNode)
451                return nullptr;
452            array->pushValue(std::move(arrayNode));
453
454            // After a list value, we expect a comma or the end of the list.
455            start = tokenEnd;
456            token = parseToken(start, end, &tokenStart, &tokenEnd);
457            if (token == ListSeparator) {
458                start = tokenEnd;
459                token = parseToken(start, end, &tokenStart, &tokenEnd);
460                if (token == ArrayEnd)
461                    return nullptr;
462            } else if (token != ArrayEnd) {
463                // Unexpected value after list value. Bail out.
464                return nullptr;
465            }
466        }
467        if (token != ArrayEnd)
468            return nullptr;
469        result = std::move(array);
470        break;
471    }
472    case ObjectBegin: {
473        std::unique_ptr<DictionaryValue> object = DictionaryValue::create();
474        start = tokenEnd;
475        token = parseToken(start, end, &tokenStart, &tokenEnd);
476        while (token != ObjectEnd) {
477            if (token != StringLiteral)
478                return nullptr;
479            String key;
480            if (!decodeString(tokenStart + 1, tokenEnd - 1, &key))
481                return nullptr;
482            start = tokenEnd;
483
484            token = parseToken(start, end, &tokenStart, &tokenEnd);
485            if (token != ObjectPairSeparator)
486                return nullptr;
487            start = tokenEnd;
488
489            std::unique_ptr<Value> value = buildValue(start, end, &tokenEnd, depth + 1);
490            if (!value)
491                return nullptr;
492            object->setValue(key, std::move(value));
493            start = tokenEnd;
494
495            // After a key/value pair, we expect a comma or the end of the
496            // object.
497            token = parseToken(start, end, &tokenStart, &tokenEnd);
498            if (token == ListSeparator) {
499                start = tokenEnd;
500                token = parseToken(start, end, &tokenStart, &tokenEnd);
501                if (token == ObjectEnd)
502                    return nullptr;
503            } else if (token != ObjectEnd) {
504                // Unexpected value after last object value. Bail out.
505                return nullptr;
506            }
507        }
508        if (token != ObjectEnd)
509            return nullptr;
510        result = std::move(object);
511        break;
512    }
513
514    default:
515        // We got a token that's not a value.
516        return nullptr;
517    }
518
519    skipWhitespaceAndComments(tokenEnd, end, valueTokenEnd);
520    return result;
521}
522
523template<typename Char>
524std::unique_ptr<Value> parseJSONInternal(const Char* start, unsigned length)
525{
526    const Char* end = start + length;
527    const Char *tokenEnd;
528    std::unique_ptr<Value> value = buildValue(start, end, &tokenEnd, 0);
529    if (!value || tokenEnd != end)
530        return nullptr;
531    return value;
532}
533
534} // anonymous namespace
535
536std::unique_ptr<Value> parseJSONCharacters(const uint16_t* characters, unsigned length)
537{
538    return parseJSONInternal<uint16_t>(characters, length);
539}
540
541std::unique_ptr<Value> parseJSONCharacters(const uint8_t* characters, unsigned length)
542{
543    return parseJSONInternal<uint8_t>(characters, length);
544}
545
546{% for namespace in config.protocol.namespace %}
547} // namespace {{namespace}}
548{% endfor %}
549