/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ /* * Files JSON.cpp and JSONValue.cpp part of the SimpleJSON Library - https://github.com/MJPA/SimpleJSON * * Copyright (C) 2010 Mike Anchor * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include "common/json.h" #ifdef __MINGW32__ #define wcsncasecmp wcsnicmp #endif // Macros to free an array/object #define FREE_ARRAY(x) { JSONArray::iterator iter; for (iter = x.begin(); iter != x.end(); iter++) { delete *iter; } } #define FREE_OBJECT(x) { JSONObject::iterator iter; for (iter = x.begin(); iter != x.end(); iter++) { delete (*iter)._value; } } namespace Common { /** * Blocks off the public constructor * * @access private * */ JSON::JSON() {} /** * Parses a complete JSON encoded string (UNICODE input version) * * @access public * * @param char* data The JSON text * * @return JSONValue* Returns a JSON Value representing the root, or NULL on error */ JSONValue *JSON::parse(const char *data) { // Skip any preceding whitespace, end of data = no JSON = fail if (!skipWhitespace(&data)) return nullptr; // We need the start of a value here now... JSONValue *value = JSONValue::parse(&data); if (value == nullptr) return nullptr; // Can be white space now and should be at the end of the string then... if (skipWhitespace(&data)) { delete value; return nullptr; } // We're now at the end of the string return value; } /** * Turns the passed in JSONValue into a JSON encode string * * @access public * * @param JSONValue* value The root value * * @return String Returns a JSON encoded string representation of the given value */ String JSON::stringify(const JSONValue *value) { if (value != nullptr) return value->stringify(); else return ""; } /** * Skips over any whitespace characters (space, tab, \r or \n) defined by the JSON spec * * @access protected * * @param char** data Pointer to a char* that contains the JSON text * * @return bool Returns true if there is more data, or false if the end of the text was reached */ bool JSON::skipWhitespace(const char **data) { while (**data != 0 && (**data == ' ' || **data == '\t' || **data == '\r' || **data == '\n')) (*data)++; return **data != 0; } /** * Extracts a JSON String as defined by the spec - "" * Any escaped characters are swapped out for their unescaped values * * @access protected * * @param char** data Pointer to a char* that contains the JSON text * @param String& str Reference to a String to receive the extracted string * * @return bool Returns true on success, false on failure */ bool JSON::extractString(const char **data, String &str) { str = ""; while (**data != 0) { // Save the char so we can change it if need be char next_char = **data; uint32 next_uchar = 0; // Escaping something? if (next_char == '\\') { // Move over the escape char (*data)++; // Deal with the escaped char switch (**data) { case '"': next_char = '"'; break; case '\\': next_char = '\\'; break; case '/': next_char = '/'; break; case 'b': next_char = '\b'; break; case 'f': next_char = '\f'; break; case 'n': next_char = '\n'; break; case 'r': next_char = '\r'; break; case 't': next_char = '\t'; break; case 'u': { next_char = 0; next_uchar = parseUnicode(data); // If the codepoint is a high surrogate, we should have a low surrogate now if (next_uchar >= 0xD800 && next_uchar <= 0xDBFF) { (*data)++; if (**data != '\\') return false; (*data)++; uint32 low_surrogate = parseUnicode(data); if (low_surrogate < 0xDC00 || low_surrogate > 0xDFFF) return false; //next_uchar = 0x10000 + (next_uchar - 0xD800) * 0x400 + (low_surrogate - 0xDC00); next_uchar = (next_uchar << 10) + low_surrogate - 0x35FDC00u; } else if (next_uchar >= 0xDC00 && next_uchar <= 0xDFFF) return false; // low surrogate, which should only follow a high surrogate // Check this is a valid code point if (next_uchar > 0x10FFFF) return false; break; } // By the spec, only the above cases are allowed default: return false; } } // End of the string? else if (next_char == '"') { (*data)++; //str.reserve(); // Remove unused capacity //TODO return true; } // Disallowed char? else if (next_char < ' ' && next_char != '\t') { // SPEC Violation: Allow tabs due to real world cases return false; } // Add the next char if (next_char != 0) str += next_char; else { if (next_uchar < 0x80) // 1-byte character (ASCII) str += (char)next_uchar; else if (next_uchar <= 0x7FF) { // 2-byte characters: 110xxxxx 10xxxxxx str += (char)(0xC0 | (next_uchar >> 6)); str += (char)(0x80 | (next_uchar & 0x3F)); } else if (next_uchar <= 0xFFFF) { // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx str += (char)(0xE0 | (next_uchar >> 12)); str += (char)(0x80 | ((next_uchar >> 6) & 0x3F)); str += (char)(0x80 | (next_uchar & 0x3F)); } else { // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx str += (char)(0xF0 | (next_uchar >> 18)); str += (char)(0x80 | ((next_uchar >> 12) & 0x3F)); str += (char)(0x80 | ((next_uchar >> 6) & 0x3F)); str += (char)(0x80 | (next_uchar & 0x3F)); } } // Move on (*data)++; } // If we're here, the string ended incorrectly return false; } /** * Parses some text as though it is a unicode hexadecimal sequence. * It assumes that the data is currently pointing on the 'u' part of '\uXXXX`. * * @access protected * * @param char** data Pointer to a char* that contains the JSON text * @param String& str Reference to a String to receive the extracted string * * @return uint32 Returns the unicode code point value or 0xFFFFFFFF in case of error. */ uint32 JSON::parseUnicode(const char **data) { if (**data != 'u') return 0xFFFFFFFF; // We need 5 chars (4 hex + the 'u') or its not valid if (!simplejson_wcsnlen(*data, 5)) return 0xFFFFFFFF; // Deal with the chars uint32 codepoint = 0; for (int i = 0; i < 4; i++) { // Do it first to move off the 'u' and leave us on the // final hex digit as we move on by one later on (*data)++; codepoint <<= 4; // Parse the hex digit if (**data >= '0' && **data <= '9') codepoint |= (**data - '0'); else if (**data >= 'A' && **data <= 'F') codepoint |= (10 + (**data - 'A')); else if (**data >= 'a' && **data <= 'f') codepoint |= (10 + (**data - 'a')); else { // Invalid hex digit return 0xFFFFFFFF; } } return codepoint; } /** * Parses some text as though it is an integer * * @access protected * * @param char** data Pointer to a char* that contains the JSON text * * @return double Returns the double value of the number found */ double JSON::parseInt(const char **data) { double integer = 0; while (**data != 0 && **data >= '0' && **data <= '9') integer = integer * 10 + (*(*data)++ - '0'); return integer; } /** * Parses some text as though it is a decimal * * @access protected * * @param char** data Pointer to a char* that contains the JSON text * * @return double Returns the double value of the decimal found */ double JSON::parseDecimal(const char **data) { double decimal = 0.0; double factor = 0.1; while (**data != 0 && **data >= '0' && **data <= '9') { int digit = (*(*data)++ - '0'); decimal = decimal + digit * factor; factor *= 0.1; } return decimal; } /** * Parses a JSON encoded value to a JSONValue object * * @access protected * * @param char** data Pointer to a char* that contains the data * * @return JSONValue* Returns a pointer to a JSONValue object on success, NULL on error */ JSONValue *JSONValue::parse(const char **data) { // Is it a string? if (**data == '"') { String str; if (!JSON::extractString(&(++(*data)), str)) return nullptr; else return new JSONValue(str); } // Is it a boolean? else if ((simplejson_wcsnlen(*data, 4) && scumm_strnicmp(*data, "true", 4) == 0) || (simplejson_wcsnlen(*data, 5) && scumm_strnicmp(*data, "false", 5) == 0)) { bool value = scumm_strnicmp(*data, "true", 4) == 0; (*data) += value ? 4 : 5; return new JSONValue(value); } // Is it a null? else if (simplejson_wcsnlen(*data, 4) && scumm_strnicmp(*data, "null", 4) == 0) { (*data) += 4; return new JSONValue(); } // Is it a number? else if (**data == '-' || (**data >= '0' && **data <= '9')) { // Negative? bool neg = **data == '-'; if (neg) (*data)++; long long int integer = 0; double number = 0.0; bool onlyInteger = true; // Parse the whole part of the number - only if it wasn't 0 if (**data == '0') (*data)++; else if (**data >= '1' && **data <= '9') number = integer = JSON::parseInt(data); else return nullptr; // Could be a decimal now... if (**data == '.') { (*data)++; // Not get any digits? if (!(**data >= '0' && **data <= '9')) return nullptr; // Find the decimal and sort the decimal place out // Use ParseDecimal as ParseInt won't work with decimals less than 0.1 // thanks to Javier Abadia for the report & fix double decimal = JSON::parseDecimal(data); // Save the number number += decimal; onlyInteger = false; } // Could be an exponent now... if (**data == 'E' || **data == 'e') { (*data)++; // Check signage of expo bool neg_expo = false; if (**data == '-' || **data == '+') { neg_expo = **data == '-'; (*data)++; } // Not get any digits? if (!(**data >= '0' && **data <= '9')) return nullptr; // Sort the expo out double expo = JSON::parseInt(data); for (double i = 0.0; i < expo; i++) number = neg_expo ? (number / 10.0) : (number * 10.0); onlyInteger = false; } // Was it neg? if (neg) number *= -1; if (onlyInteger) return new JSONValue(neg ? -integer : integer); return new JSONValue(number); } // An object? else if (**data == '{') { JSONObject object; (*data)++; while (**data != 0) { // Whitespace at the start? if (!JSON::skipWhitespace(data)) { FREE_OBJECT(object); return nullptr; } // Special case - empty object if (object.size() == 0 && **data == '}') { (*data)++; return new JSONValue(object); } // We want a string now... String name; if (!JSON::extractString(&(++(*data)), name)) { FREE_OBJECT(object); return nullptr; } // More whitespace? if (!JSON::skipWhitespace(data)) { FREE_OBJECT(object); return nullptr; } // Need a : now if (*((*data)++) != ':') { FREE_OBJECT(object); return nullptr; } // More whitespace? if (!JSON::skipWhitespace(data)) { FREE_OBJECT(object); return nullptr; } // The value is here JSONValue *value = parse(data); if (value == nullptr) { FREE_OBJECT(object); return nullptr; } // Add the name:value if (object.find(name) != object.end()) delete object[name]; object[name] = value; // More whitespace? if (!JSON::skipWhitespace(data)) { FREE_OBJECT(object); return nullptr; } // End of object? if (**data == '}') { (*data)++; return new JSONValue(object); } // Want a , now if (**data != ',') { FREE_OBJECT(object); return nullptr; } (*data)++; } // Only here if we ran out of data FREE_OBJECT(object); return nullptr; } // An array? else if (**data == '[') { JSONArray array; (*data)++; while (**data != 0) { // Whitespace at the start? if (!JSON::skipWhitespace(data)) { FREE_ARRAY(array); return nullptr; } // Special case - empty array if (array.size() == 0 && **data == ']') { (*data)++; return new JSONValue(array); } // Get the value JSONValue *value = parse(data); if (value == nullptr) { FREE_ARRAY(array); return nullptr; } // Add the value array.push_back(value); // More whitespace? if (!JSON::skipWhitespace(data)) { FREE_ARRAY(array); return nullptr; } // End of array? if (**data == ']') { (*data)++; return new JSONValue(array); } // Want a , now if (**data != ',') { FREE_ARRAY(array); return nullptr; } (*data)++; } // Only here if we ran out of data FREE_ARRAY(array); return nullptr; } // Ran out of possibilites, it's bad! else { return nullptr; } } /** * Basic constructor for creating a JSON Value of type NULL * * @access public */ JSONValue::JSONValue(/*NULL*/) { _type = JSONType_Null; } /** * Basic constructor for creating a JSON Value of type String * * @access public * * @param char* m_char_value The string to use as the value */ JSONValue::JSONValue(const char *charValue) { _type = JSONType_String; _stringValue = new String(String(charValue)); } /** * Basic constructor for creating a JSON Value of type String * * @access public * * @param String m_string_value The string to use as the value */ JSONValue::JSONValue(const String &stringValue) { _type = JSONType_String; _stringValue = new String(stringValue); } /** * Basic constructor for creating a JSON Value of type Bool * * @access public * * @param bool m_bool_value The bool to use as the value */ JSONValue::JSONValue(bool boolValue) { _type = JSONType_Bool; _boolValue = boolValue; } /** * Basic constructor for creating a JSON Value of type Number * * @access public * * @param double m_number_value The number to use as the value */ JSONValue::JSONValue(double numberValue) { _type = JSONType_Number; _numberValue = numberValue; } /** * Basic constructor for creating a JSON Value of type Number (Integer) * * @access public * * @param int numberValue The number to use as the value */ JSONValue::JSONValue(long long int numberValue) { _type = JSONType_IntegerNumber; _integerValue = numberValue; } /** * Basic constructor for creating a JSON Value of type Array * * @access public * * @param JSONArray m_array_value The JSONArray to use as the value */ JSONValue::JSONValue(const JSONArray &arrayValue) { _type = JSONType_Array; _arrayValue = new JSONArray(arrayValue); } /** * Basic constructor for creating a JSON Value of type Object * * @access public * * @param JSONObject m_object_value The JSONObject to use as the value */ JSONValue::JSONValue(const JSONObject &objectValue) { _type = JSONType_Object; _objectValue = new JSONObject(objectValue); } /** * Copy constructor to perform a deep copy of array / object values * * @access public * * @param JSONValue m_source The source JSONValue that is being copied */ JSONValue::JSONValue(const JSONValue &source) { _type = source._type; switch (_type) { case JSONType_String: _stringValue = new String(*source._stringValue); break; case JSONType_Bool: _boolValue = source._boolValue; break; case JSONType_Number: _numberValue = source._numberValue; break; case JSONType_IntegerNumber: _integerValue = source._integerValue; break; case JSONType_Array: { JSONArray source_array = *source._arrayValue; JSONArray::iterator iter; _arrayValue = new JSONArray(); for (iter = source_array.begin(); iter != source_array.end(); iter++) _arrayValue->push_back(new JSONValue(**iter)); break; } case JSONType_Object: { JSONObject source_object = *source._objectValue; _objectValue = new JSONObject(); JSONObject::iterator iter; for (iter = source_object.begin(); iter != source_object.end(); iter++) { String name = (*iter)._key; (*_objectValue)[name] = new JSONValue(*((*iter)._value)); } break; } default: // fallthrough intended case JSONType_Null: // Nothing to do. break; } } /** * The destructor for the JSON Value object * Handles deleting the objects in the array or the object value * * @access public */ JSONValue::~JSONValue() { if (_type == JSONType_Array) { JSONArray::iterator iter; for (iter = _arrayValue->begin(); iter != _arrayValue->end(); iter++) delete *iter; delete _arrayValue; } else if (_type == JSONType_Object) { JSONObject::iterator iter; for (iter = _objectValue->begin(); iter != _objectValue->end(); iter++) { delete (*iter)._value; } delete _objectValue; } else if (_type == JSONType_String) { delete _stringValue; } } /** * Checks if the value is a NULL * * @access public * * @return bool Returns true if it is a NULL value, false otherwise */ bool JSONValue::isNull() const { return _type == JSONType_Null; } /** * Checks if the value is a String * * @access public * * @return bool Returns true if it is a String value, false otherwise */ bool JSONValue::isString() const { return _type == JSONType_String; } /** * Checks if the value is a Bool * * @access public * * @return bool Returns true if it is a Bool value, false otherwise */ bool JSONValue::isBool() const { return _type == JSONType_Bool; } /** * Checks if the value is a Number * * @access public * * @return bool Returns true if it is a Number value, false otherwise */ bool JSONValue::isNumber() const { return _type == JSONType_Number; } /** * Checks if the value is an Integer * * @access public * * @return bool Returns true if it is an Integer value, false otherwise */ bool JSONValue::isIntegerNumber() const { return _type == JSONType_IntegerNumber; } /** * Checks if the value is an Array * * @access public * * @return bool Returns true if it is an Array value, false otherwise */ bool JSONValue::isArray() const { return _type == JSONType_Array; } /** * Checks if the value is an Object * * @access public * * @return bool Returns true if it is an Object value, false otherwise */ bool JSONValue::isObject() const { return _type == JSONType_Object; } /** * Retrieves the String value of this JSONValue * Use isString() before using this method. * * @access public * * @return String Returns the string value */ const String &JSONValue::asString() const { return (*_stringValue); } /** * Retrieves the Bool value of this JSONValue * Use isBool() before using this method. * * @access public * * @return bool Returns the bool value */ bool JSONValue::asBool() const { return _boolValue; } /** * Retrieves the Number value of this JSONValue * Use isNumber() before using this method. * * @access public * * @return double Returns the number value */ double JSONValue::asNumber() const { return _numberValue; } /** * Retrieves the Integer value of this JSONValue * Use isIntegerNumber() before using this method. * * @access public * * @return int Returns the number value */ long long int JSONValue::asIntegerNumber() const { return _integerValue; } /** * Retrieves the Array value of this JSONValue * Use isArray() before using this method. * * @access public * * @return JSONArray Returns the array value */ const JSONArray &JSONValue::asArray() const { return (*_arrayValue); } /** * Retrieves the Object value of this JSONValue * Use isObject() before using this method. * * @access public * * @return JSONObject Returns the object value */ const JSONObject &JSONValue::asObject() const { return (*_objectValue); } /** * Retrieves the number of children of this JSONValue. * This number will be 0 or the actual number of children * if isArray() or isObject(). * * @access public * * @return The number of children. */ std::size_t JSONValue::countChildren() const { switch (_type) { case JSONType_Array: return _arrayValue->size(); case JSONType_Object: return _objectValue->size(); default: return 0; } } /** * Checks if this JSONValue has a child at the given index. * Use isArray() before using this method. * * @access public * * @return bool Returns true if the array has a value at the given index. */ bool JSONValue::hasChild(std::size_t index) const { if (_type == JSONType_Array) { return index < _arrayValue->size(); } else { return false; } } /** * Retrieves the child of this JSONValue at the given index. * Use isArray() before using this method. * * @access public * * @return JSONValue* Returns JSONValue at the given index or NULL * if it doesn't exist. */ JSONValue *JSONValue::child(std::size_t index) { if (index < _arrayValue->size()) { return (*_arrayValue)[index]; } else { return nullptr; } } /** * Checks if this JSONValue has a child at the given key. * Use isObject() before using this method. * * @access public * * @return bool Returns true if the object has a value at the given key. */ bool JSONValue::hasChild(const char *name) const { if (_type == JSONType_Object) { return _objectValue->find(name) != _objectValue->end(); } else { return false; } } /** * Retrieves the child of this JSONValue at the given key. * Use isObject() before using this method. * * @access public * * @return JSONValue* Returns JSONValue for the given key in the object * or NULL if it doesn't exist. */ JSONValue *JSONValue::child(const char *name) { JSONObject::const_iterator it = _objectValue->find(name); if (it != _objectValue->end()) { return it->_value; } else { return nullptr; } } /** * Retrieves the keys of the JSON Object or an empty vector * if this value is not an object. * * @access public * * @return std::vector A vector containing the keys. */ Array JSONValue::objectKeys() const { Array keys; if (_type == JSONType_Object) { JSONObject::const_iterator iter = _objectValue->begin(); while (iter != _objectValue->end()) { keys.push_back(iter->_key); iter++; } } return keys; } /** * Creates a JSON encoded string for the value with all necessary characters escaped * * @access public * * @param bool prettyprint Enable prettyprint * * @return String Returns the JSON string */ String JSONValue::stringify(bool const prettyprint) const { size_t const indentDepth = prettyprint ? 1 : 0; return stringifyImpl(indentDepth); } /** * Creates a JSON encoded string for the value with all necessary characters escaped * * @access private * * @param size_t indentDepth The prettyprint indentation depth (0 : no prettyprint) * * @return String Returns the JSON string */ String JSONValue::stringifyImpl(size_t const indentDepth) const { String ret_string; size_t const indentDepth1 = indentDepth ? indentDepth + 1 : 0; String const indentStr = indent(indentDepth); String const indentStr1 = indent(indentDepth1); switch (_type) { default: // fallthrough intended case JSONType_Null: ret_string = "null"; break; case JSONType_String: ret_string = stringifyString(*_stringValue); break; case JSONType_Bool: ret_string = _boolValue ? "true" : "false"; break; case JSONType_Number: { if (isinf(_numberValue) || isnan(_numberValue)) ret_string = "null"; else { ret_string = String::format("%g", _numberValue); } break; } case JSONType_IntegerNumber: { ret_string = String::format("%lld", _integerValue); break; } case JSONType_Array: { ret_string = indentDepth ? "[\n" + indentStr1 : "["; JSONArray::const_iterator iter = _arrayValue->begin(); while (iter != _arrayValue->end()) { ret_string += (*iter)->stringifyImpl(indentDepth1); // Not at the end - add a separator if (++iter != _arrayValue->end()) ret_string += ","; } ret_string += indentDepth ? "\n" + indentStr + "]" : "]"; break; } case JSONType_Object: { ret_string = indentDepth ? "{\n" + indentStr1 : "{"; JSONObject::const_iterator iter = _objectValue->begin(); while (iter != _objectValue->end()) { ret_string += stringifyString((*iter)._key); ret_string += ":"; ret_string += (*iter)._value->stringifyImpl(indentDepth1); // Not at the end - add a separator if (++iter != _objectValue->end()) ret_string += ","; } ret_string += indentDepth ? "\n" + indentStr + "}" : "}"; break; } } return ret_string; } /** * Creates a JSON encoded string with all required fields escaped * Works from http://www.ecma-internationl.org/publications/files/ECMA-ST/ECMA-262.pdf * Section 15.12.3. * * @access private * * @param String str The string that needs to have the characters escaped * * @return String Returns the JSON string */ String JSONValue::stringifyString(const String &str) { String str_out = "\""; String::const_iterator iter = str.begin(); while (iter != str.end()) { uint32 uchr = decodeUtf8Char(iter, str.end()); if (uchr == 0xFFFFFFFF) break; // error - truncate the result if (uchr == '"' || uchr == '\\' || uchr == '/') { str_out += '\\'; str_out += (char)uchr; } else if (uchr == '\b') { str_out += "\\b"; } else if (uchr == '\f') { str_out += "\\f"; } else if (uchr == '\n') { str_out += "\\n"; } else if (uchr == '\r') { str_out += "\\r"; } else if (uchr == '\t') { str_out += "\\t"; } else if (uchr >= ' ' && uchr <= 126 ) { str_out += (char)uchr; } else { if (uchr <= 0xFFFF) str_out += String::format("\\u%04x", uchr); else str_out += String::format("\\u%04x\\u%04x", 0xD7C0 + (uchr >> 10), 0xDC00 + (uchr & 0x3FF)); } iter++; } str_out += "\""; return str_out; } /** * Decode the next utf-8 character in the String pointed to by begin. * * @param String::const_iterator &iter Iterator pointing to the start of the character to decode. * * @param const String::const_iterator &end Iterator pointing past the end of the string being decoded. * * @return The codepoint value for the next utf-8 character starting at the current iterator position, * or 0xFFFFFFFF in case of error. */ uint32 JSONValue::decodeUtf8Char(String::const_iterator &iter, const String::const_iterator &end) { uint8 state = 0; uint32 codepoint = 0; int nbRead = 0; do { uint8 byte = uint8(*iter); state = decodeUtf8Byte(state, codepoint, byte); ++nbRead; if (state == 0) return codepoint; } while (state != 1 && ++iter != end); if (state == 1) { // We failed to read this as a UTF-8 character. The string might be encoded differently, which // would be invalid (since the json standard indicate the string has to be in utf-8) but rather // that return 0FFFFFFFF and truncate, try to recover from it by rewinding and returning the // raw byte. while (--nbRead > 0) { --iter; } uint8 byte = uint8(*iter); warning("Invalid UTF-8 character 0x%x in JSON string.", byte); return byte; } return 0xFFFFFFFF; } /** * Decode one byte from a UTF-8 string. * * The function must initially (for the first byte) be called with a state of 0, and then * with the state from the previous byte until it returns 0 (success) or 1 (failure). * * Copyright (c) 2008-2009 Bjoern Hoehrmann * See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details. * * @access private * * @param uint8 state The state from the previous byte, or 0 when decoding the first byte. * * @param uint32 &codepoint The codepoint value. Unless the returned state is 0, the codepoint is * a partial reasult and the function needs to be called again with the next byte. * * @param uint8 byte The byte to decode. * * @return The state of the utf8 decoder: 0 if a character has been decoded, 1 in case of * error, and any other value for decoding in progress. */ uint8 JSONValue::decodeUtf8Byte(uint8 state, uint32 &codepoint, uint8 byte) { static const uint8 utf8d[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00..1F 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20..3F 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40..5F 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60..7F 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 80..9F 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // A0..BF 8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C0..DF 0xA, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, // E0..EF 0xB, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, // F0..FF 0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1, // s0..s0 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, // s1..s2 1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // s3..s4 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, // s5..s6 1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // s7..s8 }; const uint8 type = utf8d[byte]; codepoint = state != 0 ? (codepoint << 6) | (byte & 0x3f) : (0xFF >> type) & byte; return utf8d[256 + state * 16 + type]; } /** * Creates the indentation string for the depth given * * @access private * * @param size_t indent The prettyprint indentation depth (0 : no indentation) * * @return String Returns the string */ String JSONValue::indent(size_t depth) { const size_t indent_step = 2; depth ? --depth : 0; String indentStr; for (size_t i = 0; i < depth * indent_step; ++i) indentStr += ' '; return indentStr; } } // End of namespace Common