/src/arduinojson/src/ArduinoJson/Json/JsonDeserializer.hpp
Line | Count | Source (jump to first uncovered line) |
1 | | // ArduinoJson - https://arduinojson.org |
2 | | // Copyright © 2014-2024, Benoit BLANCHON |
3 | | // MIT License |
4 | | |
5 | | #pragma once |
6 | | |
7 | | #include <ArduinoJson/Deserialization/deserialize.hpp> |
8 | | #include <ArduinoJson/Json/EscapeSequence.hpp> |
9 | | #include <ArduinoJson/Json/Latch.hpp> |
10 | | #include <ArduinoJson/Json/Utf16.hpp> |
11 | | #include <ArduinoJson/Json/Utf8.hpp> |
12 | | #include <ArduinoJson/Memory/ResourceManager.hpp> |
13 | | #include <ArduinoJson/Numbers/parseNumber.hpp> |
14 | | #include <ArduinoJson/Polyfills/assert.hpp> |
15 | | #include <ArduinoJson/Polyfills/type_traits.hpp> |
16 | | #include <ArduinoJson/Polyfills/utility.hpp> |
17 | | #include <ArduinoJson/Variant/VariantData.hpp> |
18 | | |
19 | | ARDUINOJSON_BEGIN_PRIVATE_NAMESPACE |
20 | | |
21 | | template <typename TReader> |
22 | | class JsonDeserializer { |
23 | | public: |
24 | | JsonDeserializer(ResourceManager* resources, TReader reader) |
25 | 2.46k | : stringBuilder_(resources), |
26 | 2.46k | foundSomething_(false), |
27 | 2.46k | latch_(reader), |
28 | 2.46k | resources_(resources) {} |
29 | | |
30 | | template <typename TFilter> |
31 | | DeserializationError parse(VariantData& variant, TFilter filter, |
32 | 2.46k | DeserializationOption::NestingLimit nestingLimit) { |
33 | 2.46k | DeserializationError::Code err; |
34 | | |
35 | 2.46k | err = parseVariant(variant, filter, nestingLimit); |
36 | | |
37 | 2.46k | if (!err && latch_.last() != 0 && variant.isFloat()) { |
38 | | // We don't detect trailing characters earlier, so we need to check now |
39 | 6 | return DeserializationError::InvalidInput; |
40 | 6 | } |
41 | | |
42 | 2.45k | return err; |
43 | 2.46k | } |
44 | | |
45 | | private: |
46 | 155k | char current() { |
47 | 155k | return latch_.current(); |
48 | 155k | } |
49 | | |
50 | 73.2k | void move() { |
51 | 73.2k | latch_.clear(); |
52 | 73.2k | } |
53 | | |
54 | 28.8k | bool eat(char charToSkip) { |
55 | 28.8k | if (current() != charToSkip) |
56 | 14.1k | return false; |
57 | 14.7k | move(); |
58 | 14.7k | return true; |
59 | 28.8k | } |
60 | | |
61 | | template <typename TFilter> |
62 | | DeserializationError::Code parseVariant( |
63 | | VariantData& variant, TFilter filter, |
64 | 16.2k | DeserializationOption::NestingLimit nestingLimit) { |
65 | 16.2k | DeserializationError::Code err; |
66 | | |
67 | 16.2k | err = skipSpacesAndComments(); |
68 | 16.2k | if (err) |
69 | 332 | return err; |
70 | | |
71 | 15.9k | switch (current()) { |
72 | 2.60k | case '[': |
73 | 2.60k | if (filter.allowArray()) |
74 | 2.60k | return parseArray(variant.toArray(), filter, nestingLimit); |
75 | 0 | else |
76 | 0 | return skipArray(nestingLimit); |
77 | | |
78 | 1.75k | case '{': |
79 | 1.75k | if (filter.allowObject()) |
80 | 1.75k | return parseObject(variant.toObject(), filter, nestingLimit); |
81 | 0 | else |
82 | 0 | return skipObject(nestingLimit); |
83 | | |
84 | 759 | case '\"': |
85 | 1.28k | case '\'': |
86 | 1.28k | if (filter.allowValue()) |
87 | 1.28k | return parseStringValue(variant); |
88 | 0 | else |
89 | 0 | return skipQuotedString(); |
90 | | |
91 | 277 | case 't': |
92 | 277 | if (filter.allowValue()) |
93 | 277 | variant.setBoolean(true); |
94 | 277 | return skipKeyword("true"); |
95 | | |
96 | 248 | case 'f': |
97 | 248 | if (filter.allowValue()) |
98 | 248 | variant.setBoolean(false); |
99 | 248 | return skipKeyword("false"); |
100 | | |
101 | 279 | case 'n': |
102 | | // the variant should already by null, except if the same object key was |
103 | | // used twice, as in {"a":1,"a":null} |
104 | 279 | return skipKeyword("null"); |
105 | | |
106 | 9.47k | default: |
107 | 9.47k | if (filter.allowValue()) |
108 | 9.47k | return parseNumericValue(variant); |
109 | 0 | else |
110 | 0 | return skipNumericValue(); |
111 | 15.9k | } |
112 | 15.9k | } |
113 | | |
114 | | DeserializationError::Code skipVariant( |
115 | 0 | DeserializationOption::NestingLimit nestingLimit) { |
116 | 0 | DeserializationError::Code err; |
117 | |
|
118 | 0 | err = skipSpacesAndComments(); |
119 | 0 | if (err) |
120 | 0 | return err; |
121 | | |
122 | 0 | switch (current()) { |
123 | 0 | case '[': |
124 | 0 | return skipArray(nestingLimit); |
125 | | |
126 | 0 | case '{': |
127 | 0 | return skipObject(nestingLimit); |
128 | | |
129 | 0 | case '\"': |
130 | 0 | case '\'': |
131 | 0 | return skipQuotedString(); |
132 | | |
133 | 0 | case 't': |
134 | 0 | return skipKeyword("true"); |
135 | | |
136 | 0 | case 'f': |
137 | 0 | return skipKeyword("false"); |
138 | | |
139 | 0 | case 'n': |
140 | 0 | return skipKeyword("null"); |
141 | | |
142 | 0 | default: |
143 | 0 | return skipNumericValue(); |
144 | 0 | } |
145 | 0 | } |
146 | | |
147 | | template <typename TFilter> |
148 | | DeserializationError::Code parseArray( |
149 | | ArrayData& array, TFilter filter, |
150 | 2.60k | DeserializationOption::NestingLimit nestingLimit) { |
151 | 2.60k | DeserializationError::Code err; |
152 | | |
153 | 2.60k | if (nestingLimit.reached()) |
154 | 5 | return DeserializationError::TooDeep; |
155 | | |
156 | | // Skip opening braket |
157 | 2.60k | ARDUINOJSON_ASSERT(current() == '['); |
158 | 2.60k | move(); |
159 | | |
160 | | // Skip spaces |
161 | 2.60k | err = skipSpacesAndComments(); |
162 | 2.60k | if (err) |
163 | 42 | return err; |
164 | | |
165 | | // Empty array? |
166 | 2.55k | if (eat(']')) |
167 | 169 | return DeserializationError::Ok; |
168 | | |
169 | 2.39k | TFilter elementFilter = filter[0UL]; |
170 | | |
171 | | // Read each value |
172 | 11.1k | for (;;) { |
173 | 11.1k | if (elementFilter.allow()) { |
174 | | // Allocate slot in array |
175 | 11.1k | VariantData* value = array.addElement(resources_); |
176 | 11.1k | if (!value) |
177 | 0 | return DeserializationError::NoMemory; |
178 | | |
179 | | // 1 - Parse value |
180 | 11.1k | err = parseVariant(*value, elementFilter, nestingLimit.decrement()); |
181 | 11.1k | if (err) |
182 | 945 | return err; |
183 | 11.1k | } else { |
184 | 0 | err = skipVariant(nestingLimit.decrement()); |
185 | 0 | if (err) |
186 | 0 | return err; |
187 | 0 | } |
188 | | |
189 | | // 2 - Skip spaces |
190 | 10.2k | err = skipSpacesAndComments(); |
191 | 10.2k | if (err) |
192 | 269 | return err; |
193 | | |
194 | | // 3 - More values? |
195 | 9.96k | if (eat(']')) |
196 | 1.15k | return DeserializationError::Ok; |
197 | 8.80k | if (!eat(',')) |
198 | 17 | return DeserializationError::InvalidInput; |
199 | 8.80k | } |
200 | 2.39k | } |
201 | | |
202 | | DeserializationError::Code skipArray( |
203 | 0 | DeserializationOption::NestingLimit nestingLimit) { |
204 | 0 | DeserializationError::Code err; |
205 | |
|
206 | 0 | if (nestingLimit.reached()) |
207 | 0 | return DeserializationError::TooDeep; |
208 | | |
209 | | // Skip opening braket |
210 | 0 | ARDUINOJSON_ASSERT(current() == '['); |
211 | 0 | move(); |
212 | | |
213 | | // Read each value |
214 | 0 | for (;;) { |
215 | | // 1 - Skip value |
216 | 0 | err = skipVariant(nestingLimit.decrement()); |
217 | 0 | if (err) |
218 | 0 | return err; |
219 | | |
220 | | // 2 - Skip spaces |
221 | 0 | err = skipSpacesAndComments(); |
222 | 0 | if (err) |
223 | 0 | return err; |
224 | | |
225 | | // 3 - More values? |
226 | 0 | if (eat(']')) |
227 | 0 | return DeserializationError::Ok; |
228 | 0 | if (!eat(',')) |
229 | 0 | return DeserializationError::InvalidInput; |
230 | 0 | } |
231 | 0 | } |
232 | | |
233 | | template <typename TFilter> |
234 | | DeserializationError::Code parseObject( |
235 | | ObjectData& object, TFilter filter, |
236 | 1.75k | DeserializationOption::NestingLimit nestingLimit) { |
237 | 1.75k | DeserializationError::Code err; |
238 | | |
239 | 1.75k | if (nestingLimit.reached()) |
240 | 1 | return DeserializationError::TooDeep; |
241 | | |
242 | | // Skip opening brace |
243 | 1.75k | ARDUINOJSON_ASSERT(current() == '{'); |
244 | 1.75k | move(); |
245 | | |
246 | | // Skip spaces |
247 | 1.75k | err = skipSpacesAndComments(); |
248 | 1.75k | if (err) |
249 | 45 | return err; |
250 | | |
251 | | // Empty object? |
252 | 1.70k | if (eat('}')) |
253 | 242 | return DeserializationError::Ok; |
254 | | |
255 | | // Read each key value pair |
256 | 2.81k | for (;;) { |
257 | | // Parse key |
258 | 2.81k | err = parseKey(); |
259 | 2.81k | if (err) |
260 | 50 | return err; |
261 | | |
262 | | // Skip spaces |
263 | 2.76k | err = skipSpacesAndComments(); |
264 | 2.76k | if (err) |
265 | 130 | return err; |
266 | | |
267 | | // Colon |
268 | 2.63k | if (!eat(':')) |
269 | 25 | return DeserializationError::InvalidInput; |
270 | | |
271 | 2.60k | JsonString key = stringBuilder_.str(); |
272 | | |
273 | 2.60k | TFilter memberFilter = filter[key]; |
274 | | |
275 | 2.60k | if (memberFilter.allow()) { |
276 | 2.60k | auto member = object.getMember(adaptString(key), resources_); |
277 | 2.60k | if (!member) { |
278 | | // Save key in memory pool. |
279 | 2.12k | auto savedKey = stringBuilder_.save(); |
280 | | |
281 | | // Allocate slot in object |
282 | 2.12k | member = object.addMember(savedKey, resources_); |
283 | 2.12k | if (!member) |
284 | 0 | return DeserializationError::NoMemory; |
285 | 2.12k | } else { |
286 | 485 | member->clear(resources_); |
287 | 485 | } |
288 | | |
289 | | // Parse value |
290 | 2.60k | err = parseVariant(*member, memberFilter, nestingLimit.decrement()); |
291 | 2.60k | if (err) |
292 | 763 | return err; |
293 | 2.60k | } else { |
294 | 0 | err = skipVariant(nestingLimit.decrement()); |
295 | 0 | if (err) |
296 | 0 | return err; |
297 | 0 | } |
298 | | |
299 | | // Skip spaces |
300 | 1.84k | err = skipSpacesAndComments(); |
301 | 1.84k | if (err) |
302 | 73 | return err; |
303 | | |
304 | | // More keys/values? |
305 | 1.77k | if (eat('}')) |
306 | 364 | return DeserializationError::Ok; |
307 | 1.40k | if (!eat(',')) |
308 | 21 | return DeserializationError::InvalidInput; |
309 | | |
310 | | // Skip spaces |
311 | 1.38k | err = skipSpacesAndComments(); |
312 | 1.38k | if (err) |
313 | 40 | return err; |
314 | 1.38k | } |
315 | 1.46k | } |
316 | | |
317 | | DeserializationError::Code skipObject( |
318 | 0 | DeserializationOption::NestingLimit nestingLimit) { |
319 | 0 | DeserializationError::Code err; |
320 | |
|
321 | 0 | if (nestingLimit.reached()) |
322 | 0 | return DeserializationError::TooDeep; |
323 | | |
324 | | // Skip opening brace |
325 | 0 | ARDUINOJSON_ASSERT(current() == '{'); |
326 | 0 | move(); |
327 | | |
328 | | // Skip spaces |
329 | 0 | err = skipSpacesAndComments(); |
330 | 0 | if (err) |
331 | 0 | return err; |
332 | | |
333 | | // Empty object? |
334 | 0 | if (eat('}')) |
335 | 0 | return DeserializationError::Ok; |
336 | | |
337 | | // Read each key value pair |
338 | 0 | for (;;) { |
339 | | // Skip key |
340 | 0 | err = skipKey(); |
341 | 0 | if (err) |
342 | 0 | return err; |
343 | | |
344 | | // Skip spaces |
345 | 0 | err = skipSpacesAndComments(); |
346 | 0 | if (err) |
347 | 0 | return err; |
348 | | |
349 | | // Colon |
350 | 0 | if (!eat(':')) |
351 | 0 | return DeserializationError::InvalidInput; |
352 | | |
353 | | // Skip value |
354 | 0 | err = skipVariant(nestingLimit.decrement()); |
355 | 0 | if (err) |
356 | 0 | return err; |
357 | | |
358 | | // Skip spaces |
359 | 0 | err = skipSpacesAndComments(); |
360 | 0 | if (err) |
361 | 0 | return err; |
362 | | |
363 | | // More keys/values? |
364 | 0 | if (eat('}')) |
365 | 0 | return DeserializationError::Ok; |
366 | 0 | if (!eat(',')) |
367 | 0 | return DeserializationError::InvalidInput; |
368 | | |
369 | 0 | err = skipSpacesAndComments(); |
370 | 0 | if (err) |
371 | 0 | return err; |
372 | 0 | } |
373 | 0 | } |
374 | | |
375 | 2.81k | DeserializationError::Code parseKey() { |
376 | 2.81k | stringBuilder_.startString(); |
377 | 2.81k | if (isQuote(current())) { |
378 | 181 | return parseQuotedString(); |
379 | 2.63k | } else { |
380 | 2.63k | return parseNonQuotedString(); |
381 | 2.63k | } |
382 | 2.81k | } |
383 | | |
384 | 1.28k | DeserializationError::Code parseStringValue(VariantData& variant) { |
385 | 1.28k | DeserializationError::Code err; |
386 | | |
387 | 1.28k | stringBuilder_.startString(); |
388 | | |
389 | 1.28k | err = parseQuotedString(); |
390 | 1.28k | if (err) |
391 | 238 | return err; |
392 | | |
393 | 1.04k | variant.setOwnedString(stringBuilder_.save()); |
394 | | |
395 | 1.04k | return DeserializationError::Ok; |
396 | 1.28k | } |
397 | | |
398 | 1.46k | DeserializationError::Code parseQuotedString() { |
399 | 1.46k | #if ARDUINOJSON_DECODE_UNICODE |
400 | 1.46k | Utf16::Codepoint codepoint; |
401 | 1.46k | DeserializationError::Code err; |
402 | 1.46k | #endif |
403 | 1.46k | const char stopChar = current(); |
404 | | |
405 | 1.46k | move(); |
406 | 5.60k | for (;;) { |
407 | 5.60k | char c = current(); |
408 | 5.60k | move(); |
409 | 5.60k | if (c == stopChar) |
410 | 1.21k | break; |
411 | | |
412 | 4.39k | if (c == '\0') |
413 | 209 | return DeserializationError::IncompleteInput; |
414 | | |
415 | 4.18k | if (c == '\\') { |
416 | 718 | c = current(); |
417 | | |
418 | 718 | if (c == '\0') |
419 | 2 | return DeserializationError::IncompleteInput; |
420 | | |
421 | 716 | if (c == 'u') { |
422 | 630 | #if ARDUINOJSON_DECODE_UNICODE |
423 | 630 | move(); |
424 | 630 | uint16_t codeunit; |
425 | 630 | err = parseHex4(codeunit); |
426 | 630 | if (err) |
427 | 24 | return err; |
428 | 606 | if (codepoint.append(codeunit)) |
429 | 525 | Utf8::encodeCodepoint(codepoint.value(), stringBuilder_); |
430 | | #else |
431 | | stringBuilder_.append('\\'); |
432 | | #endif |
433 | 606 | continue; |
434 | 630 | } |
435 | | |
436 | | // replace char |
437 | 86 | c = EscapeSequence::unescapeChar(c); |
438 | 86 | if (c == '\0') |
439 | 11 | return DeserializationError::InvalidInput; |
440 | 75 | move(); |
441 | 75 | } |
442 | | |
443 | 3.53k | stringBuilder_.append(c); |
444 | 3.53k | } |
445 | | |
446 | 1.21k | if (!stringBuilder_.isValid()) |
447 | 0 | return DeserializationError::NoMemory; |
448 | | |
449 | 1.21k | return DeserializationError::Ok; |
450 | 1.21k | } |
451 | | |
452 | 2.63k | DeserializationError::Code parseNonQuotedString() { |
453 | 2.63k | char c = current(); |
454 | 2.63k | ARDUINOJSON_ASSERT(c); |
455 | | |
456 | 2.63k | if (canBeInNonQuotedString(c)) { // no quotes |
457 | 5.24k | do { |
458 | 5.24k | move(); |
459 | 5.24k | stringBuilder_.append(c); |
460 | 5.24k | c = current(); |
461 | 5.24k | } while (canBeInNonQuotedString(c)); |
462 | 2.58k | } else { |
463 | 42 | return DeserializationError::InvalidInput; |
464 | 42 | } |
465 | | |
466 | 2.58k | if (!stringBuilder_.isValid()) |
467 | 0 | return DeserializationError::NoMemory; |
468 | | |
469 | 2.58k | return DeserializationError::Ok; |
470 | 2.58k | } |
471 | | |
472 | 0 | DeserializationError::Code skipKey() { |
473 | 0 | if (isQuote(current())) { |
474 | 0 | return skipQuotedString(); |
475 | 0 | } else { |
476 | 0 | return skipNonQuotedString(); |
477 | 0 | } |
478 | 0 | } |
479 | | |
480 | 0 | DeserializationError::Code skipQuotedString() { |
481 | 0 | const char stopChar = current(); |
482 | |
|
483 | 0 | move(); |
484 | 0 | for (;;) { |
485 | 0 | char c = current(); |
486 | 0 | move(); |
487 | 0 | if (c == stopChar) |
488 | 0 | break; |
489 | 0 | if (c == '\0') |
490 | 0 | return DeserializationError::IncompleteInput; |
491 | 0 | if (c == '\\') { |
492 | 0 | if (current() != '\0') |
493 | 0 | move(); |
494 | 0 | } |
495 | 0 | } |
496 | | |
497 | 0 | return DeserializationError::Ok; |
498 | 0 | } |
499 | | |
500 | 0 | DeserializationError::Code skipNonQuotedString() { |
501 | 0 | char c = current(); |
502 | 0 | while (canBeInNonQuotedString(c)) { |
503 | 0 | move(); |
504 | 0 | c = current(); |
505 | 0 | } |
506 | 0 | return DeserializationError::Ok; |
507 | 0 | } |
508 | | |
509 | 9.47k | DeserializationError::Code parseNumericValue(VariantData& result) { |
510 | 9.47k | uint8_t n = 0; |
511 | | |
512 | 9.47k | char c = current(); |
513 | 38.8k | while (canBeInNumber(c) && n < 63) { |
514 | 29.4k | move(); |
515 | 29.4k | buffer_[n++] = c; |
516 | 29.4k | c = current(); |
517 | 29.4k | } |
518 | 9.47k | buffer_[n] = 0; |
519 | | |
520 | 9.47k | auto number = parseNumber(buffer_); |
521 | 9.47k | switch (number.type()) { |
522 | 2.96k | case NumberType::UnsignedInteger: |
523 | 2.96k | if (result.setInteger(number.asUnsignedInteger(), resources_)) |
524 | 2.96k | return DeserializationError::Ok; |
525 | 0 | else |
526 | 0 | return DeserializationError::NoMemory; |
527 | | |
528 | 578 | case NumberType::SignedInteger: |
529 | 578 | if (result.setInteger(number.asSignedInteger(), resources_)) |
530 | 578 | return DeserializationError::Ok; |
531 | 0 | else |
532 | 0 | return DeserializationError::NoMemory; |
533 | | |
534 | 4.76k | case NumberType::Float: |
535 | 4.76k | if (result.setFloat(number.asFloat(), resources_)) |
536 | 4.76k | return DeserializationError::Ok; |
537 | 0 | else |
538 | 0 | return DeserializationError::NoMemory; |
539 | | |
540 | 0 | #if ARDUINOJSON_USE_DOUBLE |
541 | 1.02k | case NumberType::Double: |
542 | 1.02k | if (result.setFloat(number.asDouble(), resources_)) |
543 | 1.02k | return DeserializationError::Ok; |
544 | 0 | else |
545 | 0 | return DeserializationError::NoMemory; |
546 | 0 | #endif |
547 | | |
548 | 142 | default: |
549 | 142 | return DeserializationError::InvalidInput; |
550 | 9.47k | } |
551 | 9.47k | } |
552 | | |
553 | 0 | DeserializationError::Code skipNumericValue() { |
554 | 0 | char c = current(); |
555 | 0 | while (canBeInNumber(c)) { |
556 | 0 | move(); |
557 | 0 | c = current(); |
558 | 0 | } |
559 | 0 | return DeserializationError::Ok; |
560 | 0 | } |
561 | | |
562 | 630 | DeserializationError::Code parseHex4(uint16_t& result) { |
563 | 630 | result = 0; |
564 | 3.07k | for (uint8_t i = 0; i < 4; ++i) { |
565 | 2.46k | char digit = current(); |
566 | 2.46k | if (!digit) |
567 | 14 | return DeserializationError::IncompleteInput; |
568 | 2.45k | uint8_t value = decodeHex(digit); |
569 | 2.45k | if (value > 0x0F) |
570 | 10 | return DeserializationError::InvalidInput; |
571 | 2.44k | result = uint16_t((result << 4) | value); |
572 | 2.44k | move(); |
573 | 2.44k | } |
574 | 606 | return DeserializationError::Ok; |
575 | 630 | } |
576 | | |
577 | 57.0k | static inline bool isBetween(char c, char min, char max) { |
578 | 57.0k | return min <= c && c <= max; |
579 | 57.0k | } |
580 | | |
581 | 38.8k | static inline bool canBeInNumber(char c) { |
582 | 38.8k | return isBetween(c, '0', '9') || c == '+' || c == '-' || c == '.' || |
583 | | #if ARDUINOJSON_ENABLE_NAN || ARDUINOJSON_ENABLE_INFINITY |
584 | | isBetween(c, 'A', 'Z') || isBetween(c, 'a', 'z'); |
585 | | #else |
586 | 38.8k | c == 'e' || c == 'E'; |
587 | 38.8k | #endif |
588 | 38.8k | } |
589 | | |
590 | 7.87k | static inline bool canBeInNonQuotedString(char c) { |
591 | 7.87k | return isBetween(c, '0', '9') || isBetween(c, '_', 'z') || |
592 | 7.87k | isBetween(c, 'A', 'Z'); |
593 | 7.87k | } |
594 | | |
595 | 2.81k | static inline bool isQuote(char c) { |
596 | 2.81k | return c == '\'' || c == '\"'; |
597 | 2.81k | } |
598 | | |
599 | 2.45k | static inline uint8_t decodeHex(char c) { |
600 | 2.45k | if (c < 'A') |
601 | 1.64k | return uint8_t(c - '0'); |
602 | 814 | c = char(c & ~0x20); // uppercase |
603 | 814 | return uint8_t(c - 'A' + 10); |
604 | 2.45k | } |
605 | | |
606 | 36.8k | DeserializationError::Code skipSpacesAndComments() { |
607 | 42.9k | for (;;) { |
608 | 42.9k | switch (current()) { |
609 | | // end of string |
610 | 931 | case '\0': |
611 | 931 | return foundSomething_ ? DeserializationError::IncompleteInput |
612 | 931 | : DeserializationError::EmptyInput; |
613 | | |
614 | | // spaces |
615 | 1.57k | case ' ': |
616 | 2.96k | case '\t': |
617 | 4.37k | case '\r': |
618 | 6.07k | case '\n': |
619 | 6.07k | move(); |
620 | 6.07k | continue; |
621 | | |
622 | | #if ARDUINOJSON_ENABLE_COMMENTS |
623 | | // comments |
624 | | case '/': |
625 | | move(); // skip '/' |
626 | | switch (current()) { |
627 | | // block comment |
628 | | case '*': { |
629 | | move(); // skip '*' |
630 | | bool wasStar = false; |
631 | | for (;;) { |
632 | | char c = current(); |
633 | | if (c == '\0') |
634 | | return DeserializationError::IncompleteInput; |
635 | | if (c == '/' && wasStar) { |
636 | | move(); |
637 | | break; |
638 | | } |
639 | | wasStar = c == '*'; |
640 | | move(); |
641 | | } |
642 | | break; |
643 | | } |
644 | | |
645 | | // trailing comment |
646 | | case '/': |
647 | | // no need to skip "//" |
648 | | for (;;) { |
649 | | move(); |
650 | | char c = current(); |
651 | | if (c == '\0') |
652 | | return DeserializationError::IncompleteInput; |
653 | | if (c == '\n') |
654 | | break; |
655 | | } |
656 | | break; |
657 | | |
658 | | // not a comment, just a '/' |
659 | | default: |
660 | | return DeserializationError::InvalidInput; |
661 | | } |
662 | | break; |
663 | | #endif |
664 | | |
665 | 35.9k | default: |
666 | 35.9k | foundSomething_ = true; |
667 | 35.9k | return DeserializationError::Ok; |
668 | 42.9k | } |
669 | 42.9k | } |
670 | 36.8k | } |
671 | | |
672 | 804 | DeserializationError::Code skipKeyword(const char* s) { |
673 | 4.02k | while (*s) { |
674 | 3.30k | char c = current(); |
675 | 3.30k | if (c == '\0') |
676 | 37 | return DeserializationError::IncompleteInput; |
677 | 3.26k | if (*s != c) |
678 | 45 | return DeserializationError::InvalidInput; |
679 | 3.21k | ++s; |
680 | 3.21k | move(); |
681 | 3.21k | } |
682 | 722 | return DeserializationError::Ok; |
683 | 804 | } |
684 | | |
685 | | StringBuilder stringBuilder_; |
686 | | bool foundSomething_; |
687 | | Latch<TReader> latch_; |
688 | | ResourceManager* resources_; |
689 | | char buffer_[64]; // using a member instead of a local variable because it |
690 | | // ended in the recursive path after compiler inlined the |
691 | | // code |
692 | | }; |
693 | | |
694 | | ARDUINOJSON_END_PRIVATE_NAMESPACE |
695 | | |
696 | | ARDUINOJSON_BEGIN_PUBLIC_NAMESPACE |
697 | | |
698 | | // Parses a JSON input, filters, and puts the result in a JsonDocument. |
699 | | // https://arduinojson.org/v7/api/json/deserializejson/ |
700 | | template <typename TDestination, typename... Args, |
701 | | detail::enable_if_t< |
702 | | detail::is_deserialize_destination<TDestination>::value, int> = 0> |
703 | | inline DeserializationError deserializeJson(TDestination&& dst, |
704 | | Args&&... args) { |
705 | | using namespace detail; |
706 | | return deserialize<JsonDeserializer>(detail::forward<TDestination>(dst), |
707 | | detail::forward<Args>(args)...); |
708 | | } |
709 | | |
710 | | // Parses a JSON input, filters, and puts the result in a JsonDocument. |
711 | | // https://arduinojson.org/v7/api/json/deserializejson/ |
712 | | template <typename TDestination, typename TChar, typename... Args, |
713 | | detail::enable_if_t< |
714 | | detail::is_deserialize_destination<TDestination>::value, int> = 0> |
715 | | inline DeserializationError deserializeJson(TDestination&& dst, TChar* input, |
716 | 2.46k | Args&&... args) { |
717 | 2.46k | using namespace detail; |
718 | 2.46k | return deserialize<JsonDeserializer>(detail::forward<TDestination>(dst), |
719 | 2.46k | input, detail::forward<Args>(args)...); |
720 | 2.46k | } |
721 | | |
722 | | ARDUINOJSON_END_PUBLIC_NAMESPACE |