diff --git a/src/json.cc b/src/json.cc index 81341ee6..78463fc3 100644 --- a/src/json.cc +++ b/src/json.cc @@ -37,441 +37,14 @@ #include #include "array_fun.h" -#include "DlAbortEx.h" -#include "error_code.h" #include "a2functional.h" #include "util.h" -#include "fmt.h" #include "base64.h" namespace aria2 { namespace json { -// Function prototype declaration -namespace { -template -std::pair, InputIterator> -decode(InputIterator first, InputIterator 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 { -template -InputIterator skipWs(InputIterator first, InputIterator last) -{ - while(first != last && std::find(vbegin(WS), vend(WS), *first) != vend(WS)) { - ++first; - } - return first; -} -} // namespace - -namespace { -template -void checkEof(InputIterator first, InputIterator last) -{ - if(first == last) { - throw DL_ABORT_EX2("JSON decoding failed: unexpected EOF", - error_code::JSON_PARSE_ERROR); - } -} -} // namespace - -namespace { -template -InputIterator -decodeKeyword(InputIterator first, InputIterator 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 { -template -std::pair, InputIterator> -decodeTrue(InputIterator first, InputIterator last) -{ - first = decodeKeyword(first, last, "true"); - return std::make_pair(Bool::gTrue(), first); -} -} // namespace - -namespace { -template -std::pair, InputIterator> -decodeFalse(InputIterator first, InputIterator last) -{ - first = decodeKeyword(first, last, "false"); - return std::make_pair(Bool::gFalse(), first); -} -} // namespace - -namespace { -template -std::pair, InputIterator> -decodeNull(InputIterator first, InputIterator last) -{ - first = decodeKeyword(first, last, "null"); - return std::make_pair(Null::g(), first); -} -} // namespace - -namespace { -template -std::pair, InputIterator> -decodeString(InputIterator first, InputIterator last) -{ - // Consume first char, assuming it is '"'. - ++first; - std::string s; - InputIterator offset = first; - while(first != last) { - if(*first == '"') { - break; - } - if(*first == '\\') { - s.append(offset, first); - ++first; - checkEof(first, last); - if(*first == 'u') { - ++first; - InputIterator uchars = first; - for(int i = 0; i < 4; ++i, ++first) { - checkEof(first, last); - } - checkEof(first, last); - uint16_t codepoint = util::parseUInt(std::string(uchars, first), 16); - if(codepoint <= 0x007fu) { - unsigned char temp[1]; - temp[0] = static_cast(codepoint); - s.append(&temp[0], &temp[sizeof(temp)]); - } else if(codepoint <= 0x07ffu) { - unsigned char temp[2]; - temp[0] = 0xC0u | (codepoint >> 6); - temp[1] = 0x80u | (codepoint & 0x003fu); - s.append(&temp[0], &temp[sizeof(temp)]); - } 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; - InputIterator uchars = first; - for(int i = 0; i < 4; ++i, ++first) { - checkEof(first, last); - } - checkEof(first, last); - uint16_t codepoint2 = util::parseUInt(std::string(uchars, first), 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 temp[4]; - temp[0] = 0xf0u | (fullcodepoint >> 18); - temp[1] = 0x80u | ((fullcodepoint >> 12) & 0x003Fu); - temp[2] = 0x80u | ((fullcodepoint >> 6) & 0x003Fu); - temp[3] = 0x80u | (fullcodepoint & 0x003Fu); - s.append(&temp[0], &temp[sizeof(temp)]); - } else { - unsigned char temp[3]; - temp[0] = 0xE0u | (codepoint >> 12); - temp[1] = 0x80u | ((codepoint >> 6) & 0x003Fu); - temp[2] = 0x80u | (codepoint & 0x003Fu); - s.append(&temp[0], &temp[sizeof(temp)]); - } - 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.append(first, first+1); - } - ++first; - offset = first; - } - } else { - ++first; - } - } - checkEof(first, last); - if(std::distance(offset, first) > 0) { - s.append(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 { -template -void checkEmptyDigit(InputIterator first, InputIterator last) -{ - if(std::distance(first, last) == 0) { - throw DL_ABORT_EX2("JSON decoding failed: zero DIGIT.", - error_code::JSON_PARSE_ERROR); - } -} -} // namespace - -namespace { -template -void checkLeadingZero(InputIterator first, InputIterator 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 { -template -std::pair, InputIterator> -decodeNumber(InputIterator first, InputIterator last) -{ - std::string s; - if(*first == '-') { - s.append(first, first+1); - ++first; - } - InputIterator offset = first; - while(first != last && in(*first, '0', '9')) { - ++first; - } - checkEof(first, last); - checkEmptyDigit(offset, first); - checkLeadingZero(offset, first); - s.append(offset, first); - bool fp = false; - if(*first == '.') { - fp = true; - s.append(first, first+1); - ++first; - offset = first; - while(first != last && in(*first, '0', '9')) { - ++first; - } - checkEof(first, last); - checkEmptyDigit(offset, first); - s.append(offset, first); - } - if(*first == 'e') { - fp = true; - s.append(first, first+1); - ++first; - checkEof(first, last); - if(*first == '+' || *first == '-') { - s.append(first, first+1); - ++first; - } - offset = first; - while(first != last && in(*first, '0', '9')) { - ++first; - } - checkEof(first, last); - checkEmptyDigit(offset, first); - s.append(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 { -template -std::pair, InputIterator> -decodeArray(InputIterator first, InputIterator last, size_t depth) -{ - checkDepth(depth); - SharedHandle list = List::g(); - // Consume first char, assuming it is '['. - ++first; - first = skipWs(first, last); - checkEof(first, last); - if(*first != ']') { - while(1) { - std::pair, InputIterator> - 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 { -template -std::pair, InputIterator> -decodeObject(InputIterator first, InputIterator last, size_t depth) -{ - checkDepth(depth); - SharedHandle dict = Dict::g(); - // Consume first char, assuming it is '{' - ++first; - first = skipWs(first, last); - checkEof(first, last); - if(*first != '}') { - while(1) { - std::pair, InputIterator> - 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, InputIterator> - valueRet = decode(first, last, depth); - dict->put(downcast(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 { -template -std::pair, InputIterator> -decode(InputIterator first, InputIterator 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 character in term context.", - error_code::JSON_PARSE_ERROR); - } -} -} // namespace - -template -SharedHandle decode(InputIterator first, InputIterator last) -{ - 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, InputIterator> 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; -} - -SharedHandle decode(const std::string& json) -{ - return decode(json.begin(), json.end()); -} - -SharedHandle decode(const unsigned char* json, size_t len) -{ - return decode(json, json+len); -} - std::string jsonEscape(const std::string& s) { std::string t; diff --git a/src/json.h b/src/json.h index 7538a2f2..7056dfec 100644 --- a/src/json.h +++ b/src/json.h @@ -42,11 +42,6 @@ namespace aria2 { namespace json { -// Parses JSON text defined in RFC4627. -SharedHandle decode(const std::string& json); - -SharedHandle decode(const unsigned char* json, size_t len); - std::string jsonEscape(const std::string& s); template diff --git a/test/JsonTest.cc b/test/JsonTest.cc index 79e4a5d8..aac95f70 100644 --- a/test/JsonTest.cc +++ b/test/JsonTest.cc @@ -12,414 +12,18 @@ namespace aria2 { class JsonTest:public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(JsonTest); - CPPUNIT_TEST(testDecode); - CPPUNIT_TEST(testDecode_error); CPPUNIT_TEST(testEncode); CPPUNIT_TEST(testDecodeGetParams); CPPUNIT_TEST_SUITE_END(); private: public: - void testDecode(); - void testDecode_error(); void testEncode(); void testDecodeGetParams(); }; CPPUNIT_TEST_SUITE_REGISTRATION( JsonTest ); -void JsonTest::testDecode() -{ - { - // empty object - SharedHandle r = json::decode("{}"); - const Dict* dict = downcast(r); - CPPUNIT_ASSERT(dict); - } - { - // empty object - SharedHandle r = json::decode("{ }"); - const Dict* dict = downcast(r); - CPPUNIT_ASSERT(dict); - } - { - // empty array - SharedHandle r = json::decode("[]"); - const List* list = downcast(r); - CPPUNIT_ASSERT(list); - } - { - // empty array - SharedHandle r = json::decode("[ ]"); - const List* list = downcast(r); - CPPUNIT_ASSERT(list); - } - { - // empty string - SharedHandle r = json::decode("[\"\"]"); - const List* list = downcast(r); - CPPUNIT_ASSERT(list); - const String* s = downcast(list->get(0)); - CPPUNIT_ASSERT_EQUAL(std::string(), s->s()); - } - { - // string - SharedHandle r = json::decode("[\"foobar\"]"); - const List* list = downcast(r); - CPPUNIT_ASSERT(list); - const String* s = downcast(list->get(0)); - CPPUNIT_ASSERT_EQUAL(std::string("foobar"), s->s()); - } - { - // string with escape - SharedHandle r = json::decode("[\"\\\\foo\\\"\\\"bar\"]"); - const List* list = downcast(r); - CPPUNIT_ASSERT(list); - const String* s = downcast(list->get(0)); - CPPUNIT_ASSERT_EQUAL(std::string("\\foo\"\"bar"), s->s()); - } - { - // string with escape - SharedHandle r = json::decode("[\"foo\\\"\"]"); - const List* list = downcast(r); - CPPUNIT_ASSERT(list); - const String* s = downcast(list->get(0)); - CPPUNIT_ASSERT_EQUAL(std::string("foo\""), s->s()); - } - { - // string: utf-8 1 to 3 bytes. - SharedHandle r = json::decode("[\"\\u0024\\u00A2\\u20AC\"]"); - const List* list = downcast(r); - CPPUNIT_ASSERT(list); - const String* s = downcast(list->get(0)); - CPPUNIT_ASSERT_EQUAL(std::string("$¢€"), s->s()); - } - { - // string: utf-8 4 bytes - SharedHandle r = json::decode("[\"\\uD852\\uDF62\"]"); - const List* list = downcast(r); - CPPUNIT_ASSERT(list); - const String* s = downcast(list->get(0)); - const unsigned char arr[] = { 0xF0u, 0xA4u, 0xADu, 0xA2u }; - CPPUNIT_ASSERT_EQUAL(std::string(vbegin(arr), vend(arr)), s->s()); - } - { - // null - SharedHandle r = json::decode("[null]"); - const List* list = downcast(r); - CPPUNIT_ASSERT(list); - const Null* s = downcast(list->get(0)); - CPPUNIT_ASSERT(s); - } - { - // true, false - SharedHandle r = json::decode("[true, false]"); - const List* list = downcast(r); - CPPUNIT_ASSERT(list); - const Bool* trueValue = downcast(list->get(0)); - CPPUNIT_ASSERT(trueValue); - CPPUNIT_ASSERT(trueValue->val()); - const Bool* falseValue = downcast(list->get(1)); - CPPUNIT_ASSERT(falseValue); - CPPUNIT_ASSERT(!falseValue->val()); - } - { - // object: 1 member - SharedHandle r = json::decode("{\"foo\":[\"bar\"]}"); - const Dict* dict = downcast(r); - CPPUNIT_ASSERT(dict); - const List* list = downcast(dict->get("foo")); - CPPUNIT_ASSERT(list); - const String* s = downcast(list->get(0)); - CPPUNIT_ASSERT_EQUAL(std::string("bar"), s->s()); - } - { - // object: 2 members - SharedHandle r = json::decode("{\"\":[\"bar\"], " - "\"alpha\" : \"bravo\"}"); - const Dict* dict = downcast(r); - CPPUNIT_ASSERT(dict); - const List* list = downcast(dict->get("")); - CPPUNIT_ASSERT(list); - const String* s = downcast(list->get(0)); - CPPUNIT_ASSERT_EQUAL(std::string("bar"), s->s()); - const String* str = downcast(dict->get("alpha")); - CPPUNIT_ASSERT_EQUAL(std::string("bravo"), str->s()); - } - { - // array: 2 values - SharedHandle r = json::decode("[\"foo\", {}]"); - const List* list = downcast(r); - CPPUNIT_ASSERT(list); - const String* s = downcast(list->get(0)); - CPPUNIT_ASSERT_EQUAL(std::string("foo"), s->s()); - const Dict* dict = downcast(list->get(1)); - CPPUNIT_ASSERT(dict); - } - { - // Number: currently we handle floating point number as string - SharedHandle r = json::decode("[0,-1,1.2,-1.2e-10,-1e10]"); - const List* list = downcast(r); - CPPUNIT_ASSERT(list); - const Integer* i = downcast(list->get(0)); - CPPUNIT_ASSERT_EQUAL((Integer::ValueType)0, i->i()); - const Integer* i1 = downcast(list->get(1)); - CPPUNIT_ASSERT_EQUAL((Integer::ValueType)-1, i1->i()); - const String* s2 = downcast(list->get(2)); - CPPUNIT_ASSERT_EQUAL(std::string("1.2"), s2->s()); - const String* s3 = downcast(list->get(3)); - CPPUNIT_ASSERT_EQUAL(std::string("-1.2e-10"), s3->s()); - const String* s4 = downcast(list->get(4)); - CPPUNIT_ASSERT_EQUAL(std::string("-1e10"), s4->s()); - } - { - // escape chars: ", \, /, \b, \f, \n, \r, \t - SharedHandle r =json::decode("[\"\\\"\\\\\\/\\b\\f\\n\\r\\t\"]"); - const List* list = downcast(r); - const String* s = downcast(list->get(0)); - CPPUNIT_ASSERT_EQUAL(std::string("\"\\/\b\f\n\r\t"), s->s()); - } - { - // string: literal + escaped chars. - SharedHandle r = - json::decode("[\"foo\\u0024b\\u00A2\\u20ACbaz\"]"); - const List* list = downcast(r); - CPPUNIT_ASSERT(list); - const String* s = downcast(list->get(0)); - CPPUNIT_ASSERT_EQUAL(std::string("foo$b¢€baz"), s->s()); - } - -} - -void JsonTest::testDecode_error() -{ - { - try { - // object - SharedHandle r = json::decode("{"); - CPPUNIT_FAIL("exception must be thrown."); - } catch(RecoverableException& e) { - // success - } - } - { - try { - // object - SharedHandle r = json::decode("}"); - CPPUNIT_FAIL("exception must be thrown."); - } catch(RecoverableException& e) { - // success - } - } - { - try { - // object - SharedHandle r = json::decode("{\"\":"); - CPPUNIT_FAIL("exception must be thrown."); - } catch(RecoverableException& e) { - // success - } - } - { - try { - // object - SharedHandle r = json::decode("{\"\":\"\","); - CPPUNIT_FAIL("exception must be thrown."); - } catch(RecoverableException& e) { - // success - } - } - { - try { - // array - SharedHandle r = json::decode("["); - CPPUNIT_FAIL("exception must be thrown."); - } catch(RecoverableException& e) { - // success - } - } - { - try { - // array - SharedHandle r = json::decode("]"); - CPPUNIT_FAIL("exception must be thrown."); - } catch(RecoverableException& e) { - // success - } - } - { - try { - // array - SharedHandle r = json::decode("[\"\""); - CPPUNIT_FAIL("exception must be thrown."); - } catch(RecoverableException& e) { - // success - } - } - { - try { - // array - SharedHandle r = json::decode("[\"\","); - CPPUNIT_FAIL("exception must be thrown."); - } catch(RecoverableException& e) { - // success - } - } - { - try { - // string - SharedHandle r = json::decode("[\"foo]"); - CPPUNIT_FAIL("exception must be thrown."); - } catch(RecoverableException& e) { - // success - } - } - { - try { - // string - SharedHandle r = json::decode("[\"\\u\"]"); - CPPUNIT_FAIL("exception must be thrown."); - } catch(RecoverableException& e) { - // success - } - } - { - try { - // string - SharedHandle r = json::decode("[\"\\u"); - CPPUNIT_FAIL("exception must be thrown."); - } catch(RecoverableException& e) { - // success - } - } - { - try { - // string - SharedHandle r = json::decode("[\"\\u000\"]"); - CPPUNIT_FAIL("exception must be thrown."); - } catch(RecoverableException& e) { - // success - } - } - { - try { - // string - SharedHandle r = json::decode("[\"\\u000"); - CPPUNIT_FAIL("exception must be thrown."); - } catch(RecoverableException& e) { - // success - } - } - { - try { - // string - SharedHandle r = json::decode("[\"\\uD852foo\"]"); - CPPUNIT_FAIL("exception must be thrown."); - } catch(RecoverableException& e) { - // success - } - } - { - try { - // string - SharedHandle r = json::decode("[\"\\uD852"); - CPPUNIT_FAIL("exception must be thrown."); - } catch(RecoverableException& e) { - // success - } - } - { - try { - // string - SharedHandle r = json::decode("[\"\\uD852\\u\"]"); - CPPUNIT_FAIL("exception must be thrown."); - } catch(RecoverableException& e) { - // success - } - } - { - try { - // string - SharedHandle r = json::decode("[\"\\uD852\\u"); - CPPUNIT_FAIL("exception must be thrown."); - } catch(RecoverableException& e) { - // success - } - } - { - try { - // string - SharedHandle r = json::decode("[\"\\uD852\\u0000\"]"); - CPPUNIT_FAIL("exception must be thrown."); - } catch(RecoverableException& e) { - // success - } - } - { - try { - // string - SharedHandle r = json::decode("[\"\\uD852\\uDF62"); - CPPUNIT_FAIL("exception must be thrown."); - } catch(RecoverableException& e) { - // success - } - } - { - try { - // object - SharedHandle r = json::decode("{:\"\"}"); - CPPUNIT_FAIL("exception must be thrown."); - } catch(RecoverableException& e) { - // success - } - } - { - try { - // object - SharedHandle r = json::decode("{\"foo\":}"); - CPPUNIT_FAIL("exception must be thrown."); - } catch(RecoverableException& e) { - // success - } - } - { - try { - // number - SharedHandle r = json::decode("{00}"); - CPPUNIT_FAIL("exception must be thrown."); - } catch(RecoverableException& e) { - // success - } - } - { - try { - // number - SharedHandle r = json::decode("{1.}"); - CPPUNIT_FAIL("exception must be thrown."); - } catch(RecoverableException& e) { - // success - } - } - { - try { - // number - SharedHandle r = json::decode("{1.1e}"); - CPPUNIT_FAIL("exception must be thrown."); - } catch(RecoverableException& e) { - // success - } - } - { - try { - // bool - SharedHandle r = json::decode("{t"); - CPPUNIT_FAIL("exception must be thrown."); - } catch(RecoverableException& e) { - // success - } - } -} - void JsonTest::testEncode() { {