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