Skip to content

Commit 20f5caa

Browse files
StilesCrisisStilesCrisis
authored andcommitted
Token-by-token pull parsing
Refactored the iterative parser so that users can parse a single JSON element at a time (invoking the handler one time) and then return control to the calling code. Call IterativeParseInit to start, and then call IterativeParseNext to retrieve one JSON element. Use IterativeParseComplete to check for JSON document completion.
1 parent 738864c commit 20f5caa

File tree

1 file changed

+83
-28
lines changed

1 file changed

+83
-28
lines changed

include/rapidjson/reader.h

Lines changed: 83 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,68 @@ class GenericReader {
513513
return Parse<kParseDefaultFlags>(is, handler);
514514
}
515515

516+
//! Initialize JSON text token-by-token parsing
517+
/*!
518+
*/
519+
void IterativeParseInit() {
520+
parseResult_.Clear();
521+
state_ = IterativeParsingStartState;
522+
}
523+
524+
//! Parse one token from JSON text
525+
/*! \tparam InputStream Type of input stream, implementing Stream concept
526+
\tparam Handler Type of handler, implementing Handler concept.
527+
\param is Input stream to be parsed.
528+
\param handler The handler to receive events.
529+
\return Whether the parsing is successful.
530+
*/
531+
template <unsigned parseFlags, typename InputStream, typename Handler>
532+
ParseResult IterativeParseNext(InputStream& is, Handler& handler) {
533+
while (is.Peek() != '\0') {
534+
RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_);
535+
SkipWhitespaceAndComments<parseFlags>(is);
536+
537+
Token t = Tokenize(is.Peek());
538+
IterativeParsingState n = Predict(state_, t);
539+
IterativeParsingState d = Transit<parseFlags>(state_, t, n, is, handler);
540+
541+
if (d == IterativeParsingErrorState) {
542+
HandleError(state_, is);
543+
return parseResult_;
544+
}
545+
546+
state_ = d;
547+
548+
// Do not further consume streams if a root JSON has been parsed.
549+
if (state_ == IterativeParsingFinishState) {
550+
// If StopWhenDone is not set, and stray data is found post-root, flag an error.
551+
if (!(parseFlags & kParseStopWhenDoneFlag)) {
552+
SkipWhitespaceAndComments<parseFlags>(is);
553+
if (is.Peek() != '\0')
554+
HandleError(state_, is);
555+
}
556+
return parseResult_;
557+
}
558+
559+
if (!IsIterativeParsingDelimiterState(n))
560+
return parseResult_;
561+
}
562+
563+
// Handle the end of file.
564+
if (state_ != IterativeParsingFinishState)
565+
HandleError(state_, is);
566+
567+
stack_.Clear();
568+
return parseResult_;
569+
}
570+
571+
//! Check if token-by-token parsing JSON text is complete
572+
/*! \return Whether the JSON has been fully decoded.
573+
*/
574+
bool IterativeParseComplete() {
575+
return IsIterativeParsingCompleteState(state_);
576+
}
577+
516578
//! Whether a parse error has occured in the last parsing.
517579
bool HasParseError() const { return parseResult_.IsError(); }
518580

@@ -1803,44 +1865,37 @@ class GenericReader {
18031865
}
18041866
}
18051867

1868+
RAPIDJSON_FORCEINLINE bool IsIterativeParsingDelimiterState(IterativeParsingState s) {
1869+
const unsigned int delimiterStateMask =
1870+
(1 << IterativeParsingKeyValueDelimiterState) |
1871+
(1 << IterativeParsingMemberDelimiterState) |
1872+
(1 << IterativeParsingElementDelimiterState);
1873+
1874+
return (1 << s) & delimiterStateMask;
1875+
}
1876+
1877+
RAPIDJSON_FORCEINLINE bool IsIterativeParsingCompleteState(IterativeParsingState s) {
1878+
const unsigned int completeStateMask =
1879+
(1 << IterativeParsingFinishState) |
1880+
(1 << IterativeParsingErrorState);
1881+
1882+
return (1 << s) & completeStateMask;
1883+
}
1884+
18061885
template <unsigned parseFlags, typename InputStream, typename Handler>
18071886
ParseResult IterativeParse(InputStream& is, Handler& handler) {
1808-
parseResult_.Clear();
1809-
ClearStackOnExit scope(*this);
1810-
IterativeParsingState state = IterativeParsingStartState;
1811-
1812-
SkipWhitespaceAndComments<parseFlags>(is);
1813-
RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_);
1814-
while (is.Peek() != '\0') {
1815-
Token t = Tokenize(is.Peek());
1816-
IterativeParsingState n = Predict(state, t);
1817-
IterativeParsingState d = Transit<parseFlags>(state, t, n, is, handler);
1818-
1819-
if (d == IterativeParsingErrorState) {
1820-
HandleError(state, is);
1887+
IterativeParseInit();
1888+
while (!IterativeParseComplete()) {
1889+
if (!IterativeParseNext<parseFlags>(is, handler))
18211890
break;
1822-
}
1823-
1824-
state = d;
1825-
1826-
// Do not further consume streams if a root JSON has been parsed.
1827-
if ((parseFlags & kParseStopWhenDoneFlag) && state == IterativeParsingFinishState)
1828-
break;
1829-
1830-
SkipWhitespaceAndComments<parseFlags>(is);
1831-
RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_);
18321891
}
1833-
1834-
// Handle the end of file.
1835-
if (state != IterativeParsingFinishState)
1836-
HandleError(state, is);
1837-
18381892
return parseResult_;
18391893
}
18401894

18411895
static const size_t kDefaultStackCapacity = 256; //!< Default stack capacity in bytes for storing a single decoded string.
18421896
internal::Stack<StackAllocator> stack_; //!< A stack for storing decoded string temporarily during non-destructive parsing.
18431897
ParseResult parseResult_;
1898+
IterativeParsingState state_;
18441899
}; // class GenericReader
18451900

18461901
//! Reader with UTF8 encoding and default allocator.

0 commit comments

Comments
 (0)