KPN IoT / senml

Fork of kpn_senml by KPN IoT

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers senml_JsonStreamingParser.cpp Source File

senml_JsonStreamingParser.cpp

00001 /**The MIT License (MIT)
00002 
00003 Copyright (c) 2015 by Daniel Eichhorn
00004 
00005 Permission is hereby granted, free of charge, to any person obtaining a copy
00006 of this software and associated documentation files (the "Software"), to deal
00007 in the Software without restriction, including without limitation the rights
00008 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
00009 copies of the Software, and to permit persons to whom the Software is
00010 furnished to do so, subject to the following conditions:
00011 
00012 The above copyright notice and this permission notice shall be included in all
00013 copies or substantial portions of the Software.
00014 
00015 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00016 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00017 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
00018 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00019 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00020 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
00021 SOFTWARE.
00022 
00023 See more at http://blog.squix.ch and https://github.com/squix78/json-streaming-parser
00024 */
00025 
00026 #include "senml_JsonStreamingParser.h"
00027 
00028 JsonStreamingParser::JsonStreamingParser(): stackPos(0), bufferPos(0), unicodeEscapeBufferPos(0),
00029                                             unicodeBufferPos(0), characterCounter(0), unicodeHighSurrogate(0) {
00030     reset();
00031 }
00032 
00033 void JsonStreamingParser::reset() {
00034     state = STATE_START_DOCUMENT;
00035     bufferPos = 0;
00036     unicodeEscapeBufferPos = 0;
00037     unicodeBufferPos = 0;
00038     characterCounter = 0;
00039 }
00040 
00041 void JsonStreamingParser::setListener(JsonListener* listener) {
00042   myListener = listener;
00043 }
00044 
00045 void JsonStreamingParser::parse(char c) {
00046     //System.out.print(c);
00047     // valid whitespace characters in JSON (from RFC4627 for JSON) include:
00048     // space, horizontal tab, line feed or new line, and carriage return.
00049     // thanks:
00050     // http://stackoverflow.com/questions/16042274/definition-of-whitespace-in-json
00051     if ((c == ' ' || c == '\t' || c == '\n' || c == '\r')
00052         && !(state == STATE_IN_STRING || state == STATE_UNICODE || state == STATE_START_ESCAPE
00053             || state == STATE_IN_NUMBER || state == STATE_START_DOCUMENT)) {
00054       return;
00055     }
00056     switch (state) {
00057     case STATE_IN_STRING:
00058       if (c == '"') {
00059         endString();
00060       } else if (c == '\\') {
00061         state = STATE_START_ESCAPE;
00062       } else if ((c < 0x1f) || (c == 0x7f)) {
00063         //throw new RuntimeException("Unescaped control character encountered: " + c + " at position" + characterCounter);
00064       } else {
00065         buffer[bufferPos] = c;
00066         increaseBufferPointer();
00067       }
00068       break;
00069     case STATE_IN_ARRAY:
00070       if (c == ']') {
00071         endArray();
00072       } else {
00073         startValue(c);
00074       }
00075       break;
00076     case STATE_IN_OBJECT:
00077       if (c == '}') {
00078         endObject();
00079       } else if (c == '"') {
00080         startKey();
00081       } else {
00082         //throw new RuntimeException("Start of string expected for object key. Instead got: " + c + " at position" + characterCounter);
00083       }
00084       break;
00085     case STATE_END_KEY:
00086       if (c != ':') {
00087         //throw new RuntimeException("Expected ':' after key. Instead got " + c + " at position" + characterCounter);
00088       }
00089       state = STATE_AFTER_KEY;
00090       break;
00091     case STATE_AFTER_KEY:
00092       startValue(c);
00093       break;
00094     case STATE_START_ESCAPE:
00095       processEscapeCharacters(c);
00096       break;
00097     case STATE_UNICODE:
00098       processUnicodeCharacter(c);
00099       break;
00100     case STATE_UNICODE_SURROGATE:
00101       unicodeEscapeBuffer[unicodeEscapeBufferPos] = c;
00102       unicodeEscapeBufferPos++;
00103       if (unicodeEscapeBufferPos == 2) {
00104         endUnicodeSurrogateInterstitial();
00105       }
00106       break;
00107     case STATE_AFTER_VALUE: {
00108       // not safe for size == 0!!!
00109       int within = stack[stackPos - 1];
00110       if (within == STACK_OBJECT) {
00111         if (c == '}') {
00112           endObject();
00113         } else if (c == ',') {
00114           state = STATE_IN_OBJECT;
00115         } else {
00116           //throw new RuntimeException("Expected ',' or '}' while parsing object. Got: " + c + ". " + characterCounter);
00117         }
00118       } else if (within == STACK_ARRAY) {
00119         if (c == ']') {
00120           endArray();
00121         } else if (c == ',') {
00122           state = STATE_IN_ARRAY;
00123         } else {
00124           //throw new RuntimeException("Expected ',' or ']' while parsing array. Got: " + c + ". " + characterCounter);
00125 
00126         }
00127       } else {
00128         //throw new RuntimeException("Finished a literal, but unclear what state to move to. Last state: " + characterCounter);
00129       }
00130     }break;
00131     case STATE_IN_NUMBER:
00132       if (c >= '0' && c <= '9') {
00133         buffer[bufferPos] = c;
00134         increaseBufferPointer();
00135       } else if (c == '.') {
00136         if (doesCharArrayContain(buffer, bufferPos, '.')) {
00137           //throw new RuntimeException("Cannot have multiple decimal points in a number. " + characterCounter);
00138         } else if (doesCharArrayContain(buffer, bufferPos, 'e')) {
00139           //throw new RuntimeException("Cannot have a decimal point in an exponent." + characterCounter);
00140         }
00141         buffer[bufferPos] = c;
00142         increaseBufferPointer();
00143       } else if (c == 'e' || c == 'E') {
00144         if (doesCharArrayContain(buffer, bufferPos, 'e')) {
00145           //throw new RuntimeException("Cannot have multiple exponents in a number. " + characterCounter);
00146         }
00147         buffer[bufferPos] = c;
00148         increaseBufferPointer();
00149       } else if (c == '+' || c == '-') {
00150         char last = buffer[bufferPos - 1];
00151         if (!(last == 'e' || last == 'E')) {
00152           //throw new RuntimeException("Can only have '+' or '-' after the 'e' or 'E' in a number." + characterCounter);
00153         }
00154         buffer[bufferPos] = c;
00155         increaseBufferPointer();
00156       } else {
00157         endNumber();
00158         // we have consumed one beyond the end of the number
00159         parse(c);
00160       }
00161       break;
00162     case STATE_IN_TRUE:
00163       buffer[bufferPos] = c;
00164       increaseBufferPointer();
00165       if (bufferPos == 4) {
00166         endTrue();
00167       }
00168       break;
00169     case STATE_IN_FALSE:
00170       buffer[bufferPos] = c;
00171       increaseBufferPointer();
00172       if (bufferPos == 5) {
00173         endFalse();
00174       }
00175       break;
00176     case STATE_IN_NULL:
00177       buffer[bufferPos] = c;
00178       increaseBufferPointer();
00179       if (bufferPos == 4) {
00180         endNull();
00181       }
00182       break;
00183     case STATE_START_DOCUMENT:
00184       //myListener->startDocument();
00185       if (c == '[') {
00186         startArray();
00187       } else if (c == '{') {
00188         startObject();
00189       } else {
00190         // throw new ParsingError($this->_line_number,
00191         // $this->_char_number,
00192         // "Document must start with object or array.");
00193       }
00194       break;
00195     //case STATE_DONE:
00196       // throw new ParsingError($this->_line_number, $this->_char_number,
00197       // "Expected end of document.");
00198     //default:
00199       // throw new ParsingError($this->_line_number, $this->_char_number,
00200       // "Internal error. Reached an unknown state: ".$this->_state);
00201     }
00202     characterCounter++;
00203   }
00204 
00205 void JsonStreamingParser::increaseBufferPointer() {
00206   bufferPos = min(bufferPos + 1, BUFFER_MAX_LENGTH - 1);
00207 }
00208 
00209 void JsonStreamingParser::endString() {
00210     int popped = stack[stackPos - 1];
00211     stackPos--;
00212     if (popped == STACK_KEY) {
00213       buffer[bufferPos] = '\0';
00214       myListener->key(String(buffer));
00215       state = STATE_END_KEY;
00216     } else if (popped == STACK_STRING) {
00217       buffer[bufferPos] = '\0';
00218       myListener->value(String(buffer));
00219       state = STATE_AFTER_VALUE;
00220     } else {
00221       // throw new ParsingError($this->_line_number, $this->_char_number,
00222       // "Unexpected end of string.");
00223     }
00224     bufferPos = 0;
00225   }
00226 void JsonStreamingParser::startValue(char c) {
00227     if (c == '[') {
00228       startArray();
00229     } else if (c == '{') {
00230       startObject();
00231     } else if (c == '"') {
00232       startString();
00233     } else if (isDigit(c)) {
00234       startNumber(c);
00235     } else if (c == 't') {
00236       state = STATE_IN_TRUE;
00237       buffer[bufferPos] = c;
00238       increaseBufferPointer();
00239     } else if (c == 'f') {
00240       state = STATE_IN_FALSE;
00241       buffer[bufferPos] = c;
00242       increaseBufferPointer();
00243     } else if (c == 'n') {
00244       state = STATE_IN_NULL;
00245       buffer[bufferPos] = c;
00246       increaseBufferPointer();
00247     } else {
00248       // throw new ParsingError($this->_line_number, $this->_char_number,
00249       // "Unexpected character for value: ".$c);
00250     }
00251   }
00252 
00253 bool JsonStreamingParser::isDigit(char c) {
00254     // Only concerned with the first character in a number.
00255     return (c >= '0' && c <= '9') || c == '-';
00256   }
00257 
00258 void JsonStreamingParser::endArray() {
00259     int popped = stack[stackPos - 1];
00260     stackPos--;
00261     if (popped != STACK_ARRAY) {
00262       // throw new ParsingError($this->_line_number, $this->_char_number,
00263       // "Unexpected end of array encountered.");
00264     }
00265     //myListener->endArray();
00266     state = STATE_AFTER_VALUE;
00267     if (stackPos == 0) {
00268       endDocument();
00269     }
00270   }
00271 
00272 void JsonStreamingParser::startKey() {
00273     stack[stackPos] = STACK_KEY;
00274     stackPos++;
00275     state = STATE_IN_STRING;
00276   }
00277 
00278 void JsonStreamingParser::endObject() {
00279     int popped = stack[stackPos];
00280     stackPos--;
00281     if (popped != STACK_OBJECT) {
00282       // throw new ParsingError($this->_line_number, $this->_char_number,
00283       // "Unexpected end of object encountered.");
00284     }
00285     //myListener->endObject();
00286     state = STATE_AFTER_VALUE;
00287     if (stackPos == 0) {
00288       endDocument();
00289     }
00290   }
00291 
00292 void JsonStreamingParser::processEscapeCharacters(char c) {
00293     if (c == '"') {
00294       buffer[bufferPos] = '"';
00295       increaseBufferPointer();
00296     } else if (c == '\\') {
00297       buffer[bufferPos] = '\\';
00298       increaseBufferPointer();
00299     } else if (c == '/') {
00300       buffer[bufferPos] = '/';
00301       increaseBufferPointer();
00302     } else if (c == 'b') {
00303       buffer[bufferPos] = 0x08;
00304       increaseBufferPointer();
00305     } else if (c == 'f') {
00306       buffer[bufferPos] = '\f';
00307       increaseBufferPointer();
00308     } else if (c == 'n') {
00309       buffer[bufferPos] = '\n';
00310       increaseBufferPointer();
00311     } else if (c == 'r') {
00312       buffer[bufferPos] = '\r';
00313       increaseBufferPointer();
00314     } else if (c == 't') {
00315       buffer[bufferPos] = '\t';
00316       increaseBufferPointer();
00317     } else if (c == 'u') {
00318       state = STATE_UNICODE;
00319     } else {
00320       // throw new ParsingError($this->_line_number, $this->_char_number,
00321       // "Expected escaped character after backslash. Got: ".$c);
00322     }
00323     if (state != STATE_UNICODE) {
00324       state = STATE_IN_STRING;
00325     }
00326   }
00327 
00328 void JsonStreamingParser::processUnicodeCharacter(char c) {
00329     if (!isHexCharacter(c)) {
00330       // throw new ParsingError($this->_line_number, $this->_char_number,
00331       // "Expected hex character for escaped Unicode character. Unicode parsed: "
00332       // . implode($this->_unicode_buffer) . " and got: ".$c);
00333     }
00334 
00335     unicodeBuffer[unicodeBufferPos] = c;
00336     unicodeBufferPos++;
00337 
00338     if (unicodeBufferPos == 4) {
00339       int codepoint = getHexArrayAsDecimal(unicodeBuffer, unicodeBufferPos);
00340       endUnicodeCharacter(codepoint);
00341       return;
00342       /*if (codepoint >= 0xD800 && codepoint < 0xDC00) {
00343         unicodeHighSurrogate = codepoint;
00344         unicodeBufferPos = 0;
00345         state = STATE_UNICODE_SURROGATE;
00346       } else if (codepoint >= 0xDC00 && codepoint <= 0xDFFF) {
00347         if (unicodeHighSurrogate == -1) {
00348           // throw new ParsingError($this->_line_number,
00349           // $this->_char_number,
00350           // "Missing high surrogate for Unicode low surrogate.");
00351         }
00352         int combinedCodePoint = ((unicodeHighSurrogate - 0xD800) * 0x400) + (codepoint - 0xDC00) + 0x10000;
00353         endUnicodeCharacter(combinedCodePoint);
00354       } else if (unicodeHighSurrogate != -1) {
00355         // throw new ParsingError($this->_line_number,
00356         // $this->_char_number,
00357         // "Invalid low surrogate following Unicode high surrogate.");
00358         endUnicodeCharacter(codepoint);
00359       } else {
00360         endUnicodeCharacter(codepoint);
00361       }*/
00362     }
00363   }
00364 bool JsonStreamingParser::isHexCharacter(char c) {
00365     return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
00366   }
00367 
00368 int JsonStreamingParser::getHexArrayAsDecimal(char hexArray[], int length) {
00369     int result = 0;
00370     for (int i = 0; i < length; i++) {
00371       char current = hexArray[length - i - 1];
00372       int value = 0;
00373       if (current >= 'a' && current <= 'f') {
00374         value = current - 'a' + 10;
00375       } else if (current >= 'A' && current <= 'F') {
00376         value = current - 'A' + 10;
00377       } else if (current >= '0' && current <= '9') {
00378         value = current - '0';
00379       }
00380       result += value * 16^i;
00381     }
00382     return result;
00383   }
00384 
00385 bool JsonStreamingParser::doesCharArrayContain(char myArray[], int length, char c) {
00386     for (int i = 0; i < length; i++) {
00387       if (myArray[i] == c) {
00388         return true;
00389       }
00390     }
00391     return false;
00392   }
00393 
00394 void JsonStreamingParser::endUnicodeSurrogateInterstitial() {
00395     char unicodeEscape = unicodeEscapeBuffer[unicodeEscapeBufferPos - 1];
00396     if (unicodeEscape != 'u') {
00397       // throw new ParsingError($this->_line_number, $this->_char_number,
00398       // "Expected '\\u' following a Unicode high surrogate. Got: " .
00399       // $unicode_escape);
00400     }
00401     unicodeBufferPos = 0;
00402     unicodeEscapeBufferPos = 0;
00403     state = STATE_UNICODE;
00404   }
00405 
00406 void JsonStreamingParser::endNumber() {
00407     buffer[bufferPos] = '\0';
00408     String value = String(buffer);
00409     //float result = 0.0;
00410     //if (doesCharArrayContain(buffer, bufferPos, '.')) {
00411     //  result = value.toFloat();
00412     //} else {
00413       // needed special treatment in php, maybe not in Java and c
00414     //  result = value.toFloat();
00415     //}
00416     myListener->value(value.c_str());
00417     bufferPos = 0;
00418     state = STATE_AFTER_VALUE;
00419   }
00420 
00421 int JsonStreamingParser::convertDecimalBufferToInt(char myArray[], int length) {
00422     int result = 0;
00423     for (int i = 0; i < length; i++) {
00424       char current = myArray[length - i - 1];
00425       result += (current - '0') * 10;
00426     }
00427     return result;
00428   }
00429 
00430 void JsonStreamingParser::endDocument() {
00431     //myListener->endDocument();
00432     state = STATE_DONE;
00433   }
00434 
00435 void JsonStreamingParser::endTrue() {
00436     buffer[bufferPos] = '\0';
00437     String value = String(buffer);
00438     if (value == "true") {
00439       
00440     } else {
00441       // throw new ParsingError($this->_line_number, $this->_char_number,
00442       // "Expected 'true'. Got: ".$true);
00443     }
00444     bufferPos = 0;
00445     state = STATE_AFTER_VALUE;
00446   }
00447 
00448 void JsonStreamingParser::endFalse() {
00449     buffer[bufferPos] = '\0';
00450     String value = String(buffer);
00451     if (value == "false") {
00452       myListener->value("false");
00453     } else {
00454       // throw new ParsingError($this->_line_number, $this->_char_number,
00455       // "Expected 'true'. Got: ".$true);
00456     }
00457     bufferPos = 0;
00458     state = STATE_AFTER_VALUE;
00459   }
00460 
00461 void JsonStreamingParser::endNull() {
00462     buffer[bufferPos] = '\0';
00463     String value = String(buffer);
00464     if (value == "null") {
00465       myListener->value("null");
00466     } else {
00467       // throw new ParsingError($this->_line_number, $this->_char_number,
00468       // "Expected 'true'. Got: ".$true);
00469     }
00470     bufferPos = 0;
00471     state = STATE_AFTER_VALUE;
00472   }
00473 
00474 void JsonStreamingParser::startArray() {
00475     //myListener->startArray();
00476     state = STATE_IN_ARRAY;
00477     stack[stackPos] = STACK_ARRAY;
00478     stackPos++;
00479   }
00480 
00481 void JsonStreamingParser::startObject() {
00482     //myListener->startObject();
00483     state = STATE_IN_OBJECT;
00484     stack[stackPos] = STACK_OBJECT;
00485     stackPos++;
00486   }
00487 
00488 void JsonStreamingParser::startString() {
00489     stack[stackPos] = STACK_STRING;
00490     stackPos++;
00491     state = STATE_IN_STRING;
00492   }
00493 
00494 void JsonStreamingParser::startNumber(char c) {
00495     state = STATE_IN_NUMBER;
00496     buffer[bufferPos] = c;
00497     increaseBufferPointer();
00498   }
00499 
00500 void JsonStreamingParser::endUnicodeCharacter(int codepoint) {
00501     buffer[bufferPos] = convertCodepointToCharacter(codepoint);
00502     increaseBufferPointer();
00503     unicodeBufferPos = 0;
00504     unicodeHighSurrogate = -1;
00505     state = STATE_IN_STRING;
00506   }
00507 
00508 char JsonStreamingParser::convertCodepointToCharacter(int num) {
00509     if (num <= 0x7F)
00510       return (char) (num);
00511     // if(num<=0x7FF) return (char)((num>>6)+192) + (char)((num&63)+128);
00512     // if(num<=0xFFFF) return
00513     // chr((num>>12)+224).chr(((num>>6)&63)+128).chr((num&63)+128);
00514     // if(num<=0x1FFFFF) return
00515     // chr((num>>18)+240).chr(((num>>12)&63)+128).chr(((num>>6)&63)+128).chr((num&63)+128);
00516     return ' ';
00517   }
00518 
00519 
00520 
00521 
00522 
00523 
00524 
00525