/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "JSONObject.h" #include <ctype.h> #include <math.h> #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/AString.h> #include <media/stagefright/MediaErrors.h> namespace android { // Returns ERROR_MALFORMED if the value overflows a signed int, returns // 0 otherwise. // This method will assert if it is asked to parse a character which is not // a digit. static ssize_t parseInt32(const char *data, size_t numDigits, int32_t *out) { int32_t x = 0; for (size_t i = 0; i < numDigits; ++i) { int32_t old_x = x; x *= 10; x += data[i] - '0'; CHECK(isdigit(data[i])); if (x < old_x) { // We've overflowed. return ERROR_MALFORMED; } } *out = x; return 0; } // static ssize_t JSONValue::Parse(const char *data, size_t size, JSONValue *out) { size_t offset = 0; while (offset < size && isspace(data[offset])) { ++offset; } if (offset == size) { return ERROR_MALFORMED; } if (data[offset] == '[') { sp<JSONArray> array = new JSONArray; ++offset; for (;;) { while (offset < size && isspace(data[offset])) { ++offset; } if (offset == size) { return ERROR_MALFORMED; } if (data[offset] == ']') { ++offset; break; } JSONValue val; ssize_t n = Parse(&data[offset], size - offset, &val); if (n < 0) { return n; } array->addValue(val); offset += n; while (offset < size && isspace(data[offset])) { ++offset; } if (offset == size) { return ERROR_MALFORMED; } if (data[offset] == ',') { ++offset; } else if (data[offset] != ']') { return ERROR_MALFORMED; } }; out->setArray(array); return offset; } else if (data[offset] == '{') { sp<JSONObject> obj = new JSONObject; ++offset; for (;;) { while (offset < size && isspace(data[offset])) { ++offset; } if (offset == size) { return ERROR_MALFORMED; } if (data[offset] == '}') { ++offset; break; } JSONValue key; ssize_t n = Parse(&data[offset], size - offset, &key); if (n < 0) { return n; } if (key.type() != TYPE_STRING) { return ERROR_MALFORMED; } offset += n; while (offset < size && isspace(data[offset])) { ++offset; } if (offset == size || data[offset] != ':') { return ERROR_MALFORMED; } ++offset; JSONValue val; n = Parse(&data[offset], size - offset, &val); if (n < 0) { return n; } AString keyVal; CHECK(key.getString(&keyVal)); obj->setValue(keyVal.c_str(), val); offset += n; while (offset < size && isspace(data[offset])) { ++offset; } if (offset == size) { return ERROR_MALFORMED; } if (data[offset] == ',') { ++offset; } else if (data[offset] != '}') { return ERROR_MALFORMED; } }; out->setObject(obj); return offset; } else if (data[offset] == '"') { ++offset; AString s; bool escaped = false; while (offset < size) { if (escaped) { char c; switch (data[offset]) { case '\"': case '\\': case '/': c = data[offset]; break; case 'b': c = '\x08'; break; case 'f': c = '\x0c'; break; case 'n': c = '\x0a'; break; case 'r': c = '\x0d'; break; case 't': c = '\x09'; break; default: return ERROR_MALFORMED; } s.append(c); ++offset; escaped = false; } else if (data[offset] == '\\') { escaped = true; } else if (data[offset] == '"') { break; } s.append(data[offset++]); } if (offset == size) { return ERROR_MALFORMED; } ++offset; out->setString(s); return offset; } else if (isdigit(data[offset]) || data[offset] == '-') { bool negate = false; if (data[offset] == '-') { negate = true; ++offset; if (offset == size) { return ERROR_MALFORMED; } } size_t firstDigitOffset = offset; while (offset < size && isdigit(data[offset])) { ++offset; } size_t numDigits = offset - firstDigitOffset; if (numDigits > 1 && data[firstDigitOffset] == '0') { // No leading zeros. return ERROR_MALFORMED; } size_t firstFracDigitOffset = 0; size_t numFracDigits = 0; if (offset < size && data[offset] == '.') { ++offset; firstFracDigitOffset = offset; while (offset < size && isdigit(data[offset])) { ++offset; } numFracDigits = offset - firstFracDigitOffset; if (numFracDigits == 0) { return ERROR_MALFORMED; } } bool negateExponent = false; size_t firstExpDigitOffset = 0; size_t numExpDigits = 0; if (offset < size && (data[offset] == 'e' || data[offset] == 'E')) { ++offset; if (offset == size) { return ERROR_MALFORMED; } if (data[offset] == '+' || data[offset] == '-') { if (data[offset] == '-') { negateExponent = true; } ++offset; } firstExpDigitOffset = offset; while (offset < size && isdigit(data[offset])) { ++offset; } numExpDigits = offset - firstExpDigitOffset; if (numExpDigits == 0) { return ERROR_MALFORMED; } } if (numFracDigits == 0 && numExpDigits == 0) { int32_t x; if (parseInt32(&data[firstDigitOffset], numDigits, &x) != 0) { return ERROR_MALFORMED; } out->setInt32(negate ? -x : x); } else { int32_t mantissa; if (parseInt32(&data[firstDigitOffset], numDigits, &mantissa) != 0) { return ERROR_MALFORMED; } int32_t fraction; if (parseInt32(&data[firstFracDigitOffset], numFracDigits, &fraction) != 0) { return ERROR_MALFORMED; } int32_t exponent; if (parseInt32(&data[firstExpDigitOffset], numExpDigits, &exponent) != 0) { return ERROR_MALFORMED; } if (negateExponent) { exponent = -exponent; } float x = (float)mantissa; x += (float)fraction * powf(10.0f, exponent - (int32_t)numFracDigits); out->setFloat(negate ? -x : x); } return offset; } else if (offset + 4 <= size && !strncmp("null", &data[offset], 4)) { out->unset(); return offset + 4; } else if (offset + 4 <= size && !strncmp("true", &data[offset], 4)) { out->setBoolean(true); return offset + 4; } else if (offset + 5 <= size && !strncmp("false", &data[offset], 5)) { out->setBoolean(false); return offset + 5; } return ERROR_MALFORMED; } JSONValue::JSONValue() : mType(TYPE_NULL) { } JSONValue::JSONValue(const JSONValue &other) : mType(TYPE_NULL) { *this = other; } JSONValue &JSONValue::operator=(const JSONValue &other) { if (&other != this) { unset(); mType = other.mType; mValue = other.mValue; switch (mType) { case TYPE_STRING: mValue.mString = new AString(*other.mValue.mString); break; case TYPE_OBJECT: case TYPE_ARRAY: mValue.mObjectOrArray->incStrong(this /* id */); break; default: break; } } return *this; } JSONValue::~JSONValue() { unset(); } JSONValue::FieldType JSONValue::type() const { return mType; } bool JSONValue::getInt32(int32_t *value) const { if (mType != TYPE_INT32) { return false; } *value = mValue.mInt32; return true; } bool JSONValue::getFloat(float *value) const { switch (mType) { case TYPE_INT32: { *value = mValue.mInt32; break; } case TYPE_FLOAT: { *value = mValue.mFloat; break; } default: return false; } return true; } bool JSONValue::getString(AString *value) const { if (mType != TYPE_STRING) { return false; } *value = *mValue.mString; return true; } bool JSONValue::getBoolean(bool *value) const { if (mType != TYPE_BOOLEAN) { return false; } *value = mValue.mBoolean; return true; } bool JSONValue::getObject(sp<JSONObject> *value) const { if (mType != TYPE_OBJECT) { return false; } *value = static_cast<JSONObject *>(mValue.mObjectOrArray); return true; } bool JSONValue::getArray(sp<JSONArray> *value) const { if (mType != TYPE_ARRAY) { return false; } *value = static_cast<JSONArray *>(mValue.mObjectOrArray); return true; } void JSONValue::setInt32(int32_t value) { unset(); mValue.mInt32 = value; mType = TYPE_INT32; } void JSONValue::setFloat(float value) { unset(); mValue.mFloat = value; mType = TYPE_FLOAT; } void JSONValue::setString(const AString &value) { unset(); mValue.mString = new AString(value); mType = TYPE_STRING; } void JSONValue::setBoolean(bool value) { unset(); mValue.mBoolean = value; mType = TYPE_BOOLEAN; } void JSONValue::setObject(const sp<JSONObject> &obj) { unset(); mValue.mObjectOrArray = obj.get(); mValue.mObjectOrArray->incStrong(this /* id */); mType = TYPE_OBJECT; } void JSONValue::setArray(const sp<JSONArray> &array) { unset(); mValue.mObjectOrArray = array.get(); mValue.mObjectOrArray->incStrong(this /* id */); mType = TYPE_ARRAY; } void JSONValue::unset() { switch (mType) { case TYPE_STRING: delete mValue.mString; break; case TYPE_OBJECT: case TYPE_ARRAY: mValue.mObjectOrArray->decStrong(this /* id */); break; default: break; } mType = TYPE_NULL; } static void EscapeString(const char *in, size_t inSize, AString *out) { CHECK(in != out->c_str()); out->clear(); for (size_t i = 0; i < inSize; ++i) { char c = in[i]; switch (c) { case '\"': out->append("\\\""); break; case '\\': out->append("\\\\"); break; case '/': out->append("\\/"); break; case '\x08': out->append("\\b"); break; case '\x0c': out->append("\\f"); break; case '\x0a': out->append("\\n"); break; case '\x0d': out->append("\\r"); break; case '\x09': out->append("\\t"); break; default: out->append(c); break; } } } AString JSONValue::toString(size_t depth, bool indentFirstLine) const { static const char kIndent[] = " "; AString out; switch (mType) { case TYPE_STRING: { AString escaped; EscapeString( mValue.mString->c_str(), mValue.mString->size(), &escaped); out.append("\""); out.append(escaped); out.append("\""); break; } case TYPE_INT32: { out = AStringPrintf("%d", mValue.mInt32); break; } case TYPE_FLOAT: { out = AStringPrintf("%f", mValue.mFloat); break; } case TYPE_BOOLEAN: { out = mValue.mBoolean ? "true" : "false"; break; } case TYPE_NULL: { out = "null"; break; } case TYPE_OBJECT: case TYPE_ARRAY: { out = (mType == TYPE_OBJECT) ? "{\n" : "[\n"; out.append(mValue.mObjectOrArray->internalToString(depth + 1)); out.append("\n"); out.append(kIndent, 2 * depth); out.append(mType == TYPE_OBJECT ? "}" : "]"); break; } default: TRESPASS(); } if (indentFirstLine) { out.insert(kIndent, 2 * depth, 0); } return out; } //////////////////////////////////////////////////////////////////////////////// // static sp<JSONCompound> JSONCompound::Parse(const char *data, size_t size) { JSONValue value; ssize_t result = JSONValue::Parse(data, size, &value); if (result < 0) { return NULL; } sp<JSONObject> obj; if (value.getObject(&obj)) { return obj; } sp<JSONArray> array; if (value.getArray(&array)) { return array; } return NULL; } AString JSONCompound::toString(size_t depth, bool indentFirstLine) const { JSONValue val; if (isObject()) { val.setObject((JSONObject *)this); } else { val.setArray((JSONArray *)this); } return val.toString(depth, indentFirstLine); } //////////////////////////////////////////////////////////////////////////////// JSONObject::JSONObject() {} JSONObject::~JSONObject() {} bool JSONObject::isObject() const { return true; } bool JSONObject::getValue(const char *key, JSONValue *value) const { ssize_t index = mValues.indexOfKey(key); if (index < 0) { return false; } *value = mValues.valueAt(index); return true; } void JSONObject::setValue(const char *key, const JSONValue &value) { mValues.add(AString(key), value); } AString JSONObject::internalToString(size_t depth) const { static const char kIndent[] = " "; AString out; for (size_t i = 0; i < mValues.size(); ++i) { AString key = mValues.keyAt(i); AString escapedKey; EscapeString(key.c_str(), key.size(), &escapedKey); out.append(kIndent, 2 * depth); out.append("\""); out.append(escapedKey); out.append("\": "); out.append(mValues.valueAt(i).toString(depth + 1, false)); if (i + 1 < mValues.size()) { out.append(",\n"); } } return out; } //////////////////////////////////////////////////////////////////////////////// JSONArray::JSONArray() {} JSONArray::~JSONArray() {} bool JSONArray::isObject() const { return false; } size_t JSONArray::size() const { return mValues.size(); } bool JSONArray::getValue(size_t key, JSONValue *value) const { if (key >= mValues.size()) { return false; } *value = mValues.itemAt(key); return true; } void JSONArray::addValue(const JSONValue &value) { mValues.push_back(value); } AString JSONArray::internalToString(size_t depth) const { AString out; for (size_t i = 0; i < mValues.size(); ++i) { out.append(mValues.itemAt(i).toString(depth)); if (i + 1 < mValues.size()) { out.append(",\n"); } } return out; } //////////////////////////////////////////////////////////////////////////////// } // namespace android