Commit c8138641 authored by Milo Yip's avatar Milo Yip

Add kParseStopWhenDoneFlag, its implementation and related unit tests

parent c4ce48cd
...@@ -65,7 +65,8 @@ enum ParseFlag { ...@@ -65,7 +65,8 @@ enum ParseFlag {
kParseDefaultFlags = 0, //!< Default parse flags. Non-destructive parsing. Text strings are decoded into allocated buffer. kParseDefaultFlags = 0, //!< Default parse flags. Non-destructive parsing. Text strings are decoded into allocated buffer.
kParseInsituFlag = 1, //!< In-situ(destructive) parsing. kParseInsituFlag = 1, //!< In-situ(destructive) parsing.
kParseValidateEncodingFlag = 2, //!< Validate encoding of JSON strings. kParseValidateEncodingFlag = 2, //!< Validate encoding of JSON strings.
kParseIterativeFlag = 4 //!< Iterative(constant complexity in terms of function call stack size) parsing. kParseIterativeFlag = 4, //!< Iterative(constant complexity in terms of function call stack size) parsing.
kParseStopWhenDoneFlag = 8 //!< After parsing a complete JSON root from stream, stop further processing the rest of stream. When this flag is used, parser will not generate kParseErrorDocumentRootNotSingular error.
}; };
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
...@@ -273,11 +274,10 @@ public: ...@@ -273,11 +274,10 @@ public:
typedef typename SourceEncoding::Ch Ch; //!< SourceEncoding character type typedef typename SourceEncoding::Ch Ch; //!< SourceEncoding character type
//! Constructor. //! Constructor.
/*! \param limit Parsing stack size limit(in bytes). Pass 0 means no limit. /*! \param allocator Optional allocator for allocating stack memory. (Only use for non-destructive parsing)
\param allocator Optional allocator for allocating stack memory. (Only use for non-destructive parsing)
\param stackCapacity stack capacity in bytes for storing a single decoded string. (Only use for non-destructive parsing) \param stackCapacity stack capacity in bytes for storing a single decoded string. (Only use for non-destructive parsing)
*/ */
GenericReader(size_t limit = 0, Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity) : stack_(allocator, stackCapacity), kStackSizeLimit_(limit), parseResult_() {} GenericReader(Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity) : stack_(allocator, stackCapacity), parseResult_() {}
//! Parse JSON text. //! Parse JSON text.
/*! \tparam parseFlags Combination of \ref ParseFlag. /*! \tparam parseFlags Combination of \ref ParseFlag.
...@@ -310,11 +310,13 @@ public: ...@@ -310,11 +310,13 @@ public:
} }
RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_);
SkipWhitespace(is); if (!(parseFlags & kParseStopWhenDoneFlag)) {
SkipWhitespace(is);
if (is.Peek() != '\0') { if (is.Peek() != '\0') {
RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentRootNotSingular, is.Tell()); RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentRootNotSingular, is.Tell());
RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_);
}
} }
} }
...@@ -513,7 +515,7 @@ private: ...@@ -513,7 +515,7 @@ private:
typedef typename TargetEncoding::Ch Ch; typedef typename TargetEncoding::Ch Ch;
StackStream(internal::Stack<Allocator>& stack) : stack_(stack), length_(0) {} StackStream(internal::Stack<Allocator>& stack) : stack_(stack), length_(0) {}
void Put(Ch c) { RAPIDJSON_FORCEINLINE void Put(Ch c) {
*stack_.template Push<Ch>() = c; *stack_.template Push<Ch>() = c;
++length_; ++length_;
} }
...@@ -572,11 +574,6 @@ private: ...@@ -572,11 +574,6 @@ private:
is.Take(); is.Take();
Ch e = is.Take(); Ch e = is.Take();
if ((sizeof(Ch) == 1 || unsigned(e) < 256) && escape[(unsigned char)e]) { if ((sizeof(Ch) == 1 || unsigned(e) < 256) && escape[(unsigned char)e]) {
if (!(parseFlags & kParseInsituFlag)) {
if (!CheckStackSpaceQuota(sizeof(Ch))) {
RAPIDJSON_PARSE_ERROR(kParseErrorStackSizeLimitExceeded, is.Tell() - 1);
}
}
os.Put(escape[(unsigned char)e]); os.Put(escape[(unsigned char)e]);
} }
else if (e == 'u') { // Unicode else if (e == 'u') { // Unicode
...@@ -597,11 +594,6 @@ private: ...@@ -597,11 +594,6 @@ private:
} }
else if (c == '"') { // Closing double quote else if (c == '"') { // Closing double quote
is.Take(); is.Take();
if (!(parseFlags & kParseInsituFlag)) {
if (!CheckStackSpaceQuota(sizeof(Ch))) {
RAPIDJSON_PARSE_ERROR(kParseErrorStackSizeLimitExceeded, is.Tell() - 1);
}
}
os.Put('\0'); // null-terminate the string os.Put('\0'); // null-terminate the string
return; return;
} }
...@@ -829,44 +821,52 @@ private: ...@@ -829,44 +821,52 @@ private:
}; };
// Tokens // Tokens
enum IterativeParsingToken { enum Token {
IterativeParsingLeftBracketToken = 0, LeftBracketToken = 0,
IterativeParsingRightBracketToken, RightBracketToken,
IterativeParsingLeftCurlyBracketToken, LeftCurlyBracketToken,
IterativeParsingRightCurlyBracketToken, RightCurlyBracketToken,
IterativeParsingCommaToken, CommaToken,
IterativeParsingColonToken, ColonToken,
IterativeParsingStringToken, StringToken,
IterativeParsingFalseToken, FalseToken,
IterativeParsingTrueToken, TrueToken,
IterativeParsingNullToken, NullToken,
IterativeParsingNumberToken, NumberToken,
cIterativeParsingTokenCount kTokenCount
}; };
IterativeParsingToken Tokenize(Ch c) { RAPIDJSON_FORCEINLINE Token Tokenize(Ch c) {
switch (c) { #define N NumberToken
case '[': return IterativeParsingLeftBracketToken; #define N16 N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N
case ']': return IterativeParsingRightBracketToken; // Maps from ASCII to Token
case '{': return IterativeParsingLeftCurlyBracketToken; static const unsigned char tokenMap[256] = {
case '}': return IterativeParsingRightCurlyBracketToken; N16, // 00~0F
case ',': return IterativeParsingCommaToken; N16, // 10~1F
case ':': return IterativeParsingColonToken; N, N, StringToken, N, N, N, N, N, N, N, N, N, CommaToken, N, N, N, // 20~2F
case '"': return IterativeParsingStringToken; N, N, N, N, N, N, N, N, N, N, ColonToken, N, N, N, N, N, // 30~3F
case 'f': return IterativeParsingFalseToken; N16, // 40~4F
case 't': return IterativeParsingTrueToken; N, N, N, N, N, N, N, N, N, N, N, LeftBracketToken, N, RightBracketToken, N, N, // 50~5F
case 'n': return IterativeParsingNullToken; N, N, N, N, N, N, FalseToken, N, N, N, N, N, N, N, NullToken, N, // 60~6F
default: return IterativeParsingNumberToken; N, N, N, N, TrueToken, N, N, N, N, N, N, LeftCurlyBracketToken, N, RightCurlyBracketToken, N, N, // 70~7F
} N16, N16, N16, N16, N16, N16, N16, N16 // 80~FF
};
#undef N
#undef N16
if (sizeof(Ch) == 1 || unsigned(c) < 256)
return (Token)tokenMap[(unsigned char)c];
else
return NumberToken;
} }
IterativeParsingState Predict(IterativeParsingState state, IterativeParsingToken token) { RAPIDJSON_FORCEINLINE IterativeParsingState Predict(IterativeParsingState state, Token token) {
// current state x one lookahead token -> new state // current state x one lookahead token -> new state
static const char G[cIterativeParsingStateCount][cIterativeParsingTokenCount] = { static const char G[cIterativeParsingStateCount][kTokenCount] = {
// Start // Start
{ {
IterativeParsingArrayInitialState, // Left bracket IterativeParsingArrayInitialState, // Left bracket
...@@ -1025,11 +1025,7 @@ private: ...@@ -1025,11 +1025,7 @@ private:
// Make an advance in the token stream and state based on the candidate destination state which was returned by Transit(). // Make an advance in the token stream and state based on the candidate destination state which was returned by Transit().
// May return a new state on state pop. // May return a new state on state pop.
template <unsigned parseFlags, typename InputStream, typename Handler> template <unsigned parseFlags, typename InputStream, typename Handler>
IterativeParsingState Transit(IterativeParsingState src, IterativeParsingToken token, IterativeParsingState dst, InputStream& is, Handler& handler) { RAPIDJSON_FORCEINLINE IterativeParsingState Transit(IterativeParsingState src, Token token, IterativeParsingState dst, InputStream& is, Handler& handler) {
int c = 0;
IterativeParsingState n;
bool hr;
switch (dst) { switch (dst) {
case IterativeParsingStartState: case IterativeParsingStartState:
RAPIDJSON_ASSERT(false); RAPIDJSON_ASSERT(false);
...@@ -1043,27 +1039,20 @@ private: ...@@ -1043,27 +1039,20 @@ private:
case IterativeParsingObjectInitialState: case IterativeParsingObjectInitialState:
case IterativeParsingArrayInitialState: case IterativeParsingArrayInitialState:
{
// Push the state(Element or MemeberValue) if we are nested in another array or value of member. // Push the state(Element or MemeberValue) if we are nested in another array or value of member.
// In this way we can get the correct state on ObjectFinish or ArrayFinish by frame pop. // In this way we can get the correct state on ObjectFinish or ArrayFinish by frame pop.
n = src; IterativeParsingState n = src;
if (src == IterativeParsingArrayInitialState || src == IterativeParsingElementDelimiterState) if (src == IterativeParsingArrayInitialState || src == IterativeParsingElementDelimiterState)
n = IterativeParsingElementState; n = IterativeParsingElementState;
else if (src == IterativeParsingKeyValueDelimiterState) else if (src == IterativeParsingKeyValueDelimiterState)
n = IterativeParsingMemberValueState; n = IterativeParsingMemberValueState;
// Check stack space limit.
if (!CheckStackSpaceQuota(sizeof(IterativeParsingState) + sizeof(int))) {
RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorStackSizeLimitExceeded, is.Tell());
return IterativeParsingErrorState;
}
// Push current state. // Push current state.
*stack_.template Push<IterativeParsingState>(1) = n; *stack_.template Push<SizeType>(1) = n;
// Initialize and push the member/element count. // Initialize and push the member/element count.
*stack_.template Push<int>(1) = 0; *stack_.template Push<SizeType>(1) = 0;
// Call handler // Call handler
if (dst == IterativeParsingObjectInitialState) bool hr = (dst == IterativeParsingObjectInitialState) ? handler.StartObject() : handler.StartArray();
hr = handler.StartObject();
else
hr = handler.StartArray();
// On handler short circuits the parsing. // On handler short circuits the parsing.
if (!hr) { if (!hr) {
RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell());
...@@ -1073,6 +1062,7 @@ private: ...@@ -1073,6 +1062,7 @@ private:
is.Take(); is.Take();
return dst; return dst;
} }
}
case IterativeParsingMemberKeyState: case IterativeParsingMemberKeyState:
ParseString<parseFlags>(is, handler); ParseString<parseFlags>(is, handler);
...@@ -1082,7 +1072,7 @@ private: ...@@ -1082,7 +1072,7 @@ private:
return dst; return dst;
case IterativeParsingKeyValueDelimiterState: case IterativeParsingKeyValueDelimiterState:
if (token == IterativeParsingColonToken) { if (token == ColonToken) {
is.Take(); is.Take();
return dst; return dst;
} }
...@@ -1109,22 +1099,23 @@ private: ...@@ -1109,22 +1099,23 @@ private:
case IterativeParsingElementDelimiterState: case IterativeParsingElementDelimiterState:
is.Take(); is.Take();
// Update member/element count. // Update member/element count.
*stack_.template Top<int>() = *stack_.template Top<int>() + 1; *stack_.template Top<SizeType>() = *stack_.template Top<SizeType>() + 1;
return dst; return dst;
case IterativeParsingObjectFinishState: case IterativeParsingObjectFinishState:
{
// Get member count. // Get member count.
c = *stack_.template Pop<int>(1); SizeType c = *stack_.template Pop<SizeType>(1);
// If the object is not empty, count the last member. // If the object is not empty, count the last member.
if (src == IterativeParsingMemberValueState) if (src == IterativeParsingMemberValueState)
++c; ++c;
// Restore the state. // Restore the state.
n = *stack_.template Pop<IterativeParsingState>(1); IterativeParsingState n = static_cast<IterativeParsingState>(*stack_.template Pop<SizeType>(1));
// Transit to Finish state if this is the topmost scope. // Transit to Finish state if this is the topmost scope.
if (n == IterativeParsingStartState) if (n == IterativeParsingStartState)
n = IterativeParsingFinishState; n = IterativeParsingFinishState;
// Call handler // Call handler
hr = handler.EndObject(c); bool hr = handler.EndObject(c);
// On handler short circuits the parsing. // On handler short circuits the parsing.
if (!hr) { if (!hr) {
RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell());
...@@ -1134,20 +1125,22 @@ private: ...@@ -1134,20 +1125,22 @@ private:
is.Take(); is.Take();
return n; return n;
} }
}
case IterativeParsingArrayFinishState: case IterativeParsingArrayFinishState:
{
// Get element count. // Get element count.
c = *stack_.template Pop<int>(1); SizeType c = *stack_.template Pop<SizeType>(1);
// If the array is not empty, count the last element. // If the array is not empty, count the last element.
if (src == IterativeParsingElementState) if (src == IterativeParsingElementState)
++c; ++c;
// Restore the state. // Restore the state.
n = *stack_.template Pop<IterativeParsingState>(1); IterativeParsingState n = static_cast<IterativeParsingState>(*stack_.template Pop<SizeType>(1));
// Transit to Finish state if this is the topmost scope. // Transit to Finish state if this is the topmost scope.
if (n == IterativeParsingStartState) if (n == IterativeParsingStartState)
n = IterativeParsingFinishState; n = IterativeParsingFinishState;
// Call handler // Call handler
hr = handler.EndArray(c); bool hr = handler.EndArray(c);
// On handler short circuits the parsing. // On handler short circuits the parsing.
if (!hr) { if (!hr) {
RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell());
...@@ -1157,6 +1150,7 @@ private: ...@@ -1157,6 +1150,7 @@ private:
is.Take(); is.Take();
return n; return n;
} }
}
default: default:
RAPIDJSON_ASSERT(false); RAPIDJSON_ASSERT(false);
...@@ -1171,29 +1165,16 @@ private: ...@@ -1171,29 +1165,16 @@ private:
return; return;
} }
if (src == IterativeParsingStartState && is.Peek() == '\0') switch (src) {
RAPIDJSON_PARSE_ERROR(kParseErrorDocumentEmpty, is.Tell()); case IterativeParsingStartState: RAPIDJSON_PARSE_ERROR(is.Peek() == '\0' ? kParseErrorDocumentEmpty : kParseErrorDocumentRootNotObjectOrArray, is.Tell());
case IterativeParsingFinishState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentRootNotSingular, is.Tell());
else if (src == IterativeParsingStartState) case IterativeParsingObjectInitialState:
RAPIDJSON_PARSE_ERROR(kParseErrorDocumentRootNotObjectOrArray, is.Tell()); case IterativeParsingMemberDelimiterState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell());
case IterativeParsingMemberKeyState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell());
else if (src == IterativeParsingFinishState) case IterativeParsingMemberValueState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell());
RAPIDJSON_PARSE_ERROR(kParseErrorDocumentRootNotSingular, is.Tell()); case IterativeParsingElementState: RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell());
default: RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell());
else if (src == IterativeParsingObjectInitialState || src == IterativeParsingMemberDelimiterState) }
RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell());
else if (src == IterativeParsingMemberKeyState)
RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell());
else if (src == IterativeParsingMemberValueState)
RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell());
else if (src == IterativeParsingElementState)
RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell());
else
RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell());
} }
template <unsigned parseFlags, typename InputStream, typename Handler> template <unsigned parseFlags, typename InputStream, typename Handler>
...@@ -1204,7 +1185,7 @@ private: ...@@ -1204,7 +1185,7 @@ private:
SkipWhitespace(is); SkipWhitespace(is);
while (is.Peek() != '\0') { while (is.Peek() != '\0') {
IterativeParsingToken t = Tokenize(is.Peek()); Token t = Tokenize(is.Peek());
IterativeParsingState n = Predict(state, t); IterativeParsingState n = Predict(state, t);
IterativeParsingState d = Transit<parseFlags>(state, t, n, is, handler); IterativeParsingState d = Transit<parseFlags>(state, t, n, is, handler);
...@@ -1214,6 +1195,11 @@ private: ...@@ -1214,6 +1195,11 @@ private:
} }
state = d; state = d;
// Do not further consume streams if a root JSON has been parsed.
if ((parseFlags & kParseStopWhenDoneFlag) && state == IterativeParsingFinishState)
break;
SkipWhitespace(is); SkipWhitespace(is);
} }
...@@ -1224,13 +1210,8 @@ private: ...@@ -1224,13 +1210,8 @@ private:
return parseResult_; return parseResult_;
} }
bool CheckStackSpaceQuota(size_t size) const {
return kStackSizeLimit_ == 0 || (stack_.GetSize() + size <= kStackSizeLimit_);
}
static const size_t kDefaultStackCapacity = 256; //!< Default stack capacity in bytes for storing a single decoded string. static const size_t kDefaultStackCapacity = 256; //!< Default stack capacity in bytes for storing a single decoded string.
internal::Stack<Allocator> stack_; //!< A stack for storing decoded string temporarily during non-destructive parsing. internal::Stack<Allocator> stack_; //!< A stack for storing decoded string temporarily during non-destructive parsing.
const size_t kStackSizeLimit_; //!< Stack size limit(in bytes). A value of 0 means no limit.
ParseResult parseResult_; ParseResult parseResult_;
}; // class GenericReader }; // class GenericReader
......
...@@ -545,6 +545,38 @@ TEST(Reader, Parse_EmptyObject) { ...@@ -545,6 +545,38 @@ TEST(Reader, Parse_EmptyObject) {
EXPECT_EQ(2u, h.step_); EXPECT_EQ(2u, h.step_);
} }
struct ParseMultipleRootHandler : BaseReaderHandler<> {
ParseMultipleRootHandler() : step_(0) {}
bool Default() { ADD_FAILURE(); return false; }
bool StartObject() { EXPECT_EQ(0u, step_); step_++; return true; }
bool EndObject(SizeType) { EXPECT_EQ(1u, step_); step_++; return true; }
bool StartArray() { EXPECT_EQ(2u, step_); step_++; return true; }
bool EndArray(SizeType) { EXPECT_EQ(3u, step_); step_++; return true; }
unsigned step_;
};
template <unsigned parseFlags>
void TestMultipleRoot() {
StringStream s("{}[]a");
ParseMultipleRootHandler h;
Reader reader;
EXPECT_TRUE(reader.Parse<parseFlags>(s, h));
EXPECT_EQ(2u, h.step_);
EXPECT_TRUE(reader.Parse<parseFlags>(s, h));
EXPECT_EQ(4u, h.step_);
EXPECT_EQ('a', s.Peek());
}
TEST(Reader, Parse_MultipleRoot) {
TestMultipleRoot<kParseStopWhenDoneFlag>();
}
TEST(Reader, ParseIterative_MultipleRoot) {
TestMultipleRoot<kParseIterativeFlag | kParseStopWhenDoneFlag>();
}
#define TEST_ERROR(errorCode, str) \ #define TEST_ERROR(errorCode, str) \
{ \ { \
char buffer[1001]; \ char buffer[1001]; \
...@@ -932,18 +964,6 @@ TEST(Reader, IterativeParsing_ShortCircuit) { ...@@ -932,18 +964,6 @@ TEST(Reader, IterativeParsing_ShortCircuit) {
} }
} }
TEST(Reader, IterativeParsing_LimitStackSize) {
BaseReaderHandler<> handler;
Reader reader(20);
StringStream is("[[[]]]");
ParseResult r = reader.Parse<kParseIterativeFlag>(is, handler);
EXPECT_TRUE(reader.HasParseError());
EXPECT_EQ(kParseErrorStackSizeLimitExceeded, r.Code());
EXPECT_EQ(2u, r.Offset());
}
#ifdef __GNUC__ #ifdef __GNUC__
RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_POP
#endif #endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment