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