| /* |
| * 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 |
| |