mirror of https://github.com/ArduPilot/ardupilot
719 lines
19 KiB
C++
719 lines
19 KiB
C++
/*
|
|
ArduPilot JSON parser, based on picojson.h cloned from:
|
|
https://github.com/kazuho/picojson
|
|
*/
|
|
/*
|
|
Picojson copyright:
|
|
|
|
* Copyright 2009-2010 Cybozu Labs, Inc.
|
|
* Copyright 2011-2014 Kazuho Oku
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#pragma GCC optimize("Os")
|
|
|
|
#define ALLOW_DOUBLE_MATH_FUNCTIONS
|
|
|
|
#include "AP_JSON.h"
|
|
#include <AP_Filesystem/AP_Filesystem.h>
|
|
#include <AP_Math/AP_Math.h>
|
|
#include <stdio.h>
|
|
|
|
/*
|
|
load JSON file, returning a value object or nullptr on failure
|
|
*/
|
|
AP_JSON::value *AP_JSON::load_json(const char *filename)
|
|
{
|
|
struct stat st;
|
|
if (AP::FS().stat(filename, &st) != 0) {
|
|
::printf("No such json file %s\n", filename);
|
|
return nullptr;
|
|
}
|
|
int fd = AP::FS().open(filename, O_RDONLY);
|
|
if (fd == -1) {
|
|
::printf("failed to open json %s\n", filename);
|
|
return nullptr;
|
|
}
|
|
char *buf = NEW_NOTHROW char[st.st_size+1];
|
|
if (buf == nullptr) {
|
|
AP::FS().close(fd);
|
|
::printf("failed to allocate json %s\n", filename);
|
|
return nullptr;
|
|
}
|
|
if (AP::FS().read(fd, buf, st.st_size) != st.st_size) {
|
|
::printf("failed to read json %s\n", filename);
|
|
delete[] buf;
|
|
AP::FS().close(fd);
|
|
return nullptr;
|
|
}
|
|
AP::FS().close(fd);
|
|
|
|
char *start = strchr(buf, '{');
|
|
if (!start) {
|
|
::printf("Invalid json %s\n", filename);
|
|
delete[] buf;
|
|
return nullptr;
|
|
}
|
|
|
|
/*
|
|
remove comments, as not allowed by the parser
|
|
*/
|
|
for (char *p = strchr(start,'#'); p; p=strchr(p+1, '#')) {
|
|
// clear to end of line
|
|
do {
|
|
*p++ = ' ';
|
|
} while (*p != '\n' && *p != '\r' && *p);
|
|
}
|
|
|
|
AP_JSON::value *obj = NEW_NOTHROW AP_JSON::value;
|
|
if (obj == nullptr) {
|
|
::printf("Invalid allocate json for %s\n", filename);
|
|
delete[] buf;
|
|
return nullptr;
|
|
}
|
|
std::string err = AP_JSON::parse(*obj, start);
|
|
if (!err.empty()) {
|
|
::printf("parse failed for json %s\n", filename);
|
|
delete obj;
|
|
delete[] buf;
|
|
return nullptr;
|
|
}
|
|
|
|
delete[] buf;
|
|
return obj;
|
|
}
|
|
|
|
typedef AP_JSON::value::array array;
|
|
typedef AP_JSON::value::object object;
|
|
typedef AP_JSON::value value;
|
|
typedef AP_JSON::null null;
|
|
|
|
enum {
|
|
null_type,
|
|
boolean_type,
|
|
number_type,
|
|
string_type,
|
|
array_type,
|
|
object_type
|
|
};
|
|
|
|
|
|
AP_JSON::value::value() : type_(null_type), u_()
|
|
{
|
|
}
|
|
|
|
AP_JSON::value::value(int type, bool) : type_(type), u_()
|
|
{
|
|
switch (type) {
|
|
#define INIT(p, v) \
|
|
case p##type: \
|
|
u_.p = v; \
|
|
break
|
|
INIT(boolean_, false);
|
|
INIT(number_, 0.0);
|
|
INIT(string_, NEW_NOTHROW std::string());
|
|
INIT(array_, NEW_NOTHROW array());
|
|
INIT(object_, NEW_NOTHROW object());
|
|
#undef INIT
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
AP_JSON::value::value(bool b) : type_(boolean_type), u_()
|
|
{
|
|
u_.boolean_ = b;
|
|
}
|
|
|
|
AP_JSON::value::value(double n) : type_(number_type), u_()
|
|
{
|
|
u_.number_ = n;
|
|
}
|
|
|
|
AP_JSON::value::value(const std::string &s) : type_(string_type), u_()
|
|
{
|
|
u_.string_ = NEW_NOTHROW std::string(s);
|
|
}
|
|
|
|
AP_JSON::value::value(const array &a) : type_(array_type), u_()
|
|
{
|
|
u_.array_ = NEW_NOTHROW array(a);
|
|
}
|
|
|
|
AP_JSON::value::value(const object &o) : type_(object_type), u_()
|
|
{
|
|
u_.object_ = NEW_NOTHROW object(o);
|
|
}
|
|
|
|
AP_JSON::value::value(std::string &&s) : type_(string_type), u_()
|
|
{
|
|
u_.string_ = NEW_NOTHROW std::string(std::move(s));
|
|
}
|
|
|
|
AP_JSON::value::value(array &&a) : type_(array_type), u_()
|
|
{
|
|
u_.array_ = NEW_NOTHROW array(std::move(a));
|
|
}
|
|
|
|
AP_JSON::value::value(object &&o) : type_(object_type), u_()
|
|
{
|
|
u_.object_ = NEW_NOTHROW object(std::move(o));
|
|
}
|
|
|
|
AP_JSON::value::value(const char *s) : type_(string_type), u_()
|
|
{
|
|
u_.string_ = NEW_NOTHROW std::string(s);
|
|
}
|
|
|
|
AP_JSON::value::value(const char *s, size_t len) : type_(string_type), u_()
|
|
{
|
|
u_.string_ = NEW_NOTHROW std::string(s, len);
|
|
}
|
|
|
|
void AP_JSON::value::clear()
|
|
{
|
|
switch (type_) {
|
|
#define DEINIT(p) \
|
|
case p##type: \
|
|
delete u_.p; \
|
|
break
|
|
DEINIT(string_);
|
|
DEINIT(array_);
|
|
DEINIT(object_);
|
|
#undef DEINIT
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
AP_JSON::value::~value()
|
|
{
|
|
clear();
|
|
}
|
|
|
|
AP_JSON::value::value(const value &x) : type_(x.type_), u_()
|
|
{
|
|
switch (type_) {
|
|
#define INIT(p, v) \
|
|
case p##type: \
|
|
u_.p = v; \
|
|
break
|
|
INIT(string_, NEW_NOTHROW std::string(*x.u_.string_));
|
|
INIT(array_, NEW_NOTHROW array(*x.u_.array_));
|
|
INIT(object_, NEW_NOTHROW object(*x.u_.object_));
|
|
#undef INIT
|
|
default:
|
|
u_ = x.u_;
|
|
break;
|
|
}
|
|
}
|
|
|
|
value &AP_JSON::value::operator=(const value &x)
|
|
{
|
|
if (this != &x) {
|
|
value t(x);
|
|
swap(t);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
AP_JSON::value::value(value &&x) : type_(null_type), u_()
|
|
{
|
|
swap(x);
|
|
}
|
|
value &AP_JSON::value::operator=(value &&x)
|
|
{
|
|
swap(x);
|
|
return *this;
|
|
}
|
|
|
|
void AP_JSON::value::swap(value &x)
|
|
{
|
|
std::swap(type_, x.type_);
|
|
std::swap(u_, x.u_);
|
|
}
|
|
|
|
#define IS(ctype, jtype) \
|
|
template <> bool AP_JSON::value::is<ctype>() const { \
|
|
return type_ == jtype##_type; \
|
|
}
|
|
IS(null, null)
|
|
IS(bool, boolean)
|
|
IS(std::string, string)
|
|
IS(array, array)
|
|
IS(object, object)
|
|
#undef IS
|
|
template <> bool AP_JSON::value::is<double>() const
|
|
{
|
|
return type_ == number_type;
|
|
}
|
|
|
|
#define GET(ctype, var) \
|
|
template <> const ctype &AP_JSON::value::get<ctype>() const { \
|
|
return var; \
|
|
} \
|
|
template <> ctype &AP_JSON::value::get<ctype>() { \
|
|
return var; \
|
|
}
|
|
GET(bool, u_.boolean_)
|
|
GET(std::string, *u_.string_)
|
|
GET(array, *u_.array_)
|
|
GET(object, *u_.object_)
|
|
GET(double, u_.number_)
|
|
#undef GET
|
|
|
|
#define SET(ctype, jtype, setter) \
|
|
template <> void AP_JSON::value::set<ctype>(const ctype &_val) { \
|
|
clear(); \
|
|
type_ = jtype##_type; \
|
|
setter \
|
|
}
|
|
SET(bool, boolean, u_.boolean_ = _val;)
|
|
SET(std::string, string, u_.string_ = NEW_NOTHROW std::string(_val);)
|
|
SET(array, array, u_.array_ = NEW_NOTHROW array(_val);)
|
|
SET(object, object, u_.object_ = NEW_NOTHROW object(_val);)
|
|
SET(double, number, u_.number_ = _val;)
|
|
#undef SET
|
|
|
|
#define MOVESET(ctype, jtype, setter) \
|
|
template <> void AP_JSON::value::set<ctype>(ctype && _val) { \
|
|
clear(); \
|
|
type_ = jtype##_type; \
|
|
setter \
|
|
}
|
|
MOVESET(std::string, string, u_.string_ = NEW_NOTHROW std::string(std::move(_val));)
|
|
MOVESET(array, array, u_.array_ = NEW_NOTHROW array(std::move(_val));)
|
|
MOVESET(object, object, u_.object_ = NEW_NOTHROW object(std::move(_val));)
|
|
#undef MOVESET
|
|
|
|
bool AP_JSON::value::evaluate_as_boolean() const
|
|
{
|
|
switch (type_) {
|
|
case null_type:
|
|
return false;
|
|
case boolean_type:
|
|
return u_.boolean_;
|
|
case number_type:
|
|
return !is_zero(u_.number_);
|
|
case string_type:
|
|
return !u_.string_->empty();
|
|
default:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
const value &AP_JSON::value::get(const size_t idx) const
|
|
{
|
|
static value s_null;
|
|
return idx < u_.array_->size() ? (*u_.array_)[idx] : s_null;
|
|
}
|
|
|
|
value &AP_JSON::value::get(const size_t idx)
|
|
{
|
|
static value s_null;
|
|
return idx < u_.array_->size() ? (*u_.array_)[idx] : s_null;
|
|
}
|
|
|
|
const value &AP_JSON::value::get(const std::string &key) const
|
|
{
|
|
static value s_null;
|
|
object::const_iterator i = u_.object_->find(key);
|
|
return i != u_.object_->end() ? i->second : s_null;
|
|
}
|
|
|
|
value &AP_JSON::value::get(const std::string &key)
|
|
{
|
|
static value s_null;
|
|
object::iterator i = u_.object_->find(key);
|
|
return i != u_.object_->end() ? i->second : s_null;
|
|
}
|
|
|
|
bool AP_JSON::value::contains(const size_t idx) const
|
|
{
|
|
return idx < u_.array_->size();
|
|
}
|
|
|
|
bool AP_JSON::value::contains(const std::string &key) const
|
|
{
|
|
object::const_iterator i = u_.object_->find(key);
|
|
return i != u_.object_->end();
|
|
}
|
|
|
|
std::string AP_JSON::value::to_str() const
|
|
{
|
|
switch (type_) {
|
|
case null_type:
|
|
return "null";
|
|
case boolean_type:
|
|
return u_.boolean_ ? "true" : "false";
|
|
case number_type: {
|
|
char buf[256];
|
|
double tmp;
|
|
snprintf(buf, sizeof(buf), fabs(u_.number_) < (1ULL << 53) && is_zero(modf(u_.number_, &tmp)) ? "%.f" : "%.17g", u_.number_);
|
|
return buf;
|
|
}
|
|
case string_type:
|
|
return *u_.string_;
|
|
case array_type:
|
|
return "array";
|
|
case object_type:
|
|
return "object";
|
|
default:
|
|
break;
|
|
}
|
|
return std::string();
|
|
}
|
|
|
|
template <typename Iter> void copy(const std::string &s, Iter oi)
|
|
{
|
|
std::copy(s.begin(), s.end(), oi);
|
|
}
|
|
|
|
template <typename Iter> class input
|
|
{
|
|
protected:
|
|
Iter cur_, end_;
|
|
bool consumed_;
|
|
int line_;
|
|
|
|
public:
|
|
input(const Iter &first, const Iter &last) : cur_(first), end_(last), consumed_(false), line_(1)
|
|
{
|
|
}
|
|
int getc()
|
|
{
|
|
if (consumed_) {
|
|
if (*cur_ == '\n') {
|
|
++line_;
|
|
}
|
|
++cur_;
|
|
}
|
|
if (cur_ == end_) {
|
|
consumed_ = false;
|
|
return -1;
|
|
}
|
|
consumed_ = true;
|
|
return *cur_ & 0xff;
|
|
}
|
|
void ungetc()
|
|
{
|
|
consumed_ = false;
|
|
}
|
|
Iter cur() const
|
|
{
|
|
if (consumed_) {
|
|
input<Iter> *self = const_cast<input<Iter> *>(this);
|
|
self->consumed_ = false;
|
|
++self->cur_;
|
|
}
|
|
return cur_;
|
|
}
|
|
int line() const
|
|
{
|
|
return line_;
|
|
}
|
|
void skip_ws()
|
|
{
|
|
while (1) {
|
|
int ch = getc();
|
|
if (!(ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r')) {
|
|
ungetc();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
bool expect(const int expected)
|
|
{
|
|
skip_ws();
|
|
if (getc() != expected) {
|
|
ungetc();
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
bool match(const std::string &pattern)
|
|
{
|
|
for (std::string::const_iterator pi(pattern.begin()); pi != pattern.end(); ++pi) {
|
|
if (getc() != *pi) {
|
|
ungetc();
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
template <typename Iter> int _parse_quadhex(input<Iter> &in)
|
|
{
|
|
int uni_ch = 0, hex;
|
|
for (int i = 0; i < 4; i++) {
|
|
if ((hex = in.getc()) == -1) {
|
|
return -1;
|
|
}
|
|
if ('0' <= hex && hex <= '9') {
|
|
hex -= '0';
|
|
} else if ('A' <= hex && hex <= 'F') {
|
|
hex -= 'A' - 0xa;
|
|
} else if ('a' <= hex && hex <= 'f') {
|
|
hex -= 'a' - 0xa;
|
|
} else {
|
|
in.ungetc();
|
|
return -1;
|
|
}
|
|
uni_ch = uni_ch * 16 + hex;
|
|
}
|
|
return uni_ch;
|
|
}
|
|
|
|
template <typename String, typename Iter> bool _parse_string(String &out, input<Iter> &in)
|
|
{
|
|
while (1) {
|
|
int ch = in.getc();
|
|
if (ch < ' ') {
|
|
in.ungetc();
|
|
return false;
|
|
} else if (ch == '"') {
|
|
return true;
|
|
} else if (ch == '\\') {
|
|
if ((ch = in.getc()) == -1) {
|
|
return false;
|
|
}
|
|
switch (ch) {
|
|
#define MAP(sym, val) \
|
|
case sym: \
|
|
out.push_back(val); \
|
|
break
|
|
MAP('"', '\"');
|
|
MAP('\\', '\\');
|
|
MAP('/', '/');
|
|
MAP('b', '\b');
|
|
MAP('f', '\f');
|
|
MAP('n', '\n');
|
|
MAP('r', '\r');
|
|
MAP('t', '\t');
|
|
#undef MAP
|
|
default:
|
|
return false;
|
|
}
|
|
} else {
|
|
out.push_back(static_cast<char>(ch));
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
template <typename Context, typename Iter> bool _parse_array(Context &ctx, input<Iter> &in)
|
|
{
|
|
if (!ctx.parse_array_start()) {
|
|
return false;
|
|
}
|
|
size_t idx = 0;
|
|
if (in.expect(']')) {
|
|
return ctx.parse_array_stop(idx);
|
|
}
|
|
do {
|
|
if (!ctx.parse_array_item(in, idx)) {
|
|
return false;
|
|
}
|
|
idx++;
|
|
} while (in.expect(','));
|
|
return in.expect(']') && ctx.parse_array_stop(idx);
|
|
}
|
|
|
|
template <typename Context, typename Iter> bool _parse_object(Context &ctx, input<Iter> &in)
|
|
{
|
|
if (!ctx.parse_object_start()) {
|
|
return false;
|
|
}
|
|
if (in.expect('}')) {
|
|
return true;
|
|
}
|
|
do {
|
|
std::string key;
|
|
if (!in.expect('"') || !_parse_string(key, in) || !in.expect(':')) {
|
|
return false;
|
|
}
|
|
if (!ctx.parse_object_item(in, key)) {
|
|
return false;
|
|
}
|
|
} while (in.expect(','));
|
|
return in.expect('}');
|
|
}
|
|
|
|
template <typename Iter> std::string _parse_number(input<Iter> &in)
|
|
{
|
|
std::string num_str;
|
|
while (1) {
|
|
int ch = in.getc();
|
|
if (('0' <= ch && ch <= '9') || ch == '+' || ch == '-' || ch == 'e' || ch == 'E') {
|
|
num_str.push_back(static_cast<char>(ch));
|
|
} else if (ch == '.') {
|
|
num_str.push_back('.');
|
|
} else {
|
|
in.ungetc();
|
|
break;
|
|
}
|
|
}
|
|
return num_str;
|
|
}
|
|
|
|
template <typename Context, typename Iter> bool _parse(Context &ctx, input<Iter> &in)
|
|
{
|
|
in.skip_ws();
|
|
int ch = in.getc();
|
|
switch (ch) {
|
|
#define IS(ch, text, op) \
|
|
case ch: \
|
|
if (in.match(text) && op) { \
|
|
return true; \
|
|
} else { \
|
|
return false; \
|
|
}
|
|
IS('n', "ull", ctx.set_null());
|
|
IS('f', "alse", ctx.set_bool(false));
|
|
IS('t', "rue", ctx.set_bool(true));
|
|
#undef IS
|
|
case '"':
|
|
return ctx.parse_string(in);
|
|
case '[':
|
|
return _parse_array(ctx, in);
|
|
case '{':
|
|
return _parse_object(ctx, in);
|
|
default:
|
|
if (('0' <= ch && ch <= '9') || ch == '-') {
|
|
double f;
|
|
char *endp;
|
|
in.ungetc();
|
|
std::string num_str(_parse_number(in));
|
|
if (num_str.empty()) {
|
|
return false;
|
|
}
|
|
f = strtod(num_str.c_str(), &endp);
|
|
if (endp == num_str.c_str() + num_str.size()) {
|
|
ctx.set_number(f);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
in.ungetc();
|
|
return false;
|
|
}
|
|
|
|
class default_parse_context
|
|
{
|
|
protected:
|
|
value *out_;
|
|
|
|
public:
|
|
default_parse_context(value *out) : out_(out)
|
|
{
|
|
}
|
|
bool set_null()
|
|
{
|
|
*out_ = value();
|
|
return true;
|
|
}
|
|
bool set_bool(bool b)
|
|
{
|
|
*out_ = value(b);
|
|
return true;
|
|
}
|
|
bool set_number(double f)
|
|
{
|
|
*out_ = value(f);
|
|
return true;
|
|
}
|
|
template <typename Iter> bool parse_string(input<Iter> &in)
|
|
{
|
|
*out_ = value(string_type, false);
|
|
return _parse_string(out_->get<std::string>(), in);
|
|
}
|
|
bool parse_array_start()
|
|
{
|
|
*out_ = value(array_type, false);
|
|
return true;
|
|
}
|
|
template <typename Iter> bool parse_array_item(input<Iter> &in, size_t)
|
|
{
|
|
array &a = out_->get<array>();
|
|
a.push_back(value());
|
|
default_parse_context ctx(&a.back());
|
|
return _parse(ctx, in);
|
|
}
|
|
bool parse_array_stop(size_t)
|
|
{
|
|
return true;
|
|
}
|
|
bool parse_object_start()
|
|
{
|
|
*out_ = value(object_type, false);
|
|
return true;
|
|
}
|
|
template <typename Iter> bool parse_object_item(input<Iter> &in, const std::string &key)
|
|
{
|
|
object &o = out_->get<object>();
|
|
default_parse_context ctx(&o[key]);
|
|
return _parse(ctx, in);
|
|
}
|
|
|
|
private:
|
|
default_parse_context(const default_parse_context &);
|
|
default_parse_context &operator=(const default_parse_context &);
|
|
};
|
|
|
|
template <typename Context, typename Iter> Iter _parse(Context &ctx, const Iter &first, const Iter &last, std::string *err)
|
|
{
|
|
input<Iter> in(first, last);
|
|
if (!_parse(ctx, in) && err != NULL) {
|
|
char buf[64];
|
|
snprintf(buf, sizeof(buf), "syntax error at line %d near: ", in.line());
|
|
*err = buf;
|
|
while (1) {
|
|
int ch = in.getc();
|
|
if (ch == -1 || ch == '\n') {
|
|
break;
|
|
} else if (ch >= ' ') {
|
|
err->push_back(static_cast<char>(ch));
|
|
}
|
|
}
|
|
}
|
|
return in.cur();
|
|
}
|
|
|
|
template <typename Iter> Iter parse(value &out, const Iter &first, const Iter &last, std::string *err)
|
|
{
|
|
default_parse_context ctx(&out);
|
|
return _parse(ctx, first, last, err);
|
|
}
|
|
|
|
std::string AP_JSON::parse(value &out, const std::string &s)
|
|
{
|
|
std::string err;
|
|
::parse(out, s.begin(), s.end(), &err);
|
|
return err;
|
|
}
|