From bcf7cee788866dd9cc72fa883af39c62fe2876be Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Sun, 29 Jun 2014 20:59:01 +0800 Subject: [PATCH] Add stream copying optimization switch depending stream type. An unit test is added --- include/rapidjson/filereadstream.h | 5 +++ include/rapidjson/rapidjson.h | 28 +++++++++++++++++ include/rapidjson/reader.h | 8 ++--- test/unittest/readertest.cpp | 50 +++++++++++++++++++++++++++++- 4 files changed, 86 insertions(+), 5 deletions(-) diff --git a/include/rapidjson/filereadstream.h b/include/rapidjson/filereadstream.h index 4d42d38..29ab393 100644 --- a/include/rapidjson/filereadstream.h +++ b/include/rapidjson/filereadstream.h @@ -69,6 +69,11 @@ private: bool eof_; }; +template<> +struct StreamTraits { + typedef FileReadStream StreamCopyType; // Enable stream copy optimization. +}; + } // namespace rapidjson #endif // RAPIDJSON_FILESTREAM_H_ diff --git a/include/rapidjson/rapidjson.h b/include/rapidjson/rapidjson.h index fd31aef..be3d7fa 100644 --- a/include/rapidjson/rapidjson.h +++ b/include/rapidjson/rapidjson.h @@ -193,6 +193,24 @@ concept Stream { \endcode */ +//! Provides additional information for stream. +/*! + By using traits pattern, this type provides a default configuration for stream. + For custom stream, this type can be specialized for other configuration. + See TEST(Reader, CustomStringStream) in readertest.cpp for example. +*/ +template +struct StreamTraits { + //! Whether to make local copy of stream for optimization during parsing. + /*! + If it is defined as Stream&, it will not make a local copy. + If it is defined as Stream, it will make a local copy for optimization. + By default, for safety, streams do not use local copy optimization, i.e. it is defined as Stream&. + Stream that can be copied fast should specialize this, like StreamTraits. + */ + typedef Stream& StreamCopyType; +}; + //! Put N copies of a character to a stream. template inline void PutN(Stream& stream, Ch c, size_t n) { @@ -225,6 +243,11 @@ struct GenericStringStream { const Ch* head_; //!< Original head of the string. }; +template +struct StreamTraits> { + typedef GenericStringStream StreamCopyType; // Enable stream copy optimization. +}; + typedef GenericStringStream > StringStream; /////////////////////////////////////////////////////////////////////////////// @@ -256,6 +279,11 @@ struct GenericInsituStringStream { Ch* head_; }; +template +struct StreamTraits> { + typedef GenericInsituStringStream StreamCopyType; // Enable stream copy optimization. +}; + typedef GenericInsituStringStream > InsituStringStream; /////////////////////////////////////////////////////////////////////////////// diff --git a/include/rapidjson/reader.h b/include/rapidjson/reader.h index 4028c19..1df5cf8 100644 --- a/include/rapidjson/reader.h +++ b/include/rapidjson/reader.h @@ -109,7 +109,7 @@ struct BaseReaderHandler { */ template void SkipWhitespace(InputStream& is) { - InputStream s = is; // Use a local copy for optimization + StreamTraits::StreamCopyType s = is; // Use a local copy for optimization while (s.Peek() == ' ' || s.Peek() == '\n' || s.Peek() == '\r' || s.Peek() == '\t') s.Take(); is = s; @@ -378,7 +378,7 @@ private: // Helper function to parse four hexidecimal digits in \uXXXX in ParseString(). template unsigned ParseHex4(InputStream& is) { - InputStream s = is; // Use a local copy for optimization + StreamTraits::StreamCopyType s = is; // Use a local copy for optimization unsigned codepoint = 0; for (int i = 0; i < 4; i++) { Ch c = s.Take(); @@ -419,7 +419,7 @@ private: // Parse string and generate String event. Different code paths for kParseInsituFlag. template void ParseString(InputStream& is, Handler& handler) { - InputStream s = is; // Local copy for optimization + StreamTraits::StreamCopyType s = is; // Local copy for optimization if (parseFlags & kParseInsituFlag) { Ch *head = s.PutBegin(); ParseStringToStream(s, s); @@ -499,7 +499,7 @@ private: template void ParseNumber(InputStream& is, Handler& handler) { - InputStream s = is; // Local copy for optimization + StreamTraits::StreamCopyType s = is; // Local copy for optimization // Parse minus bool minus = false; if (s.Peek() == '-') { diff --git a/test/unittest/readertest.cpp b/test/unittest/readertest.cpp index 1fa33f1..5cf937e 100644 --- a/test/unittest/readertest.cpp +++ b/test/unittest/readertest.cpp @@ -592,4 +592,52 @@ TEST(Reader, SkipWhitespace) { SkipWhitespace(ss); EXPECT_EQ(expected[i], ss.Take()); } -} \ No newline at end of file +} + +// Test implementing a stream without copy stream optimization. +// Clone from GenericStringStream except that copy constructor is disabled. +template +class CustomStringStream { +public: + typedef typename Encoding::Ch Ch; + + CustomStringStream(const Ch *src) : src_(src), head_(src) {} + + Ch Peek() const { return *src_; } + Ch Take() { return *src_++; } + size_t Tell() const { return static_cast(src_ - head_); } + + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + // Not support copy constructor. + CustomStringStream(const CustomStringStream&); + + const Ch* src_; //!< Current read position. + const Ch* head_; //!< Original head of the string. +}; + +// If the following code is compiled, it should generate compilation error as predicted. +// Because CustomStringStream<> is not copyable via making copy constructor private. +#if 0 +namespace rapidjson { + +template +struct StreamTraits> { + typedef CustomStringStream StreamCopyType; +}; + +} // namespace rapdijson +#endif + +TEST(Reader, CustomStringStream) { + const char* json = "{ \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3] } "; + CustomStringStream> s(json); + ParseObjectHandler h; + Reader reader; + reader.ParseObject<0>(s, h); + EXPECT_EQ(20u, h.step_); +}