From 7f4fcc51f624d56b9729cf88bb4e7d6376419e2f Mon Sep 17 00:00:00 2001 From: Raphael Isemann Date: Fri, 9 Jan 2015 20:03:18 +0100 Subject: [PATCH 1/2] Implemented the JSON spec for string parsing for everything but the \uXXXX escaping for unicode --- src/json.cc | 52 +++++++++++++++++++++++++++++++++++------------ test/json_unit.cc | 33 ++++++++++++++++++++++++++++-- 2 files changed, 70 insertions(+), 15 deletions(-) diff --git a/src/json.cc b/src/json.cc index 1a18a9e4..1666b23c 100644 --- a/src/json.cc +++ b/src/json.cc @@ -2042,32 +2042,58 @@ Parses a string after opening quotes (\p ") where read. */ std::string json::parser::parseString() { - // remember the position where the first character of the string was - const auto startPos = pos_; // true if and only if the amount of backslashes before the current // character is even bool evenAmountOfBackslashes = true; + // the result of the parse process + std::string result; + // iterate with pos_ over the whole string - for (; pos_ < buffer_.size(); pos_++) - { + for (; pos_ < buffer_.size(); pos_++) { char currentChar = buffer_[pos_]; - // currentChar is a quote, so we might have found the end of the string - if (currentChar == '"') - { - // but only if the amount of backslashes before that quote is even - if (evenAmountOfBackslashes) - { + // uneven amount of backslashes means the user wants to escape something + if (!evenAmountOfBackslashes) { + + // slash, backslash and quote are copied as is + if ( currentChar == '/' + || currentChar == '\\' + || currentChar == '"') { + result += currentChar; + } else { + // All other characters are replaced by their respective special character + if (currentChar == 't') { + result += '\t'; + } else if (currentChar == 'b') { + result += '\b'; + } else if (currentChar == 'f') { + result += '\f'; + } else if (currentChar == 'n') { + result += '\n'; + } else if (currentChar == 'r') { + result += '\r'; + } else { + error("expected one of \\,/,b,f,n,r,t behind backslash."); + } + // TODO implement \uXXXX + } + } else { + if (currentChar == '"') { + // currentChar is a quote, so we found the end of the string + - const auto stringLength = pos_ - startPos; // set pos_ behind the trailing quote pos_++; // find next char to parse next(); - // return string inside the quotes - return buffer_.substr(startPos, stringLength); + // bring the result of the parsing process back to the caller + return result; + } else if (currentChar != '\\') { + // all non-backslash characters are added to the end of the result string. + // the only backslashes we want in the result are the ones that are escaped (which happens above). + result += currentChar; } } diff --git a/test/json_unit.cc b/test/json_unit.cc index 7856bdd1..3835e198 100644 --- a/test/json_unit.cc +++ b/test/json_unit.cc @@ -1618,10 +1618,39 @@ TEST_CASE("Parser") CHECK(json::parse("\"\"") == json("")); CHECK(json::parse("\"foo\"") == json("foo")); - // escape characters + // escaping quotes CHECK_THROWS_AS(json::parse("\"\\\""), std::invalid_argument); CHECK_NOTHROW(json::parse("\"\\\"\"")); - CHECK_NOTHROW(json::parse("\"\\\\\"")); + + // escaping backslashes + CHECK(json::parse("\"a\\\\z\"") == json("a\\z")); + CHECK(json::parse("\"\\\\\"") == json("\\")); + CHECK(json::parse("\"\\\\a\\\\\"") == json("\\a\\")); + CHECK(json::parse("\"\\\\\\\\\"") == json("\\\\")); + + // escaping slash + CHECK(json::parse("\"a\\/z\"") == json("a/z")); + CHECK(json::parse("\"\\/\"") == json("/")); + + // escaping tabs + CHECK(json::parse("\"a\\tz\"") == json("a\tz")); + CHECK(json::parse("\"\\t\"") == json("\t")); + + // escaping formfeed + CHECK(json::parse("\"a\\fz\"") == json("a\fz")); + CHECK(json::parse("\"\\f\"") == json("\f")); + + // escaping carriage return + CHECK(json::parse("\"a\\rz\"") == json("a\rz")); + CHECK(json::parse("\"\\r\"") == json("\r")); + + // escaping backspace + CHECK(json::parse("\"a\\bz\"") == json("a\bz")); + CHECK(json::parse("\"\\b\"") == json("\b")); + + // escaping newline + CHECK(json::parse("\"a\\nz\"") == json("a\nz")); + CHECK(json::parse("\"\\n\"") == json("\n")); // quotes must be closed CHECK_THROWS_AS(json::parse("\""), std::invalid_argument); From 13efc7a02ab0cc23a28528510ba692b65eac18dc Mon Sep 17 00:00:00 2001 From: Raphael Isemann Date: Fri, 9 Jan 2015 20:24:58 +0100 Subject: [PATCH 2/2] More tests to get line coverage backt o 100% --- test/json_unit.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/json_unit.cc b/test/json_unit.cc index 3835e198..b2fcd65e 100644 --- a/test/json_unit.cc +++ b/test/json_unit.cc @@ -1652,6 +1652,11 @@ TEST_CASE("Parser") CHECK(json::parse("\"a\\nz\"") == json("a\nz")); CHECK(json::parse("\"\\n\"") == json("\n")); + // escaping senseless stuff + CHECK_THROWS_AS(json::parse("\"\\z\""), std::invalid_argument); + CHECK_THROWS_AS(json::parse("\"\\ \""), std::invalid_argument); + CHECK_THROWS_AS(json::parse("\"\\9\""), std::invalid_argument); + // quotes must be closed CHECK_THROWS_AS(json::parse("\""), std::invalid_argument); }