xref: /haiku/src/kits/shared/Json.cpp (revision 3634f142352af2428aed187781fc9d75075e9140)
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:
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 
60 	~JsonParseAssemblyBuffer()
61 	{
62 		if (fAssemblyBuffer != NULL)
63 			free(fAssemblyBuffer);
64 	}
65 
66 	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 
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 
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 
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 
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 
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:
169 	JsonParseAssemblyBufferResetter(JsonParseAssemblyBuffer* assemblyBuffer)
170 		:
171 		fAssemblyBuffer(assemblyBuffer)
172 	{
173 	}
174 
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:
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 
202 	~JsonParseContext()
203 	{
204 		delete fAssemblyBuffer;
205 	}
206 
207 
208 	BJsonEventListener* Listener() const
209 	{
210 		return fListener;
211 	}
212 
213 
214 	BDataIO* Data() const
215 	{
216 		return fData;
217 	}
218 
219 
220 	int LineNumber() const
221 	{
222 		return fLineNumber;
223 	}
224 
225 
226 	void IncrementLineNumber()
227 	{
228 		fLineNumber++;
229 	}
230 
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 
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 
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
269 BJson::Parse(const BString& JSON, BMessage& message)
270 {
271 	return Parse(JSON.String(), message);
272 }
273 
274 
275 status_t
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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