summaryrefslogtreecommitdiff
path: root/src/json.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/json.cc')
-rw-r--r--src/json.cc612
1 files changed, 612 insertions, 0 deletions
diff --git a/src/json.cc b/src/json.cc
new file mode 100644
index 00000000..f389d854
--- /dev/null
+++ b/src/json.cc
@@ -0,0 +1,612 @@
+/* <!-- copyright */
+/*
+ * aria2 - The high speed download utility
+ *
+ * Copyright (C) 2011 Tatsuhiro Tsujikawa
+ *
+ * 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
+ *
+ * In addition, as a special exception, the copyright holders give
+ * permission to link the code of portions of this program with the
+ * OpenSSL library under certain conditions as described in each
+ * individual source file, and distribute linked combinations
+ * including the two.
+ * You must obey the GNU General Public License in all respects
+ * for all of the code used other than OpenSSL. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you
+ * do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source
+ * files in the program, then also delete it here.
+ */
+/* copyright --> */
+#include "json.h"
+
+#include <sstream>
+
+#include "array_fun.h"
+#include "DlAbortEx.h"
+#include "error_code.h"
+#include "a2functional.h"
+#include "util.h"
+#include "fmt.h"
+
+namespace aria2 {
+
+namespace json {
+
+// Function prototype declaration
+namespace {
+std::pair<SharedHandle<ValueBase>, std::string::const_iterator>
+decode
+(std::string::const_iterator first,
+ std::string::const_iterator last,
+ size_t depth);
+} // namespace
+
+namespace {
+const char WS[] = { 0x20, 0x09, 0x0a, 0x0d };
+const char ESCAPE_CHARS[] = { '"', '\\', '/', '\b', '\f', '\n', '\r', '\t' };
+const size_t MAX_STRUCTURE_DEPTH = 100;
+} // namespace
+
+namespace {
+std::string::const_iterator skipWs
+(std::string::const_iterator first,
+ std::string::const_iterator last)
+{
+ while(first != last && std::find(vbegin(WS), vend(WS), *first) != vend(WS)) {
+ ++first;
+ }
+ return first;
+}
+} // namespace
+
+namespace {
+void checkEof
+(std::string::const_iterator first,
+ std::string::const_iterator last)
+{
+ if(first == last) {
+ throw DL_ABORT_EX2("JSON decoding failed: unexpected EOF",
+ error_code::JSON_PARSE_ERROR);
+ }
+}
+} // namespace
+
+namespace {
+std::string::const_iterator
+decodeKeyword
+(std::string::const_iterator first,
+ std::string::const_iterator last,
+ const std::string& keyword)
+{
+ size_t len = keyword.size();
+ for(size_t i = 0; i < len; ++i) {
+ checkEof(first, last);
+ if(*first != keyword[i]) {
+ throw DL_ABORT_EX2(fmt("JSON decoding failed: %s not found.",
+ keyword.c_str()),
+ error_code::JSON_PARSE_ERROR);
+ }
+ ++first;
+ }
+ return first;
+}
+} // namespace
+
+namespace {
+std::pair<SharedHandle<ValueBase>, std::string::const_iterator>
+decodeTrue
+(std::string::const_iterator first,
+ std::string::const_iterator last)
+{
+ first = decodeKeyword(first, last, "true");
+ return std::make_pair(Bool::gTrue(), first);
+}
+} // namespace
+
+namespace {
+std::pair<SharedHandle<ValueBase>, std::string::const_iterator>
+decodeFalse
+(std::string::const_iterator first,
+ std::string::const_iterator last)
+{
+ first = decodeKeyword(first, last, "false");
+ return std::make_pair(Bool::gFalse(), first);
+}
+} // namespace
+
+namespace {
+std::pair<SharedHandle<ValueBase>, std::string::const_iterator>
+decodeNull
+(std::string::const_iterator first,
+ std::string::const_iterator last)
+{
+ first = decodeKeyword(first, last, "null");
+ return std::make_pair(Null::g(), first);
+}
+} // namespace
+
+namespace {
+std::pair<SharedHandle<ValueBase>, std::string::const_iterator>
+decodeString
+(std::string::const_iterator first,
+ std::string::const_iterator last)
+{
+ // Consume first char, assuming it is '"'.
+ ++first;
+ std::string s;
+ std::string::const_iterator offset = first;
+ while(first != last) {
+ if(*first == '"') {
+ break;
+ }
+ if(*first == '\\') {
+ s += std::string(offset, first);
+ ++first;
+ checkEof(first, last);
+ if(*first == 'u') {
+ ++first;
+ std::string uchars;
+ for(int i = 0; i < 4; ++i, ++first) {
+ checkEof(first, last);
+ uchars += *first;
+ }
+ checkEof(first, last);
+ uint16_t codepoint = util::parseUInt(uchars, 16);
+ if(codepoint <= 0x007fu) {
+ s += static_cast<char>(codepoint);
+ } else if(codepoint <= 0x07ffu) {
+ unsigned char c2 = 0x80u | (codepoint & 0x003fu);
+ unsigned char c1 = 0xC0u | (codepoint >> 6);
+ s += c1;
+ s += c2;
+ } else if(in(codepoint, 0xD800u, 0xDBFFu)) {
+ // surrogate pair
+ if(*first != '\\' || first+1 == last ||
+ *(first+1) != 'u') {
+ throw DL_ABORT_EX2("JSON decoding failed: bad UTF-8 sequence.",
+ error_code::JSON_PARSE_ERROR);
+ }
+ first += 2;
+ std::string uchars;
+ for(int i = 0; i < 4; ++i, ++first) {
+ checkEof(first, last);
+ uchars += *first;
+ }
+ checkEof(first, last);
+ uint16_t codepoint2 = util::parseUInt(uchars, 16);
+ if(!in(codepoint2, 0xDC00u, 0xDFFFu)) {
+ throw DL_ABORT_EX2("JSON decoding failed: bad UTF-8 sequence.",
+ error_code::JSON_PARSE_ERROR);
+ }
+ uint32_t fullcodepoint = 0x010000u;
+ fullcodepoint += (codepoint & 0x03FFu) << 10;
+ fullcodepoint += (codepoint2 & 0x03FFu);
+ unsigned char c4 = 0x80u | (fullcodepoint & 0x003Fu);
+ unsigned char c3 = 0x80u | ((fullcodepoint >> 6) & 0x003Fu);
+ unsigned char c2 = 0x80u | ((fullcodepoint >> 12) & 0x003Fu);
+ unsigned char c1 = 0xf0u | (fullcodepoint >> 18);
+ s += c1;
+ s += c2;
+ s += c3;
+ s += c4;
+ } else if(codepoint <= 0xffffu) {
+ unsigned char c3 = 0x80u | (codepoint & 0x003Fu);
+ unsigned char c2 = 0x80u | ((codepoint >> 6) & 0x003Fu);
+ unsigned char c1 = 0xE0u | (codepoint >> 12);
+ s += c1;
+ s += c2;
+ s += c3;
+ }
+ offset = first;
+ } else {
+ if(*first == 'b') {
+ s += '\b';
+ } else if(*first == 'f') {
+ s += '\f';
+ } else if(*first == 'n') {
+ s += '\n';
+ } else if(*first == 'r') {
+ s += '\r';
+ } else if(*first == 't') {
+ s += '\t';
+ } else {
+ s += *first;
+ }
+ ++first;
+ offset = first;
+ }
+ } else {
+ ++first;
+ }
+ }
+ checkEof(first, last);
+ if(std::distance(offset, first) > 0) {
+ s += std::string(offset, first);
+ }
+ if(!util::isUtf8(s)) {
+ throw DL_ABORT_EX2("JSON decoding failed: Non UTF-8 string.",
+ error_code::JSON_PARSE_ERROR);
+ }
+ ++first;
+ return std::make_pair(String::g(s), first);
+}
+} // namespace
+
+namespace {
+void checkEmptyDigit
+(std::string::const_iterator first,
+ std::string::const_iterator last)
+{
+ if(std::distance(first, last) == 0) {
+ throw DL_ABORT_EX2("JSON decoding failed: zero DIGIT.",
+ error_code::JSON_PARSE_ERROR);
+ }
+}
+} // namespace
+
+namespace {
+void checkLeadingZero
+(std::string::const_iterator first,
+ std::string::const_iterator last)
+{
+ if(std::distance(first, last) > 2 && *first == '0') {
+ throw DL_ABORT_EX2("JSON decoding failed: leading zero.",
+ error_code::JSON_PARSE_ERROR);
+ }
+}
+} // namespace
+
+namespace {
+std::pair<SharedHandle<ValueBase>, std::string::const_iterator>
+decodeNumber
+(std::string::const_iterator first,
+ std::string::const_iterator last)
+{
+ std::string s;
+ if(*first == '-') {
+ s += *first;
+ ++first;
+ }
+ std::string::const_iterator offset = first;
+ while(first != last && in(*first, '0', '9')) {
+ ++first;
+ }
+ checkEof(first, last);
+ checkEmptyDigit(offset, first);
+ checkLeadingZero(offset, first);
+ s += std::string(offset, first);
+ bool fp = false;
+ if(*first == '.') {
+ fp = true;
+ s += *first;
+ ++first;
+ offset = first;
+ while(first != last && in(*first, '0', '9')) {
+ ++first;
+ }
+ checkEof(first, last);
+ checkEmptyDigit(offset, first);
+ s += std::string(offset, first);
+ }
+ if(*first == 'e') {
+ fp = true;
+ s += *first;
+ ++first;
+ checkEof(first, last);
+ if(*first == '+' || *first == '-') {
+ s += *first;
+ ++first;
+ }
+ offset = first;
+ while(first != last && in(*first, '0', '9')) {
+ ++first;
+ }
+ checkEof(first, last);
+ checkEmptyDigit(offset, first);
+ s += std::string(offset, first);
+ }
+ if(fp) {
+ // Since we don't have floating point coutner part in ValueBase,
+ // we just treat it as string.
+ return std::make_pair(String::g(s), first);
+ } else {
+ Integer::ValueType val = util::parseLLInt(s);
+ return std::make_pair(Integer::g(val), first);
+ }
+}
+} // namespace
+
+namespace {
+void checkDepth(size_t depth)
+{
+ if(depth >= MAX_STRUCTURE_DEPTH) {
+ throw DL_ABORT_EX2("JSON decoding failed: Structure is too deep.",
+ error_code::JSON_PARSE_ERROR);
+ }
+}
+} // namespace
+
+namespace {
+std::pair<SharedHandle<ValueBase>, std::string::const_iterator>
+decodeArray
+(std::string::const_iterator first,
+ std::string::const_iterator last,
+ size_t depth)
+{
+ checkDepth(depth);
+ SharedHandle<List> list = List::g();
+ // Consume first char, assuming it is '['.
+ ++first;
+ first = skipWs(first, last);
+ checkEof(first, last);
+ if(*first != ']') {
+ while(1) {
+ std::pair<SharedHandle<ValueBase>, std::string::const_iterator>
+ r = decode(first, last, depth);
+ list->append(r.first);
+ first = r.second;
+ first = skipWs(first, last);
+ if(first == last || (*first != ',' && *first != ']')) {
+ throw DL_ABORT_EX2("JSON decoding failed:"
+ " value-separator ',' or ']' is not found.",
+ error_code::JSON_PARSE_ERROR);
+ }
+ if(*first == ']') {
+ break;
+ }
+ ++first;
+ }
+ }
+ ++first;
+ return std::make_pair(list, first);
+}
+} // namespace
+
+namespace {
+std::pair<SharedHandle<ValueBase>, std::string::const_iterator>
+decodeObject
+(std::string::const_iterator first,
+ std::string::const_iterator last,
+ size_t depth)
+{
+ checkDepth(depth);
+ SharedHandle<Dict> dict = Dict::g();
+ // Consume first char, assuming it is '{'
+ ++first;
+ first = skipWs(first, last);
+ checkEof(first, last);
+ if(*first != '}') {
+ while(1) {
+ std::pair<SharedHandle<ValueBase>, std::string::const_iterator>
+ keyRet = decodeString(first, last);
+ first = keyRet.second;
+ first = skipWs(first, last);
+ if(first == last || *first != ':') {
+ throw DL_ABORT_EX2("JSON decoding failed:"
+ " name-separator ':' is not found.",
+ error_code::JSON_PARSE_ERROR);
+ }
+ ++first;
+ std::pair<SharedHandle<ValueBase>, std::string::const_iterator>
+ valueRet = decode(first, last, depth);
+ dict->put(asString(keyRet.first)->s(), valueRet.first);
+ first = valueRet.second;
+ first = skipWs(first, last);
+ if(first == last || (*first != ',' && *first != '}')) {
+ throw DL_ABORT_EX2("JSON decoding failed:"
+ " value-separator ',' or '}' is not found.",
+ error_code::JSON_PARSE_ERROR);
+ }
+ if(*first == '}') {
+ break;
+ }
+ ++first;
+ first = skipWs(first, last);
+ checkEof(first, last);
+ }
+ }
+ ++first;
+ return std::make_pair(dict, first);
+}
+} // namespace
+
+namespace {
+std::pair<SharedHandle<ValueBase>, std::string::const_iterator>
+decode
+(std::string::const_iterator first,
+ std::string::const_iterator last,
+ size_t depth)
+{
+ first = skipWs(first, last);
+ if(first == last) {
+ throw DL_ABORT_EX2("JSON decoding failed:"
+ " Unexpected EOF in term context.",
+ error_code::JSON_PARSE_ERROR);
+ }
+ if(*first == '[') {
+ return decodeArray(first, last, depth+1);
+ } else if(*first == '{') {
+ return decodeObject(first, last, depth+1);
+ } else if(*first == '"') {
+ return decodeString(first, last);
+ } else if(*first == '-' || in(*first, '0', '9')) {
+ return decodeNumber(first, last);
+ } else if(*first == 't') {
+ return decodeTrue(first, last);
+ } else if(*first == 'f') {
+ return decodeFalse(first, last);
+ } else if(*first == 'n') {
+ return decodeNull(first, last);
+ } else {
+ throw DL_ABORT_EX2("JSON decoding failed:"
+ " Unexpected EOF in term context.",
+ error_code::JSON_PARSE_ERROR);
+ }
+}
+} // namespace
+
+SharedHandle<ValueBase> decode(const std::string& json)
+{
+ std::string::const_iterator first = json.begin();
+ std::string::const_iterator last = json.end();
+ first = skipWs(first, last);
+ if(first == last) {
+ throw DL_ABORT_EX2("JSON decoding failed:"
+ " Unexpected EOF in term context.",
+ error_code::JSON_PARSE_ERROR);
+ }
+ std::pair<SharedHandle<ValueBase>, std::string::const_iterator> r;
+ if(*first == '[') {
+ r = decodeArray(first, last, 1);
+ } else if(*first == '{') {
+ r = decodeObject(first, last, 1);
+ } else {
+ throw DL_ABORT_EX2("JSON decoding failed:"
+ " Unexpected EOF in term context.",
+ error_code::JSON_PARSE_ERROR);
+ }
+ return r.first;
+}
+
+std::string jsonEscape(const std::string& s)
+{
+ std::string t;
+ for(std::string::const_iterator i = s.begin(), eoi = s.end(); i != eoi;
+ ++i) {
+ if(*i == '"' || *i == '\\' || *i == '/') {
+ t += '\\';
+ t += *i;
+ } else if(*i == '\b') {
+ t += "\\b";
+ } else if(*i == '\f') {
+ t += "\\f";
+ } else if(*i == '\n') {
+ t += "\\n";
+ } else if(*i == '\r') {
+ t += "\\r";
+ } else if(*i == '\t') {
+ t += "\\t";
+ } else if(in(static_cast<unsigned char>(*i), 0x00u, 0x1Fu)) {
+ t += "\\u00";
+ char temp[3];
+ temp[2] = '\0';
+ temp[0] = (*i >> 4);
+ temp[1] = (*i)&0x0Fu;
+ for(int j = 0; j < 2; ++j) {
+ if(temp[j] < 10) {
+ temp[j] += '0';
+ } else {
+ temp[j] += 'A'-10;
+ }
+ }
+ t += temp;
+ } else {
+ t += *i;
+ }
+ }
+ return t;
+}
+
+std::string encode(const ValueBase* vlb)
+{
+ class JsonValueBaseVisitor:public ValueBaseVisitor {
+ private:
+ std::ostringstream out_;
+ public:
+ virtual void visit(const String& string)
+ {
+ const std::string& s = string.s();
+ std::string t = jsonEscape(s);
+ out_ << '"';
+ out_.write(t.data(), t.size());
+ out_ << '"';
+ }
+
+ virtual void visit(const Integer& integer)
+ {
+ out_ << integer.i();
+ }
+
+ virtual void visit(const Bool& boolValue)
+ {
+ out_ << (boolValue.val() ? "true" : "false");
+ }
+
+ virtual void visit(const Null& nullValue)
+ {
+ out_ << "null";
+ }
+
+ virtual void visit(const List& list)
+ {
+ out_ << '[';
+ List::ValueType::const_iterator i = list.begin();
+ if(!list.empty()) {
+ (*i)->accept(*this);
+ }
+ ++i;
+ for(List::ValueType::const_iterator eoi = list.end(); i != eoi; ++i){
+ out_ << ',';
+ (*i)->accept(*this);
+ }
+ out_ << ']';
+ }
+
+ virtual void visit(const Dict& dict)
+ {
+ out_ << '{';
+ Dict::ValueType::const_iterator i = dict.begin();
+ if(!dict.empty()) {
+ std::string key = jsonEscape((*i).first);
+ out_ << '"';
+ out_.write(key.data(), key.size());
+ out_ << "\":";
+ (*i).second->accept(*this);
+ }
+ ++i;
+ for(Dict::ValueType::const_iterator eoi = dict.end(); i != eoi; ++i){
+ out_ << ',';
+ std::string key = jsonEscape((*i).first);
+ out_ << '"';
+ out_.write(key.data(), key.size());
+ out_ << "\":";
+ (*i).second->accept(*this);
+ }
+ out_ << '}';
+ }
+
+ std::string getResult() const
+ {
+ return out_.str();
+ }
+ };
+ JsonValueBaseVisitor visitor;
+ vlb->accept(visitor);
+ return visitor.getResult();
+}
+
+// Serializes JSON object or array.
+std::string encode(const SharedHandle<ValueBase>& json)
+{
+ std::ostringstream out;
+ return encode(out, json.get()).str();
+}
+
+} // namespace json
+
+} // namespace aria2