1 /*
2 * Copyright 2017-2023, Andrew Lindesay <apl@lindesay.co.nz>
3 * Copyright 2014-2017, Augustin Cavalier (waddlesplash)
4 * Copyright 2014, Stephan Aßmus <superstippi@gmx.de>
5 * Distributed under the terms of the MIT License.
6 */
7
8
9 #include "Json.h"
10
11 #include <cstdio>
12 #include <cstdlib>
13 #include <ctype.h>
14 #include <cerrno>
15
16 #include <AutoDeleter.h>
17 #include <DataIO.h>
18 #include <UnicodeChar.h>
19
20 #include "JsonEventListener.h"
21 #include "JsonMessageWriter.h"
22
23
24 // #pragma mark - Public methods
25
26 namespace BPrivate {
27
28 /*! A buffer is used to assemble strings into. This will be the initial size
29 of this buffer.
30 */
31
32 static const size_t kInitialAssemblyBufferSize = 64;
33
34 /*! A buffer is used to assemble strings into. This buffer starts off small
35 but is able to grow as the string it needs to process as encountered. To
36 avoid frequent reallocation of the buffer, the buffer will be retained
37 between strings. This is the maximum size of buffer that will be retained.
38 */
39
40 static const size_t kRetainedAssemblyBufferSize = 32 * 1024;
41
42 static const size_t kAssemblyBufferSizeIncrement = 256;
43
44 static const size_t kMaximumUtf8SequenceLength = 7;
45
46
47 class JsonParseAssemblyBuffer {
48 public:
JsonParseAssemblyBuffer()49 JsonParseAssemblyBuffer()
50 :
51 fAssemblyBuffer(NULL),
52 fAssemblyBufferAllocatedSize(0),
53 fAssemblyBufferUsedSize(0)
54 {
55 fAssemblyBuffer = (char*) malloc(kInitialAssemblyBufferSize);
56 if (fAssemblyBuffer != NULL)
57 fAssemblyBufferAllocatedSize = kInitialAssemblyBufferSize;
58 }
59
~JsonParseAssemblyBuffer()60 ~JsonParseAssemblyBuffer()
61 {
62 if (fAssemblyBuffer != NULL)
63 free(fAssemblyBuffer);
64 }
65
Buffer() const66 const char* Buffer() const
67 {
68 return fAssemblyBuffer;
69 }
70
71 /*! This method should be used each time that the assembly buffer has
72 been finished with by some section of logic.
73 */
74
Reset()75 status_t Reset()
76 {
77 fAssemblyBufferUsedSize = 0;
78
79 if (fAssemblyBufferAllocatedSize > kRetainedAssemblyBufferSize) {
80 fAssemblyBuffer = (char*) realloc(fAssemblyBuffer, kRetainedAssemblyBufferSize);
81 if (fAssemblyBuffer == NULL) {
82 fAssemblyBufferAllocatedSize = 0;
83 return B_NO_MEMORY;
84 }
85 fAssemblyBufferAllocatedSize = kRetainedAssemblyBufferSize;
86 }
87
88 return B_OK;
89 }
90
AppendCharacter(char c)91 status_t AppendCharacter(char c)
92 {
93 status_t result = _EnsureAssemblyBufferAllocatedSize(fAssemblyBufferUsedSize + 1);
94
95 if (result == B_OK) {
96 fAssemblyBuffer[fAssemblyBufferUsedSize] = c;
97 fAssemblyBufferUsedSize++;
98 }
99
100 return result;
101 }
102
AppendCharacters(char * str,size_t len)103 status_t AppendCharacters(char* str, size_t len)
104 {
105 status_t result = _EnsureAssemblyBufferAllocatedSize(fAssemblyBufferUsedSize + len);
106
107 if (result == B_OK) {
108 memcpy(&fAssemblyBuffer[fAssemblyBufferUsedSize], str, len);
109 fAssemblyBufferUsedSize += len;
110 }
111
112 return result;
113 }
114
AppendUnicodeCharacter(uint32 c)115 status_t AppendUnicodeCharacter(uint32 c)
116 {
117 status_t result = _EnsureAssemblyBufferAllocatedSize(
118 fAssemblyBufferUsedSize + kMaximumUtf8SequenceLength);
119 if (result == B_OK) {
120 char* insertPtr = &fAssemblyBuffer[fAssemblyBufferUsedSize];
121 char* ptr = insertPtr;
122 BUnicodeChar::ToUTF8(c, &ptr);
123 size_t sequenceLength = static_cast<uint32>(ptr - insertPtr);
124 fAssemblyBufferUsedSize += sequenceLength;
125 }
126
127 return result;
128 }
129
130 private:
131
132 /*! This method will return the assembly buffer ensuring that it has at
133 least `minimumSize` bytes available.
134 */
135
_EnsureAssemblyBufferAllocatedSize(size_t minimumSize)136 status_t _EnsureAssemblyBufferAllocatedSize(size_t minimumSize)
137 {
138 if (fAssemblyBufferAllocatedSize < minimumSize) {
139 size_t requestedSize = minimumSize;
140
141 // if the requested quantity of memory is less than the retained buffer size then
142 // it makes sense to request a wee bit more in order to reduce the number of small
143 // requests to increment the buffer over time.
144
145 if (requestedSize < kRetainedAssemblyBufferSize - kAssemblyBufferSizeIncrement) {
146 requestedSize = ((requestedSize / kAssemblyBufferSizeIncrement) + 1)
147 * kAssemblyBufferSizeIncrement;
148 }
149
150 fAssemblyBuffer = (char*) realloc(fAssemblyBuffer, requestedSize);
151 if (fAssemblyBuffer == NULL) {
152 fAssemblyBufferAllocatedSize = 0;
153 return B_NO_MEMORY;
154 }
155 fAssemblyBufferAllocatedSize = requestedSize;
156 }
157 return B_OK;
158 }
159
160 private:
161 char* fAssemblyBuffer;
162 size_t fAssemblyBufferAllocatedSize;
163 size_t fAssemblyBufferUsedSize;
164 };
165
166
167 class JsonParseAssemblyBufferResetter {
168 public:
JsonParseAssemblyBufferResetter(JsonParseAssemblyBuffer * assemblyBuffer)169 JsonParseAssemblyBufferResetter(JsonParseAssemblyBuffer* assemblyBuffer)
170 :
171 fAssemblyBuffer(assemblyBuffer)
172 {
173 }
174
~JsonParseAssemblyBufferResetter()175 ~JsonParseAssemblyBufferResetter()
176 {
177 fAssemblyBuffer->Reset();
178 }
179
180 private:
181 JsonParseAssemblyBuffer*
182 fAssemblyBuffer;
183 };
184
185
186 /*! This class carries state around the parsing process. */
187
188 class JsonParseContext {
189 public:
JsonParseContext(BDataIO * data,BJsonEventListener * listener)190 JsonParseContext(BDataIO* data, BJsonEventListener* listener)
191 :
192 fListener(listener),
193 fData(data),
194 fLineNumber(1), // 1 is the first line
195 fPushbackChar(0),
196 fHasPushbackChar(false),
197 fAssemblyBuffer(new JsonParseAssemblyBuffer())
198 {
199 }
200
201
~JsonParseContext()202 ~JsonParseContext()
203 {
204 delete fAssemblyBuffer;
205 }
206
207
Listener() const208 BJsonEventListener* Listener() const
209 {
210 return fListener;
211 }
212
213
Data() const214 BDataIO* Data() const
215 {
216 return fData;
217 }
218
219
LineNumber() const220 int LineNumber() const
221 {
222 return fLineNumber;
223 }
224
225
IncrementLineNumber()226 void IncrementLineNumber()
227 {
228 fLineNumber++;
229 }
230
NextChar(char * buffer)231 status_t NextChar(char* buffer)
232 {
233 if (fHasPushbackChar) {
234 buffer[0] = fPushbackChar;
235 fHasPushbackChar = false;
236 return B_OK;
237 }
238
239 return Data()->ReadExactly(buffer, 1);
240 }
241
PushbackChar(char c)242 void PushbackChar(char c)
243 {
244 if (fHasPushbackChar)
245 debugger("illegal state - more than one character pushed back");
246 fPushbackChar = c;
247 fHasPushbackChar = true;
248 }
249
250
AssemblyBuffer()251 JsonParseAssemblyBuffer* AssemblyBuffer()
252 {
253 return fAssemblyBuffer;
254 }
255
256
257 private:
258 BJsonEventListener* fListener;
259 BDataIO* fData;
260 uint32 fLineNumber;
261 char fPushbackChar;
262 bool fHasPushbackChar;
263 JsonParseAssemblyBuffer*
264 fAssemblyBuffer;
265 };
266
267
268 status_t
Parse(const BString & JSON,BMessage & message)269 BJson::Parse(const BString& JSON, BMessage& message)
270 {
271 return Parse(JSON.String(), message);
272 }
273
274
275 status_t
Parse(const char * JSON,size_t length,BMessage & message)276 BJson::Parse(const char* JSON, size_t length, BMessage& message)
277 {
278 BMemoryIO* input = new BMemoryIO(JSON, length);
279 ObjectDeleter<BMemoryIO> inputDeleter(input);
280 BJsonMessageWriter* writer = new BJsonMessageWriter(message);
281 ObjectDeleter<BJsonMessageWriter> writerDeleter(writer);
282
283 Parse(input, writer);
284 status_t result = writer->ErrorStatus();
285
286 return result;
287 }
288
289
290 status_t
Parse(const char * JSON,BMessage & message)291 BJson::Parse(const char* JSON, BMessage& message)
292 {
293 return Parse(JSON, strlen(JSON), message);
294 }
295
296
297 /*! The data is read as a stream of JSON data. As the JSON is read, events are
298 raised such as;
299 - string
300 - number
301 - true
302 - array start
303 - object end
304 Each event is sent to the listener to process as required.
305 */
306
307 void
Parse(BDataIO * data,BJsonEventListener * listener)308 BJson::Parse(BDataIO* data, BJsonEventListener* listener)
309 {
310 JsonParseContext context(data, listener);
311 ParseAny(context);
312 listener->Complete();
313 }
314
315
316 // #pragma mark - Specific parse logic.
317
318
319 bool
NextChar(JsonParseContext & jsonParseContext,char * c)320 BJson::NextChar(JsonParseContext& jsonParseContext, char* c)
321 {
322 status_t result = jsonParseContext.NextChar(c);
323
324 switch (result) {
325 case B_OK:
326 return true;
327
328 case B_PARTIAL_READ:
329 {
330 jsonParseContext.Listener()->HandleError(B_BAD_DATA,
331 jsonParseContext.LineNumber(), "unexpected end of input");
332 return false;
333 }
334
335 default:
336 {
337 jsonParseContext.Listener()->HandleError(result, -1,
338 "io related read error");
339 return false;
340 }
341 }
342 }
343
344
345 bool
NextNonWhitespaceChar(JsonParseContext & jsonParseContext,char * c)346 BJson::NextNonWhitespaceChar(JsonParseContext& jsonParseContext, char* c)
347 {
348 while (true) {
349 if (!NextChar(jsonParseContext, c))
350 return false;
351
352 switch (*c) {
353 case 0x0a: // newline
354 case 0x0d: // cr
355 jsonParseContext.IncrementLineNumber();
356 case ' ': // space
357 // swallow whitespace as it is not syntactically
358 // significant.
359 break;
360
361 default:
362 return true;
363 }
364 }
365 }
366
367
368 bool
ParseAny(JsonParseContext & jsonParseContext)369 BJson::ParseAny(JsonParseContext& jsonParseContext)
370 {
371 char c;
372
373 if (!NextNonWhitespaceChar(jsonParseContext, &c))
374 return false;
375
376 switch (c) {
377 case 'f': // [f]alse
378 return ParseExpectedVerbatimStringAndRaiseEvent(
379 jsonParseContext, "alse", 4, 'f', B_JSON_FALSE);
380
381 case 't': // [t]rue
382 return ParseExpectedVerbatimStringAndRaiseEvent(
383 jsonParseContext, "rue", 3, 't', B_JSON_TRUE);
384
385 case 'n': // [n]ull
386 return ParseExpectedVerbatimStringAndRaiseEvent(
387 jsonParseContext, "ull", 3, 'n', B_JSON_NULL);
388
389 case '"':
390 return ParseString(jsonParseContext, B_JSON_STRING);
391
392 case '{':
393 return ParseObject(jsonParseContext);
394
395 case '[':
396 return ParseArray(jsonParseContext);
397
398 case '+':
399 case '-':
400 case '0':
401 case '1':
402 case '2':
403 case '3':
404 case '4':
405 case '5':
406 case '6':
407 case '7':
408 case '8':
409 case '9':
410 jsonParseContext.PushbackChar(c); // keeps the parse simple
411 return ParseNumber(jsonParseContext);
412
413 default:
414 {
415 BString errorMessage;
416 if (c >= 0x20 && c < 0x7f) {
417 errorMessage.SetToFormat("unexpected character [%" B_PRIu8 "]"
418 " (%c) when parsing element", static_cast<uint8>(c), c);
419 } else {
420 errorMessage.SetToFormat("unexpected character [%" B_PRIu8 "]"
421 " when parsing element", (uint8) c);
422 }
423 jsonParseContext.Listener()->HandleError(B_BAD_DATA,
424 jsonParseContext.LineNumber(), errorMessage.String());
425 return false;
426 }
427 }
428
429 return true;
430 }
431
432
433 /*! This method captures an object name, a separator ':' and then any value. */
434
435 bool
ParseObjectNameValuePair(JsonParseContext & jsonParseContext)436 BJson::ParseObjectNameValuePair(JsonParseContext& jsonParseContext)
437 {
438 bool didParseName = false;
439 char c;
440
441 while (true) {
442 if (!NextNonWhitespaceChar(jsonParseContext, &c))
443 return false;
444
445 switch (c) {
446 case '\"': // name of the object
447 {
448 if (!didParseName) {
449 if (!ParseString(jsonParseContext, B_JSON_OBJECT_NAME))
450 return false;
451
452 didParseName = true;
453 } else {
454 jsonParseContext.Listener()->HandleError(B_BAD_DATA,
455 jsonParseContext.LineNumber(), "unexpected"
456 " [\"] character when parsing object name-"
457 " value separator");
458 return false;
459 }
460 break;
461 }
462
463 case ':': // separator
464 {
465 if (didParseName) {
466 if (!ParseAny(jsonParseContext))
467 return false;
468 return true;
469 } else {
470 jsonParseContext.Listener()->HandleError(B_BAD_DATA,
471 jsonParseContext.LineNumber(), "unexpected"
472 " [:] character when parsing object name-"
473 " value pair");
474 return false;
475 }
476 }
477
478 default:
479 {
480 BString errorMessage;
481 errorMessage.SetToFormat(
482 "unexpected character [%c] when parsing object"
483 " name-value pair",
484 c);
485 jsonParseContext.Listener()->HandleError(B_BAD_DATA,
486 jsonParseContext.LineNumber(), errorMessage.String());
487 return false;
488 }
489 }
490 }
491 }
492
493
494 bool
ParseObject(JsonParseContext & jsonParseContext)495 BJson::ParseObject(JsonParseContext& jsonParseContext)
496 {
497 if (!jsonParseContext.Listener()->Handle(
498 BJsonEvent(B_JSON_OBJECT_START))) {
499 return false;
500 }
501
502 char c;
503 bool firstItem = true;
504
505 while (true) {
506 if (!NextNonWhitespaceChar(jsonParseContext, &c))
507 return false;
508
509 switch (c) {
510 case '}': // terminate the object
511 {
512 if (!jsonParseContext.Listener()->Handle(
513 BJsonEvent(B_JSON_OBJECT_END))) {
514 return false;
515 }
516 return true;
517 }
518
519 case ',': // next value.
520 {
521 if (firstItem) {
522 jsonParseContext.Listener()->HandleError(B_BAD_DATA,
523 jsonParseContext.LineNumber(), "unexpected"
524 " item separator when parsing start of"
525 " object");
526 return false;
527 }
528
529 if (!ParseObjectNameValuePair(jsonParseContext))
530 return false;
531 break;
532 }
533
534 default:
535 {
536 if (firstItem) {
537 jsonParseContext.PushbackChar(c);
538 if (!ParseObjectNameValuePair(jsonParseContext))
539 return false;
540 firstItem = false;
541 } else {
542 jsonParseContext.Listener()->HandleError(B_BAD_DATA,
543 jsonParseContext.LineNumber(), "expected"
544 " separator when parsing an object");
545 }
546 }
547 }
548 }
549
550 return true;
551 }
552
553
554 bool
ParseArray(JsonParseContext & jsonParseContext)555 BJson::ParseArray(JsonParseContext& jsonParseContext)
556 {
557 if (!jsonParseContext.Listener()->Handle(
558 BJsonEvent(B_JSON_ARRAY_START))) {
559 return false;
560 }
561
562 char c;
563 bool firstItem = true;
564
565 while (true) {
566 if (!NextNonWhitespaceChar(jsonParseContext, &c))
567 return false;
568
569 switch (c) {
570 case ']': // terminate the array
571 {
572 if (!jsonParseContext.Listener()->Handle(
573 BJsonEvent(B_JSON_ARRAY_END))) {
574 return false;
575 }
576 return true;
577 }
578
579 case ',': // next value.
580 {
581 if (firstItem) {
582 jsonParseContext.Listener()->HandleError(B_BAD_DATA,
583 jsonParseContext.LineNumber(), "unexpected"
584 " item separator when parsing start of"
585 " array");
586 }
587
588 if (!ParseAny(jsonParseContext))
589 return false;
590 break;
591 }
592
593 default:
594 {
595 if (firstItem) {
596 jsonParseContext.PushbackChar(c);
597 if (!ParseAny(jsonParseContext))
598 return false;
599 firstItem = false;
600 } else {
601 jsonParseContext.Listener()->HandleError(B_BAD_DATA,
602 jsonParseContext.LineNumber(), "expected"
603 " separator when parsing an array");
604 }
605 }
606 }
607 }
608
609 return true;
610 }
611
612
613 bool
ParseEscapeUnicodeSequence(JsonParseContext & jsonParseContext)614 BJson::ParseEscapeUnicodeSequence(JsonParseContext& jsonParseContext)
615 {
616 char ch;
617 uint32 unicodeCh = 0;
618
619 for (int i = 3; i >= 0; i--) {
620 if (!NextChar(jsonParseContext, &ch)) {
621 jsonParseContext.Listener()->HandleError(B_ERROR, jsonParseContext.LineNumber(),
622 "unable to read unicode sequence");
623 return false;
624 }
625
626 if (ch >= '0' && ch <= '9')
627 unicodeCh |= static_cast<uint32>(ch - '0') << (i * 4);
628 else if (ch >= 'a' && ch <= 'f')
629 unicodeCh |= (10 + static_cast<uint32>(ch - 'a')) << (i * 4);
630 else if (ch >= 'A' && ch <= 'F')
631 unicodeCh |= (10 + static_cast<uint32>(ch - 'A')) << (i * 4);
632 else {
633 BString errorMessage;
634 errorMessage.SetToFormat(
635 "malformed hex character [%c] in unicode sequence in string parsing", ch);
636 jsonParseContext.Listener()->HandleError(B_BAD_DATA, jsonParseContext.LineNumber(),
637 errorMessage.String());
638 return false;
639 }
640 }
641
642 JsonParseAssemblyBuffer* assemblyBuffer = jsonParseContext.AssemblyBuffer();
643 status_t result = assemblyBuffer->AppendUnicodeCharacter(unicodeCh);
644
645 if (result != B_OK) {
646 jsonParseContext.Listener()->HandleError(result, jsonParseContext.LineNumber(),
647 "unable to store unicode char as utf-8");
648 return false;
649 }
650
651 return true;
652 }
653
654
655 bool
ParseStringEscapeSequence(JsonParseContext & jsonParseContext)656 BJson::ParseStringEscapeSequence(JsonParseContext& jsonParseContext)
657 {
658 char c;
659
660 if (!NextChar(jsonParseContext, &c))
661 return false;
662
663 JsonParseAssemblyBuffer* assemblyBuffer = jsonParseContext.AssemblyBuffer();
664
665 switch (c) {
666 case 'n':
667 assemblyBuffer->AppendCharacter('\n');
668 break;
669 case 'r':
670 assemblyBuffer->AppendCharacter('\r');
671 break;
672 case 'b':
673 assemblyBuffer->AppendCharacter('\b');
674 break;
675 case 'f':
676 assemblyBuffer->AppendCharacter('\f');
677 break;
678 case '\\':
679 assemblyBuffer->AppendCharacter('\\');
680 break;
681 case '/':
682 assemblyBuffer->AppendCharacter('/');
683 break;
684 case 't':
685 assemblyBuffer->AppendCharacter('\t');
686 break;
687 case '"':
688 assemblyBuffer->AppendCharacter('"');
689 break;
690 case 'u':
691 {
692 // unicode escape sequence.
693 if (!ParseEscapeUnicodeSequence(jsonParseContext)) {
694 return false;
695 }
696 break;
697 }
698 default:
699 {
700 BString errorMessage;
701 errorMessage.SetToFormat("unexpected escaped character [%c] in string parsing", c);
702 jsonParseContext.Listener()->HandleError(B_BAD_DATA,
703 jsonParseContext.LineNumber(), errorMessage.String());
704 return false;
705 }
706 }
707
708 return true;
709 }
710
711
712 bool
ParseString(JsonParseContext & jsonParseContext,json_event_type eventType)713 BJson::ParseString(JsonParseContext& jsonParseContext,
714 json_event_type eventType)
715 {
716 char c;
717 JsonParseAssemblyBuffer* assemblyBuffer = jsonParseContext.AssemblyBuffer();
718 JsonParseAssemblyBufferResetter assembleBufferResetter(assemblyBuffer);
719
720 while(true) {
721 if (!NextChar(jsonParseContext, &c))
722 return false;
723
724 switch (c) {
725 case '"':
726 {
727 // terminates the string assembled so far.
728 assemblyBuffer->AppendCharacter(0);
729 jsonParseContext.Listener()->Handle(
730 BJsonEvent(eventType, assemblyBuffer->Buffer()));
731 return true;
732 }
733
734 case '\\':
735 {
736 if (!ParseStringEscapeSequence(jsonParseContext))
737 return false;
738 break;
739 }
740
741 default:
742 {
743 uint8 uc = static_cast<uint8>(c);
744
745 if(uc < 0x20) { // control characters are not allowed
746 BString errorMessage;
747 errorMessage.SetToFormat("illegal control character"
748 " [%" B_PRIu8 "] when parsing a string", uc);
749 jsonParseContext.Listener()->HandleError(B_BAD_DATA,
750 jsonParseContext.LineNumber(),
751 errorMessage.String());
752 return false;
753 }
754
755 assemblyBuffer->AppendCharacter(c);
756 break;
757 }
758 }
759 }
760 }
761
762
763 bool
ParseExpectedVerbatimStringAndRaiseEvent(JsonParseContext & jsonParseContext,const char * expectedString,size_t expectedStringLength,char leadingChar,json_event_type jsonEventType)764 BJson::ParseExpectedVerbatimStringAndRaiseEvent(
765 JsonParseContext& jsonParseContext, const char* expectedString,
766 size_t expectedStringLength, char leadingChar,
767 json_event_type jsonEventType)
768 {
769 if (ParseExpectedVerbatimString(jsonParseContext, expectedString,
770 expectedStringLength, leadingChar)) {
771 if (!jsonParseContext.Listener()->Handle(BJsonEvent(jsonEventType)))
772 return false;
773 }
774
775 return true;
776 }
777
778 /*! This will make sure that the constant string is available at the input. */
779
780 bool
ParseExpectedVerbatimString(JsonParseContext & jsonParseContext,const char * expectedString,size_t expectedStringLength,char leadingChar)781 BJson::ParseExpectedVerbatimString(JsonParseContext& jsonParseContext,
782 const char* expectedString, size_t expectedStringLength, char leadingChar)
783 {
784 char c;
785 size_t offset = 0;
786
787 while (offset < expectedStringLength) {
788 if (!NextChar(jsonParseContext, &c))
789 return false;
790
791 if (c != expectedString[offset]) {
792 BString errorMessage;
793 errorMessage.SetToFormat("malformed json primative literal; "
794 "expected [%c%s], but got [%c] at position %" B_PRIdSSIZE,
795 leadingChar, expectedString, c, offset);
796 jsonParseContext.Listener()->HandleError(B_BAD_DATA,
797 jsonParseContext.LineNumber(), errorMessage.String());
798 return false;
799 }
800
801 offset++;
802 }
803
804 return true;
805 }
806
807
808 /*! This function checks to see that the supplied string is a well formed
809 JSON number. It does this from a string rather than a stream for
810 convenience. This is not anticipated to impact performance because
811 the string values are short.
812 */
813
814 bool
IsValidNumber(const char * value)815 BJson::IsValidNumber(const char* value)
816 {
817 int32 offset = 0;
818 int32 len = strlen(value);
819
820 if (offset < len && value[offset] == '-')
821 offset++;
822
823 if (offset >= len)
824 return false;
825
826 if (isdigit(value[offset]) && value[offset] != '0') {
827 while (offset < len && isdigit(value[offset]))
828 offset++;
829 } else {
830 if (value[offset] == '0')
831 offset++;
832 else
833 return false;
834 }
835
836 if (offset < len && value[offset] == '.') {
837 offset++;
838
839 if (offset >= len)
840 return false;
841
842 while (offset < len && isdigit(value[offset]))
843 offset++;
844 }
845
846 if (offset < len && (value[offset] == 'E' || value[offset] == 'e')) {
847 offset++;
848
849 if(offset < len && (value[offset] == '+' || value[offset] == '-'))
850 offset++;
851
852 if (offset >= len)
853 return false;
854
855 while (offset < len && isdigit(value[offset]))
856 offset++;
857 }
858
859 return offset == len;
860 }
861
862
863 /*! Note that this method hits the 'NextChar' method on the context directly
864 and handles any end-of-file state itself because it is feasible that the
865 entire JSON payload is a number and because (unlike other structures, the
866 number can take the end-of-file to signify the end of the number.
867 */
868
869 bool
ParseNumber(JsonParseContext & jsonParseContext)870 BJson::ParseNumber(JsonParseContext& jsonParseContext)
871 {
872 JsonParseAssemblyBuffer* assemblyBuffer = jsonParseContext.AssemblyBuffer();
873 JsonParseAssemblyBufferResetter assembleBufferResetter(assemblyBuffer);
874
875 while (true) {
876 char c;
877 status_t result = jsonParseContext.NextChar(&c);
878
879 switch (result) {
880 case B_OK:
881 {
882 if (isdigit(c) || c == '.' || c == '-' || c == 'e' || c == 'E' || c == '+') {
883 assemblyBuffer->AppendCharacter(c);
884 break;
885 }
886
887 jsonParseContext.PushbackChar(c);
888 // intentional fall through
889 }
890 case B_PARTIAL_READ:
891 {
892 errno = 0;
893 assemblyBuffer->AppendCharacter(0);
894
895 if (!IsValidNumber(assemblyBuffer->Buffer())) {
896 jsonParseContext.Listener()->HandleError(B_BAD_DATA,
897 jsonParseContext.LineNumber(), "malformed number");
898 return false;
899 }
900
901 jsonParseContext.Listener()->Handle(BJsonEvent(B_JSON_NUMBER,
902 assemblyBuffer->Buffer()));
903
904 return true;
905 }
906 default:
907 {
908 jsonParseContext.Listener()->HandleError(result, -1,
909 "io related read error");
910 return false;
911 }
912 }
913 }
914 }
915
916 } // namespace BPrivate
917