You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
751 lines
17 KiB
751 lines
17 KiB
/*
|
|
* 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
|
|
|