From 84126175e40193800834a142cfc4b152deef3779 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Thu, 10 Jul 2014 01:32:50 +0800 Subject: [PATCH 01/39] Wrote a part of SAX --- doc/sax.md | 247 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 242 insertions(+), 5 deletions(-) diff --git a/doc/sax.md b/doc/sax.md index 6442b6b..c0a1af0 100644 --- a/doc/sax.md +++ b/doc/sax.md @@ -1,11 +1,248 @@ # SAX -## Reader +The term "SAX" originated from [Simple API for XML](http://en.wikipedia.org/wiki/Simple_API_for_XML). We borrowed this term for JSON parsing and generation. -### Handler +In RapidJSON, `Reader` (typedef of `GenericReader<...>`) is the SAX-style parser for JSON, and `Writer` (typedef of `GenericWriter<...>`) is the SAX-style generator for JSON. -### Parse Error +[TOC] -## Writer +# Reader {#Reader} + +`Reader` parses a JSON from a stream. While it reads characters from the stream, it analyze the characters according to the syntax of JSON, and publish events to a handler. + +For example, here is a JSON. + +~~~~~~~~~~js +{ + "hello": "world", + "t": true , + "f": false, + "n": null, + "i": 123, + "pi": 3.1416, + "a": [1, 2, 3, 4] +} +~~~~~~~~~~ + +While a `Reader` parses the JSON, it will publish the following events to the handler sequentially: + +~~~~~~~~~~ +BeginObject() +String("hello", 5, true) +String("world", 5, true) +String("t", 1, true) +Bool(true) +String("f", 1, true) +Bool(false) +String("n", 1, true) +Null() +String("i") +UInt(123) +String("pi") +Double(3.1416) +String("a") +BeginArray() +Uint(1) +Uint(2) +Uint(3) +Uint(4) +EndArray(4) +EndObject(7) +~~~~~~~~~~ + +These events can be easily match up with the JSON, except some event parameters need further explanation. Let's see the code which produces exactly the same output as above: + +~~~~~~~~~~cpp +#include "rapidjson/reader.h" +#include + +using namespace rapidjson; +using namespace std; + +struct MyHandler { + void Null() { cout << "Null()" << endl; } + void Bool(bool b) { cout << "Bool(" << (b ? "true" : "false") << ")" << endl; } + void Int(int i) { cout << "Int(" << i << ")" << endl; } + void Uint(unsigned u) { cout << "Uint(" << u << ")" << endl; } + void Int64(int64_t i) { cout << "Int64(" << i << ")" << endl; } + void Uint64(uint64_t u) { cout << "Uint64(" << u << ")" << endl; } + void Double(double d) { { cout << "Double(" << d << ")" << endl; } + void String(const char* str, SizeType length, bool copy) { + cout << "String(" << str << ", " << length << ", " << (b ? "true" : "false") << ")" << endl; } + void StartObject() { cout << "StartObject()" << endl; } + void EndObject(SizeType memberCount) { cout << "EndObject(" << memberCount << ")" << endl; } + void StartArray() { cout << "StartArray()" << endl; } + void EndArray(SizeType elementCount) { cout << "EndArray(" << elementCount << ")" << endl; } +}; + +void main() { + const char* json = "..."; + + MyHandler handler; + Reader reader; + StringStream ss(json); + reader.Parse(ss, handler); +} +~~~~~~~~~~ + +Note that, RapidJSON uses template to statically bind the `Reader` type and the handler type, instead of using class with virtual functions. This paradigm can improve the performance by inlining functions. + +## Handler {#Handler} + +As the previous example showed, user needs to implement a handler, which consumes the events (function calls) from `Reader`. The handler concept has the following member type and member functions. + +~~~~~~~~~~cpp +concept Handler { + typename Ch; + + void Null(); + void Bool(bool b); + void Int(int i); + void Uint(unsigned i); + void Int64(int64_t i); + void Uint64(uint64_t i); + void Double(double d); + void String(const Ch* str, SizeType length, bool copy); + void StartObject(); + void EndObject(SizeType memberCount); + void StartArray(); + void EndArray(SizeType elementCount); +}; +~~~~~~~~~~ + +`Null()` is called when the `Reader` encounters a JSON null value. + +`Bool(bool)` is called when the `Reader` encounters a JSON true or false value. + +When the `Reader` encounters a JSON number, it chooses a suitable C++ type mapping. And then it calls *one* function out of `Int(int)`, `Uint(unsigned)`, `Int64(int64_t)`, `Uint64(uint64_t)` and `Double(double)`. + +`String(const char* str, SizeType length, bool copy)` is called when the `Reader` encounters a string. The first parameter is pointer to the string. The second parameter is the length of the string (excluding the null terminator). Note that RapidJSON supports null character `'\0'` inside a string. If such situation happens, `strlen(str) < length`. The last `copy` indicates whether the handler needs to make a copy of the string. For normal parsing, `copy = true`. Only when *insitu* parsing is used, `copy = false`. And beware that, the character type depends on the target encoding, which will be explained later. + +When the `Reader` encounters the beginning of an object, it calls `StartObject()`. An object in JSON is a set of name-value pairs. If the object contains members it first calls `String()` for the name of member, and then calls functions depending on the type of the value. These calls of name-value pairs repeats until calling `EndObject(SizeType memberCount)`. Note that the `memberCount` parameter is just an aid for the handler, user may not need this parameter. + +Array is similar to object but simpler. At the beginning of an array, the `Reader` calls `BeginArary()`. If there is elements, it calls functions according to the types of element. Similarly, in the last call `EndArray(SizeType elementCount)`, the parameter `elementCount` is just an aid for the handler. + +## GenericReader {#GenericReader} + +As mentioned before, `Reader` is a typedef of a template class `GenericReader`: + +~~~~~~~~~~cpp +namespace rapidjson { + +template > +class GenericReader { + // ... +}; + +typedef GenericReader, UTF8<> > Reader; + +} // namespace rapidjson +~~~~~~~~~~ + +The `Reader` uses UTF-8 as both source and target encoding. The source encoding means the encoding in the JSON stream. The target encoding means the encoding of the `str` parameter in `String()` calls. For example, to parse a UTF-8 stream and outputs UTF-16 string events, you can define a reader by: + +~~~~~~~~~~cpp +GenericReader, UTF16<> > reader; +~~~~~~~~~~ + +Note that, the default character type of `UTF16` is `wchar_t`. So this `reader`needs to call `String(const wchar_t*, SizeType, bool)` of the handler. + +The third template parameter `Allocator` is the allocator type for internal data structure (actually a stack). + +## Parsing {#Parsing} + +The one and only one function of `Reader` is to parse JSON. + +~~~~~~~~~~cpp +template +bool Parse(InputStream& is, Handler& handler); + +// with parseFlags = kDefaultParseFlags +template +bool Parse(InputStream& is, Handler& handler); +~~~~~~~~~~ + +If an error occurs during parsing, it will return `false`. User can also calls `bool HasParseEror()`, `ParseErrorCode GetParseErrorCode()` and `size_t GetErrorOffset()` to obtain the error states. Actually `Document` uses these `Reader` functions to obtain parse errors. Please refer to [DOM](doc/dom.md) for details about parse error. + +# Writer {#Writer} + +## PrettyWriter {#PrettyWriter} + +# Techniques {#Techniques} + +## Parsing JSON to Custom Data Structure {#CustomDataStructure} + +`Document`'s parsing capability is completely based on `Reader`. Actually `Document` is a handler which receives events from a reader to build a DOM during parsing. + +User may uses `Reader` to build other data structures directly. This eliminates building of DOM, thus reducing memory and improving performance. + +Example: +~~~~~~~~~~cpp +// Note: Ad hoc, not yet tested. +using namespace std; +using namespace rapidjson; + +typedef map MessageMap; + +struct MessageHandler : public GenericBaseHandler<> { + MessageHandler() : mState(kExpectStart) { + } + + bool Default() { + return false; + } + + bool StartObject() { + if (!kBeforeStart) + return false; + mState = mExpectName; + return true; + } + + bool String(const Ch* str, SizeType length, bool copy) { + if (mState == kExpectName) { + name_ = string(str, length); + return true; + } + else if (mState == kExpectValue) { + messages_.insert(MessageMap::value_type(name_, string(str, length))); + return true; + } + else + return false; + } + + bool EndObject() { + return mState == kExpectName; + } + + MessageMap messages_; + enum State { + kExpectObjectStart, + kExpectName, + kExpectValue, + }mState; + std::string name_; +}; + +void ParseMessages(const char* json, MessageMap& messages) { + Reader reader; + MessageHandler handler; + StringStream ss(json); + if (reader.Parse(ss, handler)) + messages.swap(handler.messages_); +} + +main() { + MessageMap messages; + ParseMessages("{ \"greeting\" : \"Hello!\", \"farewell\" : \"bye-bye!\" }", messages); +} +~~~~~~~~~~ + +~~~~~~~~~~cpp +// Parse a NxM array +const char* json = "[3, 4, [1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]" +~~~~~~~~~~ + +## Filtering of JSON {#Filtering} -### PrettyWriter From cc04219e36f796f898e1e2c21fdb1f72fde4b0bd Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Thu, 10 Jul 2014 19:33:01 +0800 Subject: [PATCH 02/39] Fixes #59 by adding bool return value for each event handler function --- include/rapidjson/document.h | 78 ++++++++++--------- include/rapidjson/prettywriter.h | 52 ++++++------- include/rapidjson/reader.h | 125 +++++++++++++++++++------------ include/rapidjson/writer.h | 88 ++++++++++++---------- test/perftest/perftest.h | 6 +- test/perftest/rapidjsontest.cpp | 4 +- test/unittest/readertest.cpp | 99 ++++++++++++------------ test/unittest/writertest.cpp | 14 ++-- 8 files changed, 257 insertions(+), 209 deletions(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 74e5c66..c37f7f0 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -1015,44 +1015,45 @@ int z = a[0u].GetInt(); // This works too. \param handler An object implementing concept Handler. */ template - const GenericValue& Accept(Handler& handler) const { + bool Accept(Handler& handler) const { switch(GetType()) { - case kNullType: handler.Null(); break; - case kFalseType: handler.Bool(false); break; - case kTrueType: handler.Bool(true); break; + case kNullType: return handler.Null(); + case kFalseType: return handler.Bool(false); + case kTrueType: return handler.Bool(true); case kObjectType: - handler.StartObject(); + if (!handler.StartObject()) + return false; for (ConstMemberIterator m = MemberBegin(); m != MemberEnd(); ++m) { - handler.String(m->name.data_.s.str, m->name.data_.s.length, (m->name.flags_ & kCopyFlag) != 0); - m->value.Accept(handler); + if (!handler.String(m->name.data_.s.str, m->name.data_.s.length, (m->name.flags_ & kCopyFlag) != 0)) + return false; + if (!m->value.Accept(handler)) + return false; } - handler.EndObject(data_.o.size); - break; + return handler.EndObject(data_.o.size); case kArrayType: - handler.StartArray(); + if (!handler.StartArray()) + return false; for (GenericValue* v = data_.a.elements; v != data_.a.elements + data_.a.size; ++v) - v->Accept(handler); - handler.EndArray(data_.a.size); - break; - + if (!v->Accept(handler)) + return false; + return handler.EndArray(data_.a.size); + case kStringType: - handler.String(data_.s.str, data_.s.length, (flags_ & kCopyFlag) != 0); - break; - + return handler.String(data_.s.str, data_.s.length, (flags_ & kCopyFlag) != 0); + case kNumberType: - if (IsInt()) handler.Int(data_.n.i.i); - else if (IsUint()) handler.Uint(data_.n.u.u); - else if (IsInt64()) handler.Int64(data_.n.i64); - else if (IsUint64()) handler.Uint64(data_.n.u64); - else handler.Double(data_.n.d); - break; - + if (IsInt()) return handler.Int(data_.n.i.i); + else if (IsUint()) return handler.Uint(data_.n.u.u); + else if (IsInt64()) return handler.Int64(data_.n.i64); + else if (IsUint64()) return handler.Uint64(data_.n.u64); + else return handler.Double(data_.n.d); + default: RAPIDJSON_ASSERT(false); } - return *this; + return false; } private: @@ -1354,33 +1355,36 @@ private: friend class GenericValue; // for deep copying // Implementation of Handler - void Null() { new (stack_.template Push()) ValueType(); } - void Bool(bool b) { new (stack_.template Push()) ValueType(b); } - void Int(int i) { new (stack_.template Push()) ValueType(i); } - void Uint(unsigned i) { new (stack_.template Push()) ValueType(i); } - void Int64(int64_t i) { new (stack_.template Push()) ValueType(i); } - void Uint64(uint64_t i) { new (stack_.template Push()) ValueType(i); } - void Double(double d) { new (stack_.template Push()) ValueType(d); } + bool Null() { new (stack_.template Push()) ValueType(); return true; } + bool Bool(bool b) { new (stack_.template Push()) ValueType(b); return true; } + bool Int(int i) { new (stack_.template Push()) ValueType(i); return true; } + bool Uint(unsigned i) { new (stack_.template Push()) ValueType(i); return true; } + bool Int64(int64_t i) { new (stack_.template Push()) ValueType(i); return true; } + bool Uint64(uint64_t i) { new (stack_.template Push()) ValueType(i); return true; } + bool Double(double d) { new (stack_.template Push()) ValueType(d); return true; } - void String(const Ch* str, SizeType length, bool copy) { + bool String(const Ch* str, SizeType length, bool copy) { if (copy) new (stack_.template Push()) ValueType(str, length, GetAllocator()); else new (stack_.template Push()) ValueType(str, length); + return true; } - void StartObject() { new (stack_.template Push()) ValueType(kObjectType); } + bool StartObject() { new (stack_.template Push()) ValueType(kObjectType); return true; } - void EndObject(SizeType memberCount) { + bool EndObject(SizeType memberCount) { typename ValueType::Member* members = stack_.template Pop(memberCount); stack_.template Top()->SetObjectRaw(members, (SizeType)memberCount, GetAllocator()); + return true; } - void StartArray() { new (stack_.template Push()) ValueType(kArrayType); } + bool StartArray() { new (stack_.template Push()) ValueType(kArrayType); return true; } - void EndArray(SizeType elementCount) { + bool EndArray(SizeType elementCount) { ValueType* elements = stack_.template Pop(elementCount); stack_.template Top()->SetArrayRaw(elements, elementCount, GetAllocator()); + return true; } private: diff --git a/include/rapidjson/prettywriter.h b/include/rapidjson/prettywriter.h index 031f5de..f01e53e 100644 --- a/include/rapidjson/prettywriter.h +++ b/include/rapidjson/prettywriter.h @@ -51,29 +51,27 @@ public: */ //@{ - PrettyWriter& Null() { PrettyPrefix(kNullType); Base::WriteNull(); return *this; } - PrettyWriter& Bool(bool b) { PrettyPrefix(b ? kTrueType : kFalseType); Base::WriteBool(b); return *this; } - PrettyWriter& Int(int i) { PrettyPrefix(kNumberType); Base::WriteInt(i); return *this; } - PrettyWriter& Uint(unsigned u) { PrettyPrefix(kNumberType); Base::WriteUint(u); return *this; } - PrettyWriter& Int64(int64_t i64) { PrettyPrefix(kNumberType); Base::WriteInt64(i64); return *this; } - PrettyWriter& Uint64(uint64_t u64) { PrettyPrefix(kNumberType); Base::WriteUint64(u64); return *this; } - PrettyWriter& Double(double d) { PrettyPrefix(kNumberType); Base::WriteDouble(d); return *this; } + bool Null() { PrettyPrefix(kNullType); return Base::WriteNull(); } + bool Bool(bool b) { PrettyPrefix(b ? kTrueType : kFalseType); return Base::WriteBool(b); } + bool Int(int i) { PrettyPrefix(kNumberType); return Base::WriteInt(i); } + bool Uint(unsigned u) { PrettyPrefix(kNumberType); return Base::WriteUint(u); } + bool Int64(int64_t i64) { PrettyPrefix(kNumberType); return Base::WriteInt64(i64); } + bool Uint64(uint64_t u64) { PrettyPrefix(kNumberType); return Base::WriteUint64(u64); } + bool Double(double d) { PrettyPrefix(kNumberType); return Base::WriteDouble(d); } - PrettyWriter& String(const Ch* str, SizeType length, bool copy = false) { + bool String(const Ch* str, SizeType length, bool copy = false) { (void)copy; PrettyPrefix(kStringType); - Base::WriteString(str, length); - return *this; + return Base::WriteString(str, length); } - PrettyWriter& StartObject() { + bool StartObject() { PrettyPrefix(kObjectType); new (Base::level_stack_.template Push()) typename Base::Level(false); - Base::WriteStartObject(); - return *this; + return Base::WriteStartObject(); } - PrettyWriter& EndObject(SizeType memberCount = 0) { + bool EndObject(SizeType memberCount = 0) { (void)memberCount; RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); RAPIDJSON_ASSERT(!Base::level_stack_.template Top()->inArray); @@ -83,20 +81,20 @@ public: Base::os_.Put('\n'); WriteIndent(); } - Base::WriteEndObject(); + if (!Base::WriteEndObject()) + return false; if (Base::level_stack_.Empty()) // end of json text Base::os_.Flush(); - return *this; + return true; } - PrettyWriter& StartArray() { + bool StartArray() { PrettyPrefix(kArrayType); new (Base::level_stack_.template Push()) typename Base::Level(true); - Base::WriteStartArray(); - return *this; + return Base::WriteStartArray(); } - PrettyWriter& EndArray(SizeType memberCount = 0) { + bool EndArray(SizeType memberCount = 0) { (void)memberCount; RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); RAPIDJSON_ASSERT(Base::level_stack_.template Top()->inArray); @@ -106,10 +104,11 @@ public: Base::os_.Put('\n'); WriteIndent(); } - Base::WriteEndArray(); + if (!Base::WriteEndArray()) + return false; if (Base::level_stack_.Empty()) // end of json text Base::os_.Flush(); - return *this; + return true; } //@} @@ -118,12 +117,15 @@ public: //@{ //! Simpler but slower overload. - PrettyWriter& String(const Ch* str) { return String(str, internal::StrLen(str)); } + bool String(const Ch* str) { return String(str, internal::StrLen(str)); } //! Overridden for fluent API, see \ref Writer::Double() - PrettyWriter& Double(double d, int precision) { + bool Double(double d, int precision) { int oldPrecision = Base::GetDoublePrecision(); - return SetDoublePrecision(precision).Double(d).SetDoublePrecision(oldPrecision); + SetDoublePrecision(precision); + bool ret = Double(d); + SetDoublePrecision(oldPrecision); + return ret; } //@} diff --git a/include/rapidjson/reader.h b/include/rapidjson/reader.h index 59d03b6..2e1480d 100644 --- a/include/rapidjson/reader.h +++ b/include/rapidjson/reader.h @@ -75,7 +75,9 @@ enum ParseErrorCode { kParseErrorNumberTooBig, //!< Number too big to be stored in double. kParseErrorNumberMissFraction, //!< Miss fraction part in number. - kParseErrorNumberMissExponent //!< Miss exponent in number. + kParseErrorNumberMissExponent, //!< Miss exponent in number. + + kParseErrorTermination //!< Parsing was terminated. }; /////////////////////////////////////////////////////////////////////////////// @@ -83,22 +85,24 @@ enum ParseErrorCode { /*! \class rapidjson::Handler \brief Concept for receiving events from GenericReader upon parsing. + The functions return true if no error occurs. If they return false, + the event publisher should terminate the process. \code concept Handler { typename Ch; - void Null(); - void Bool(bool b); - void Int(int i); - void Uint(unsigned i); - void Int64(int64_t i); - void Uint64(uint64_t i); - void Double(double d); - void String(const Ch* str, SizeType length, bool copy); - void StartObject(); - void EndObject(SizeType memberCount); - void StartArray(); - void EndArray(SizeType elementCount); + bool Null(); + bool Bool(bool b); + bool Int(int i); + bool Uint(unsigned i); + bool Int64(int64_t i); + bool Uint64(uint64_t i); + bool Double(double d); + bool String(const Ch* str, SizeType length, bool copy); + bool StartObject(); + bool EndObject(SizeType memberCount); + bool StartArray(); + bool EndArray(SizeType elementCount); }; \endcode */ @@ -113,19 +117,19 @@ template > struct BaseReaderHandler { typedef typename Encoding::Ch Ch; - void Default() {} - void Null() { Default(); } - void Bool(bool) { Default(); } - void Int(int) { Default(); } - void Uint(unsigned) { Default(); } - void Int64(int64_t) { Default(); } - void Uint64(uint64_t) { Default(); } - void Double(double) { Default(); } - void String(const Ch*, SizeType, bool) { Default(); } - void StartObject() { Default(); } - void EndObject(SizeType) { Default(); } - void StartArray() { Default(); } - void EndArray(SizeType) { Default(); } + bool Default() { return true; } + bool Null() { return Default(); } + bool Bool(bool) { return Default(); } + bool Int(int) { return Default(); } + bool Uint(unsigned) { return Default(); } + bool Int64(int64_t) { return Default(); } + bool Uint64(uint64_t) { return Default(); } + bool Double(double) { return Default(); } + bool String(const Ch*, SizeType, bool) { return Default(); } + bool StartObject() { return Default(); } + bool EndObject(SizeType) { return Default(); } + bool StartArray() { return Default(); } + bool EndArray(SizeType) { return Default(); } }; /////////////////////////////////////////////////////////////////////////////// @@ -351,12 +355,16 @@ private: void ParseObject(InputStream& is, Handler& handler) { RAPIDJSON_ASSERT(is.Peek() == '{'); is.Take(); // Skip '{' - handler.StartObject(); + + if (!handler.StartObject()) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + SkipWhitespace(is); if (is.Peek() == '}') { is.Take(); - handler.EndObject(0); // empty object + if (!handler.EndObject(0)) // empty object + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); return; } @@ -383,9 +391,13 @@ private: ++memberCount; - switch(is.Take()) { + switch (is.Take()) { case ',': SkipWhitespace(is); break; - case '}': handler.EndObject(memberCount); return; + case '}': + if (!handler.EndObject(memberCount)) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + else + return; default: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); } } @@ -396,12 +408,16 @@ private: void ParseArray(InputStream& is, Handler& handler) { RAPIDJSON_ASSERT(is.Peek() == '['); is.Take(); // Skip '[' - handler.StartArray(); + + if (!handler.StartArray()) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + SkipWhitespace(is); if (is.Peek() == ']') { is.Take(); - handler.EndArray(0); // empty array + if (!handler.EndArray(0)) // empty array + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); return; } @@ -415,7 +431,11 @@ private: switch (is.Take()) { case ',': SkipWhitespace(is); break; - case ']': handler.EndArray(elementCount); return; + case ']': + if (!handler.EndArray(elementCount)) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + else + return; default: RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); } } @@ -426,8 +446,10 @@ private: RAPIDJSON_ASSERT(is.Peek() == 'n'); is.Take(); - if (is.Take() == 'u' && is.Take() == 'l' && is.Take() == 'l') - handler.Null(); + if (is.Take() == 'u' && is.Take() == 'l' && is.Take() == 'l') { + if (!handler.Null()) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + } else RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell() - 1); } @@ -437,10 +459,12 @@ private: RAPIDJSON_ASSERT(is.Peek() == 't'); is.Take(); - if (is.Take() == 'r' && is.Take() == 'u' && is.Take() == 'e') - handler.Bool(true); + if (is.Take() == 'r' && is.Take() == 'u' && is.Take() == 'e') { + if (!handler.Bool(true)) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + } else - RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell() - 1); } template @@ -448,8 +472,10 @@ private: RAPIDJSON_ASSERT(is.Peek() == 'f'); is.Take(); - if (is.Take() == 'a' && is.Take() == 'l' && is.Take() == 's' && is.Take() == 'e') - handler.Bool(false); + if (is.Take() == 'a' && is.Take() == 'l' && is.Take() == 's' && is.Take() == 'e') { + if (!handler.Bool(false)) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + } else RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell() - 1); } @@ -506,14 +532,16 @@ private: return; size_t length = s.PutEnd(head) - 1; RAPIDJSON_ASSERT(length <= 0xFFFFFFFF); - handler.String((typename TargetEncoding::Ch*)head, SizeType(length), false); + if (!handler.String((typename TargetEncoding::Ch*)head, SizeType(length), false)) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell()); } else { StackStream stackStream(stack_); ParseStringToStream(s, stackStream); if (HasParseError()) return; - handler.String(stack_.template Pop(stackStream.length_), stackStream.length_ - 1, true); + if (!handler.String(stack_.template Pop(stackStream.length_), stackStream.length_ - 1, true)) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell()); } } @@ -715,6 +743,7 @@ private: } // Finish parsing, call event according to the type of number. + bool cont = true; if (useDouble) { int expSum = exp + expFrac; if (expSum < -308) { @@ -725,22 +754,24 @@ private: else d *= internal::Pow10(expSum); - handler.Double(minus ? -d : d); + cont = handler.Double(minus ? -d : d); } else { if (try64bit) { if (minus) - handler.Int64(-(int64_t)i64); + cont = handler.Int64(-(int64_t)i64); else - handler.Uint64(i64); + cont = handler.Uint64(i64); } else { if (minus) - handler.Int(-(int)i); + cont = handler.Int(-(int)i); else - handler.Uint(i); + cont = handler.Uint(i); } } + if (!cont) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell()); } // Parse any JSON value diff --git a/include/rapidjson/writer.h b/include/rapidjson/writer.h index 979b781..33bf71b 100644 --- a/include/rapidjson/writer.h +++ b/include/rapidjson/writer.h @@ -64,12 +64,12 @@ public: */ //@{ - Writer& Null() { Prefix(kNullType); WriteNull(); return *this; } - Writer& Bool(bool b) { Prefix(b ? kTrueType : kFalseType); WriteBool(b); return *this; } - Writer& Int(int i) { Prefix(kNumberType); WriteInt(i); return *this; } - Writer& Uint(unsigned u) { Prefix(kNumberType); WriteUint(u); return *this; } - Writer& Int64(int64_t i64) { Prefix(kNumberType); WriteInt64(i64); return *this; } - Writer& Uint64(uint64_t u64) { Prefix(kNumberType); WriteUint64(u64); return *this; } + bool Null() { Prefix(kNullType); return WriteNull(); } + bool Bool(bool b) { Prefix(b ? kTrueType : kFalseType); return WriteBool(b); } + bool Int(int i) { Prefix(kNumberType); return WriteInt(i); } + bool Uint(unsigned u) { Prefix(kNumberType); return WriteUint(u); } + bool Int64(int64_t i64) { Prefix(kNumberType); return WriteInt64(i64); } + bool Uint64(uint64_t u64) { Prefix(kNumberType); return WriteUint64(u64); } //! Writes the given \c double value to the stream /*! @@ -80,51 +80,48 @@ public: writer.SetDoublePrecision(12).Double(M_PI); \endcode \param d The value to be written. - \return The Writer itself for fluent API. + \return Whether it is succeed. */ - Writer& Double(double d) { Prefix(kNumberType); WriteDouble(d); return *this; } + bool Double(double d) { Prefix(kNumberType); return WriteDouble(d); } - Writer& String(const Ch* str, SizeType length, bool copy = false) { + bool String(const Ch* str, SizeType length, bool copy = false) { (void)copy; Prefix(kStringType); - WriteString(str, length); - return *this; + return WriteString(str, length); } - Writer& StartObject() { + bool StartObject() { Prefix(kObjectType); new (level_stack_.template Push()) Level(false); - WriteStartObject(); - return *this; + return WriteStartObject(); } - Writer& EndObject(SizeType memberCount = 0) { + bool EndObject(SizeType memberCount = 0) { (void)memberCount; RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); RAPIDJSON_ASSERT(!level_stack_.template Top()->inArray); level_stack_.template Pop(1); - WriteEndObject(); + bool ret = WriteEndObject(); if (level_stack_.Empty()) // end of json text os_.Flush(); - return *this; + return ret; } - Writer& StartArray() { + bool StartArray() { Prefix(kArrayType); new (level_stack_.template Push()) Level(true); - WriteStartArray(); - return *this; + return WriteStartArray(); } - Writer& EndArray(SizeType elementCount = 0) { + bool EndArray(SizeType elementCount = 0) { (void)elementCount; RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); RAPIDJSON_ASSERT(level_stack_.template Top()->inArray); level_stack_.template Pop(1); - WriteEndArray(); + bool ret = WriteEndArray(); if (level_stack_.Empty()) // end of json text os_.Flush(); - return *this; + return ret; } //@} @@ -138,15 +135,18 @@ public: \see Double(), SetDoublePrecision(), GetDoublePrecision() \param d The value to be written \param precision The number of significant digits for this value - \return The Writer itself for fluent API. + \return Whether it is succeeded. */ - Writer& Double(double d, int precision) { + bool Double(double d, int precision) { int oldPrecision = GetDoublePrecision(); - return SetDoublePrecision(precision).Double(d).SetDoublePrecision(oldPrecision); + SetDoublePrecision(precision); + bool ret = Double(d); + SetDoublePrecision(oldPrecision); + return ret; } //! Simpler but slower overload. - Writer& String(const Ch* str) { return String(str, internal::StrLen(str)); } + bool String(const Ch* str) { return String(str, internal::StrLen(str)); } //@} @@ -160,28 +160,29 @@ protected: static const size_t kDefaultLevelDepth = 32; - void WriteNull() { - os_.Put('n'); os_.Put('u'); os_.Put('l'); os_.Put('l'); + bool WriteNull() { + os_.Put('n'); os_.Put('u'); os_.Put('l'); os_.Put('l'); return true; } - void WriteBool(bool b) { + bool WriteBool(bool b) { if (b) { os_.Put('t'); os_.Put('r'); os_.Put('u'); os_.Put('e'); } else { os_.Put('f'); os_.Put('a'); os_.Put('l'); os_.Put('s'); os_.Put('e'); } + return true; } - void WriteInt(int i) { + bool WriteInt(int i) { if (i < 0) { os_.Put('-'); i = -i; } - WriteUint((unsigned)i); + return WriteUint((unsigned)i); } - void WriteUint(unsigned u) { + bool WriteUint(unsigned u) { char buffer[10]; char *p = buffer; do { @@ -193,17 +194,19 @@ protected: --p; os_.Put(*p); } while (p != buffer); + return true; } - void WriteInt64(int64_t i64) { + bool WriteInt64(int64_t i64) { if (i64 < 0) { os_.Put('-'); i64 = -i64; } WriteUint64((uint64_t)i64); + return true; } - void WriteUint64(uint64_t u64) { + bool WriteUint64(uint64_t u64) { char buffer[20]; char *p = buffer; do { @@ -215,6 +218,7 @@ protected: --p; os_.Put(*p); } while (p != buffer); + return true; } #ifdef _MSC_VER @@ -224,16 +228,17 @@ protected: #endif //! \todo Optimization with custom double-to-string converter. - void WriteDouble(double d) { + bool WriteDouble(double d) { char buffer[100]; int ret = RAPIDJSON_SNPRINTF(buffer, sizeof(buffer), "%.*g", doublePrecision_, d); RAPIDJSON_ASSERT(ret >= 1); for (int i = 0; i < ret; i++) os_.Put(buffer[i]); + return true; } #undef RAPIDJSON_SNPRINTF - void WriteString(const Ch* str, SizeType length) { + bool WriteString(const Ch* str, SizeType length) { static const char hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; static const char escape[256] = { #define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 @@ -266,12 +271,13 @@ protected: Transcoder::Transcode(is, os_); } os_.Put('\"'); + return true; } - void WriteStartObject() { os_.Put('{'); } - void WriteEndObject() { os_.Put('}'); } - void WriteStartArray() { os_.Put('['); } - void WriteEndArray() { os_.Put(']'); } + bool WriteStartObject() { os_.Put('{'); return true; } + bool WriteEndObject() { os_.Put('}'); return true; } + bool WriteStartArray() { os_.Put('['); return true; } + bool WriteEndArray() { os_.Put(']'); return true; } void Prefix(Type type) { (void)type; diff --git a/test/perftest/perftest.h b/test/perftest/perftest.h index 0f42a6c..be36920 100644 --- a/test/perftest/perftest.h +++ b/test/perftest/perftest.h @@ -8,7 +8,11 @@ #define TEST_PLATFORM 0 #define TEST_MISC 0 -#if TEST_RAPIDJSON +#define TEST_VERSION_CODE(x,y,z) \ + (((x)*100000) + ((y)*100) + (z)) + +// Only gcc >4.3 supports SSE4.2 +#if TEST_RAPIDJSON && !(defined(__GNUC__) && TEST_VERSION_CODE(__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__) < TEST_VERSION_CODE(4,3,0)) //#define RAPIDJSON_SSE2 #define RAPIDJSON_SSE42 #endif diff --git a/test/perftest/rapidjsontest.cpp b/test/perftest/rapidjsontest.cpp index dd8bc30..6eb6e2a 100644 --- a/test/perftest/rapidjsontest.cpp +++ b/test/perftest/rapidjsontest.cpp @@ -179,8 +179,8 @@ RAPIDJSON_DIAG_OFF(effc++) struct ValueCounter : public BaseReaderHandler<> { ValueCounter() : count_(1) {} // root - void EndObject(SizeType memberCount) { count_ += memberCount * 2; } - void EndArray(SizeType elementCount) { count_ += elementCount; } + bool EndObject(SizeType memberCount) { count_ += memberCount * 2; return true; } + bool EndArray(SizeType elementCount) { count_ += elementCount; return true; } SizeType count_; }; diff --git a/test/unittest/readertest.cpp b/test/unittest/readertest.cpp index 494a9f2..fe6613a 100644 --- a/test/unittest/readertest.cpp +++ b/test/unittest/readertest.cpp @@ -13,10 +13,10 @@ RAPIDJSON_DIAG_OFF(effc++) template struct ParseBoolHandler : BaseReaderHandler<> { ParseBoolHandler() : step_(0) {} - void Default() { FAIL(); } + bool Default() { FAIL(); } // gcc 4.8.x generates warning in EXPECT_EQ(bool, bool) on this gtest version. // Workaround with EXPECT_TRUE(). - void Bool(bool b) { /*EXPECT_EQ(expect, b); */EXPECT_TRUE(expect == b); ++step_; } + bool Bool(bool b) { /*EXPECT_EQ(expect, b); */EXPECT_TRUE(expect == b); ++step_; return true; } unsigned step_; }; @@ -39,8 +39,8 @@ TEST(Reader, ParseFalse) { struct ParseIntHandler : BaseReaderHandler<> { ParseIntHandler() : step_(0), actual_() {} - void Default() { FAIL(); } - void Int(int i) { actual_ = i; step_++; } + bool Default() { ADD_FAILURE(); return false; } + bool Int(int i) { actual_ = i; step_++; return true; } unsigned step_; int actual_; @@ -48,8 +48,8 @@ struct ParseIntHandler : BaseReaderHandler<> { struct ParseUintHandler : BaseReaderHandler<> { ParseUintHandler() : step_(0), actual_() {} - void Default() { FAIL(); } - void Uint(unsigned i) { actual_ = i; step_++; } + bool Default() { ADD_FAILURE(); return false; } + bool Uint(unsigned i) { actual_ = i; step_++; return true; } unsigned step_; unsigned actual_; @@ -57,8 +57,8 @@ struct ParseUintHandler : BaseReaderHandler<> { struct ParseInt64Handler : BaseReaderHandler<> { ParseInt64Handler() : step_(0), actual_() {} - void Default() { FAIL(); } - void Int64(int64_t i) { actual_ = i; step_++; } + bool Default() { ADD_FAILURE(); return false; } + bool Int64(int64_t i) { actual_ = i; step_++; return true; } unsigned step_; int64_t actual_; @@ -66,8 +66,8 @@ struct ParseInt64Handler : BaseReaderHandler<> { struct ParseUint64Handler : BaseReaderHandler<> { ParseUint64Handler() : step_(0), actual_() {} - void Default() { FAIL(); } - void Uint64(uint64_t i) { actual_ = i; step_++; } + bool Default() { ADD_FAILURE(); return false; } + bool Uint64(uint64_t i) { actual_ = i; step_++; return true; } unsigned step_; uint64_t actual_; @@ -75,8 +75,8 @@ struct ParseUint64Handler : BaseReaderHandler<> { struct ParseDoubleHandler : BaseReaderHandler<> { ParseDoubleHandler() : step_(0), actual_() {} - void Default() { FAIL(); } - void Double(double d) { actual_ = d; step_++; } + bool Default() { ADD_FAILURE(); return false; } + bool Double(double d) { actual_ = d; step_++; return true; } unsigned step_; double actual_; @@ -194,8 +194,8 @@ struct ParseStringHandler : BaseReaderHandler { ParseStringHandler(const ParseStringHandler&); ParseStringHandler& operator=(const ParseStringHandler&); - void Default() { FAIL(); } - void String(const typename Encoding::Ch* str, size_t length, bool copy) { + bool Default() { ADD_FAILURE(); return false; } + bool String(const typename Encoding::Ch* str, size_t length, bool copy) { EXPECT_EQ(0, str_); if (copy) { str_ = (typename Encoding::Ch*)malloc((length + 1) * sizeof(typename Encoding::Ch)); @@ -204,7 +204,8 @@ struct ParseStringHandler : BaseReaderHandler { else str_ = str; length_ = length; - copy_ = copy; + copy_ = copy; + return true; } const typename Encoding::Ch* str_; @@ -411,10 +412,10 @@ template struct ParseArrayHandler : BaseReaderHandler<> { ParseArrayHandler() : step_(0) {} - void Default() { FAIL(); } - void Uint(unsigned i) { EXPECT_EQ(step_, i); step_++; } - void StartArray() { EXPECT_EQ(0u, step_); step_++; } - void EndArray(SizeType) { step_++; } + bool Default() { ADD_FAILURE(); return false; } + bool Uint(unsigned i) { EXPECT_EQ(step_, i); step_++; return true; } + bool StartArray() { EXPECT_EQ(0u, step_); step_++; return true; } + bool EndArray(SizeType) { step_++; return true; } unsigned step_; }; @@ -462,42 +463,42 @@ TEST(Reader, ParseArray_Error) { struct ParseObjectHandler : BaseReaderHandler<> { ParseObjectHandler() : step_(0) {} - void Null() { EXPECT_EQ(8u, step_); step_++; } - void Bool(bool b) { + bool Null() { EXPECT_EQ(8u, step_); step_++; return true; } + bool Bool(bool b) { switch(step_) { - case 4: EXPECT_TRUE(b); step_++; break; - case 6: EXPECT_FALSE(b); step_++; break; - default: FAIL(); + case 4: EXPECT_TRUE(b); step_++; return true; + case 6: EXPECT_FALSE(b); step_++; return true; + default: ADD_FAILURE(); return false; } } - void Int(int i) { + bool Int(int i) { switch(step_) { - case 10: EXPECT_EQ(123, i); step_++; break; - case 15: EXPECT_EQ(1, i); step_++; break; - case 16: EXPECT_EQ(2, i); step_++; break; - case 17: EXPECT_EQ(3, i); step_++; break; - default: FAIL(); + case 10: EXPECT_EQ(123, i); step_++; return true; + case 15: EXPECT_EQ(1, i); step_++; return true; + case 16: EXPECT_EQ(2, i); step_++; return true; + case 17: EXPECT_EQ(3, i); step_++; return true; + default: ADD_FAILURE(); return false; } } - void Uint(unsigned i) { Int(i); } - void Double(double d) { EXPECT_EQ(12u, step_); EXPECT_EQ(3.1416, d); step_++; } - void String(const char* str, size_t, bool) { + bool Uint(unsigned i) { return Int(i); } + bool Double(double d) { EXPECT_EQ(12u, step_); EXPECT_EQ(3.1416, d); step_++; return true; } + bool String(const char* str, size_t, bool) { switch(step_) { - case 1: EXPECT_STREQ("hello", str); step_++; break; - case 2: EXPECT_STREQ("world", str); step_++; break; - case 3: EXPECT_STREQ("t", str); step_++; break; - case 5: EXPECT_STREQ("f", str); step_++; break; - case 7: EXPECT_STREQ("n", str); step_++; break; - case 9: EXPECT_STREQ("i", str); step_++; break; - case 11: EXPECT_STREQ("pi", str); step_++; break; - case 13: EXPECT_STREQ("a", str); step_++; break; - default: FAIL(); + case 1: EXPECT_STREQ("hello", str); step_++; return true; + case 2: EXPECT_STREQ("world", str); step_++; return true; + case 3: EXPECT_STREQ("t", str); step_++; return true; + case 5: EXPECT_STREQ("f", str); step_++; return true; + case 7: EXPECT_STREQ("n", str); step_++; return true; + case 9: EXPECT_STREQ("i", str); step_++; return true; + case 11: EXPECT_STREQ("pi", str); step_++; return true; + case 13: EXPECT_STREQ("a", str); step_++; return true; + default: ADD_FAILURE(); return false; } } - void StartObject() { EXPECT_EQ(0u, step_); step_++; } - void EndObject(SizeType memberCount) { EXPECT_EQ(19u, step_); EXPECT_EQ(7u, memberCount); step_++;} - void StartArray() { EXPECT_EQ(14u, step_); step_++; } - void EndArray(SizeType elementCount) { EXPECT_EQ(18u, step_); EXPECT_EQ(3u, elementCount); step_++;} + bool StartObject() { EXPECT_EQ(0u, step_); step_++; return true; } + bool EndObject(SizeType memberCount) { EXPECT_EQ(19u, step_); EXPECT_EQ(7u, memberCount); step_++; return true; } + bool StartArray() { EXPECT_EQ(14u, step_); step_++; return true; } + bool EndArray(SizeType elementCount) { EXPECT_EQ(18u, step_); EXPECT_EQ(3u, elementCount); step_++; return true; } unsigned step_; }; @@ -529,9 +530,9 @@ TEST(Reader, ParseObject) { struct ParseEmptyObjectHandler : BaseReaderHandler<> { ParseEmptyObjectHandler() : step_(0) {} - void Default() { FAIL(); } - void StartObject() { EXPECT_EQ(0u, step_); step_++; } - void EndObject(SizeType) { EXPECT_EQ(1u, step_); step_++; } + 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; } unsigned step_; }; diff --git a/test/unittest/writertest.cpp b/test/unittest/writertest.cpp index d189fa1..be3d77b 100644 --- a/test/unittest/writertest.cpp +++ b/test/unittest/writertest.cpp @@ -83,13 +83,13 @@ TEST(Writer,DoublePrecision) { buffer.Clear(); } { // explicit individual double precisions - writer.SetDoublePrecision(2) - .StartArray() - .Double(1.2345,5) - .Double(1.2345678,9) - .Double(0.123456789012,12) - .Double(1234567.8,8) - .EndArray(); + writer.SetDoublePrecision(2); + writer.StartArray(); + writer.Double(1.2345, 5); + writer.Double(1.2345678, 9); + writer.Double(0.123456789012, 12); + writer.Double(1234567.8, 8); + writer.EndArray(); EXPECT_EQ(writer.GetDoublePrecision(), 2); EXPECT_STREQ(json, buffer.GetString()); From f1ae39f9e2885fc840875f8f9e19de2c8c1d0aff Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Fri, 11 Jul 2014 00:37:55 +0800 Subject: [PATCH 03/39] Fixed readertest compilation error --- test/unittest/readertest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unittest/readertest.cpp b/test/unittest/readertest.cpp index fe6613a..a7d286e 100644 --- a/test/unittest/readertest.cpp +++ b/test/unittest/readertest.cpp @@ -13,7 +13,7 @@ RAPIDJSON_DIAG_OFF(effc++) template struct ParseBoolHandler : BaseReaderHandler<> { ParseBoolHandler() : step_(0) {} - bool Default() { FAIL(); } + bool Default() { ADD_FAILURE(); return false; } // gcc 4.8.x generates warning in EXPECT_EQ(bool, bool) on this gtest version. // Workaround with EXPECT_TRUE(). bool Bool(bool b) { /*EXPECT_EQ(expect, b); */EXPECT_TRUE(expect == b); ++step_; return true; } From 41d211cd51acc97240217d9dbebd0b27ceb41242 Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Fri, 11 Jul 2014 08:27:28 +0200 Subject: [PATCH 04/39] GenericValue::operator= : fixup assignment operator While MSVC doesn't like the explicit `.template operator=<...>` syntax (see 4f40ed6), Clang 3.5 complains about the absence of it: In file included from ../../test/perftest/rapidjsontest.cpp:6: ../../include/rapidjson/document.h:504:18: error: use 'template' keyword to treat 'operator =' as a dependent template name return (*this).operator=(str); ^ template Delegate both operator=(StringRefType) and operator=(T) to operator(GenericValue&). --- include/rapidjson/document.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 8406598..45b6c3f 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -501,7 +501,8 @@ public: \see GenericStringRef, operator=(T) */ GenericValue& operator=(StringRefType str) { - return (*this).operator=(str); + GenericValue s(str); + return *this = s; } //! Assignment with primitive types. @@ -519,9 +520,8 @@ public: template RAPIDJSON_DISABLEIF_RETURN(internal::IsPointer,GenericValue&) operator=(T value) { - this->~GenericValue(); - new (this) GenericValue(value); - return *this; + GenericValue v(value); + return *this = v; } //! Deep-copy assignment from Value From b37bd85318170c179494911bbb29dfbf677aa8b2 Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Thu, 3 Jul 2014 17:58:59 +0200 Subject: [PATCH 05/39] move ParseErrorCode to error/error.h In order to enable the customization of the error macros - RAPIDJSON_PARSE_ERROR_NORETURN - RAPIDJSON_PARSE_ERROR_EARLY_RETURN the user may need to have access to the ParseErrorCode enum already. This requires a separate header location than the GenericReader. --- include/rapidjson/error/error.h | 35 +++++++++++++++++++++++++++++++-- include/rapidjson/reader.h | 29 ++------------------------- 2 files changed, 35 insertions(+), 29 deletions(-) diff --git a/include/rapidjson/error/error.h b/include/rapidjson/error/error.h index ba45e7e..af597d2 100644 --- a/include/rapidjson/error/error.h +++ b/include/rapidjson/error/error.h @@ -1,8 +1,6 @@ #ifndef RAPIDJSON_ERROR_ERROR_H__ #define RAPIDJSON_ERROR_ERROR_H__ -#include "../reader.h" // ParseErrorCode - /////////////////////////////////////////////////////////////////////////////// // RAPIDJSON_ERROR_CHARTYPE @@ -29,6 +27,39 @@ namespace rapidjson { +/////////////////////////////////////////////////////////////////////////////// +// ParseErrorCode + +//! Error code of parsing. +/*! \see GenericReader::Parse, GenericReader::GetParseErrorCode +*/ +enum ParseErrorCode { + kParseErrorNone = 0, //!< No error. + + kParseErrorDocumentEmpty, //!< The document is empty. + kParseErrorDocumentRootNotObjectOrArray, //!< The document root must be either object or array. + kParseErrorDocumentRootNotSingular, //!< The document root must not follow by other values. + + kParseErrorValueInvalid, //!< Invalid value. + + kParseErrorObjectMissName, //!< Missing a name for object member. + kParseErrorObjectMissColon, //!< Missing a colon after a name of object member. + kParseErrorObjectMissCommaOrCurlyBracket, //!< Missing a comma or '}' after an object member. + + kParseErrorArrayMissCommaOrSquareBracket, //!< Missing a comma or ']' after an array element. + + kParseErrorStringUnicodeEscapeInvalidHex, //!< Incorrect hex digit after \\u escape in string. + kParseErrorStringUnicodeSurrogateInvalid, //!< The surrogate pair in string is invalid. + kParseErrorStringEscapeInvalid, //!< Invalid escape character in string. + kParseErrorStringMissQuotationMark, //!< Missing a closing quotation mark in string. + kParseErrorStringInvalidEncoding, //!< Invalid encoidng in string. + + kParseErrorNumberTooBig, //!< Number too big to be stored in double. + kParseErrorNumberMissFraction, //!< Miss fraction part in number. + kParseErrorNumberMissExponent //!< Miss exponent in number. +}; + + //! Function pointer type of GetParseError(). /*! This is the prototype for GetParseError_X(), where X is a locale. User can dynamically change locale in runtime, e.g.: diff --git a/include/rapidjson/reader.h b/include/rapidjson/reader.h index bd141a1..1396fc2 100644 --- a/include/rapidjson/reader.h +++ b/include/rapidjson/reader.h @@ -41,6 +41,8 @@ RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant RAPIDJSON_MULTILINEMACRO_END #endif +#include "error/error.h" // ParseErrorCode + namespace rapidjson { /////////////////////////////////////////////////////////////////////////////// @@ -55,33 +57,6 @@ enum ParseFlag { kParseValidateEncodingFlag = 2 //!< Validate encoding of JSON strings. }; -//! Error code of parsing. -enum ParseErrorCode { - kParseErrorNone = 0, //!< No error. - - kParseErrorDocumentEmpty, //!< The document is empty. - kParseErrorDocumentRootNotObjectOrArray, //!< The document root must be either object or array. - kParseErrorDocumentRootNotSingular, //!< The document root must not follow by other values. - - kParseErrorValueInvalid, //!< Invalid value. - - kParseErrorObjectMissName, //!< Missing a name for object member. - kParseErrorObjectMissColon, //!< Missing a colon after a name of object member. - kParseErrorObjectMissCommaOrCurlyBracket, //!< Missing a comma or '}' after an object member. - - kParseErrorArrayMissCommaOrSquareBracket, //!< Missing a comma or ']' after an array element. - - kParseErrorStringUnicodeEscapeInvalidHex, //!< Incorrect hex digit after \\u escape in string. - kParseErrorStringUnicodeSurrogateInvalid, //!< The surrogate pair in string is invalid. - kParseErrorStringEscapeInvalid, //!< Invalid escape character in string. - kParseErrorStringMissQuotationMark, //!< Missing a closing quotation mark in string. - kParseErrorStringInvalidEncoding, //!< Invalid encoidng in string. - - kParseErrorNumberTooBig, //!< Number too big to be stored in double. - kParseErrorNumberMissFraction, //!< Miss fraction part in number. - kParseErrorNumberMissExponent //!< Miss exponent in number. -}; - /////////////////////////////////////////////////////////////////////////////// // Handler From 3c1d4bc21d7299a6abdfde9896fd2aaedb03a9c0 Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Wed, 2 Jul 2014 01:04:33 +0200 Subject: [PATCH 06/39] reader.h: prepare "early return path" for exception support In case of a user-defined RAPIDJSON_PARSE_ERROR_NORETURN that throws an exception instead of using the Rapidjson ParseError API, the early return paths performing the stack unwinding manually can be omitted as well. This patch provides a customizable RAPIDJSON_PARSE_ERROR_EARLY_RETURN macro to remove these (then unneeded) control paths from the parsing implementation (with and without a return value). Secondly, clearing the parse stack is moved to a small helper struct that calls stack_.Clear() from its destructor. This avoids the need for the 'goto' in the ParseStream function and ensures proper cleanup even if e.g. a user-defined Allocator throws an exception. --- include/rapidjson/reader.h | 55 +++++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 19 deletions(-) diff --git a/include/rapidjson/reader.h b/include/rapidjson/reader.h index 1396fc2..00ee43e 100644 --- a/include/rapidjson/reader.h +++ b/include/rapidjson/reader.h @@ -24,6 +24,16 @@ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant #endif +#define RAPIDJSON_NOTHING /* deliberately empty */ +#ifndef RAPIDJSON_PARSE_ERROR_EARLY_RETURN +#define RAPIDJSON_PARSE_ERROR_EARLY_RETURN(value) \ + RAPIDJSON_MULTILINEMACRO_BEGIN \ + if (HasParseError()) { return value; } \ + RAPIDJSON_MULTILINEMACRO_END +#endif +#define RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID \ + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(RAPIDJSON_NOTHING) + #ifndef RAPIDJSON_PARSE_ERROR_NORETURN #define RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset) \ RAPIDJSON_MULTILINEMACRO_BEGIN \ @@ -37,7 +47,7 @@ RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant #define RAPIDJSON_PARSE_ERROR(parseErrorCode, offset) \ RAPIDJSON_MULTILINEMACRO_BEGIN \ RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset); \ - return; \ + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; \ RAPIDJSON_MULTILINEMACRO_END #endif @@ -278,28 +288,30 @@ public: parseErrorCode_ = kParseErrorNone; errorOffset_ = 0; + ClearStackOnExit scope(*this); SkipWhitespace(is); - if (is.Peek() == '\0') + if (is.Peek() == '\0') { RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentEmpty, is.Tell()); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(false); + } else { switch (is.Peek()) { case '{': ParseObject(is, handler); break; case '[': ParseArray(is, handler); break; default: RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentRootNotObjectOrArray, is.Tell()); } - if (HasParseError()) - goto out; + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(false); SkipWhitespace(is); - if (is.Peek() != '\0') + if (is.Peek() != '\0') { RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentRootNotSingular, is.Tell()); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(false); + } } - out: - stack_.Clear(); - return !HasParseError(); + return true; } //! Parse JSON text (with \ref kParseDefaultFlags) @@ -325,6 +337,16 @@ private: GenericReader(const GenericReader&); GenericReader& operator=(const GenericReader&); + void ClearStack() { stack_.Clear(); } + + // clear stack on any exit from ParseStream, e.g. due to exception + struct ClearStackOnExit { + explicit ClearStackOnExit(GenericReader& r) : r_(r) {} + ~ClearStackOnExit() { r_.ClearStack(); } + private: + GenericReader& r_; + }; + // Parse object: { string : value, ... } template void ParseObject(InputStream& is, Handler& handler) { @@ -344,8 +366,7 @@ private: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); ParseString(is, handler); - if (HasParseError()) - return; + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; SkipWhitespace(is); @@ -355,8 +376,7 @@ private: SkipWhitespace(is); ParseValue(is, handler); - if (HasParseError()) - return; + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; SkipWhitespace(is); @@ -386,8 +406,7 @@ private: for (SizeType elementCount = 0;;) { ParseValue(is, handler); - if (HasParseError()) - return; + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; ++elementCount; SkipWhitespace(is); @@ -449,7 +468,7 @@ private: codepoint -= 'a' - 10; else { RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorStringUnicodeEscapeInvalidHex, is.Tell() - 1); - return 0; + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(0); } } return codepoint; @@ -481,8 +500,7 @@ private: if (parseFlags & kParseInsituFlag) { typename InputStream::Ch *head = s.PutBegin(); ParseStringToStream(s, s); - if (HasParseError()) - return; + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; size_t length = s.PutEnd(head) - 1; RAPIDJSON_ASSERT(length <= 0xFFFFFFFF); handler.String((typename TargetEncoding::Ch*)head, SizeType(length), false); @@ -490,8 +508,7 @@ private: else { StackStream stackStream(stack_); ParseStringToStream(s, stackStream); - if (HasParseError()) - return; + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; handler.String(stack_.template Pop(stackStream.length_), stackStream.length_ - 1, true); } } From 4475521177ca6830a5f3840da1c8a794102ba6c5 Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Wed, 2 Jul 2014 01:58:49 +0200 Subject: [PATCH 07/39] GenericDocument: simplify error handling in ParseStream * unconditionally store error state of reader after parsing * clear stack after parsing by using a ClearStackOnExit scope guard --- include/rapidjson/document.h | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 45b6c3f..e397a48 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -1226,17 +1226,13 @@ public: GenericDocument& ParseStream(InputStream& is) { ValueType::SetNull(); // Remove existing root if exist GenericReader reader(&GetAllocator()); + ClearStackOnExit scope(*this); if (reader.template Parse(is, *this)) { RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object this->RawAssign(*stack_.template Pop(1)); // Add this-> to prevent issue 13. - parseErrorCode_ = kParseErrorNone; - errorOffset_ = 0; - } - else { - parseErrorCode_ = reader.GetParseErrorCode(); - errorOffset_ = reader.GetErrorOffset(); - ClearStack(); } + parseErrorCode_ = reader.GetParseErrorCode(); + errorOffset_ = reader.GetErrorOffset(); return *this; } @@ -1349,6 +1345,14 @@ public: size_t GetStackCapacity() const { return stack_.GetCapacity(); } private: + // clear stack on any exit from ParseStream, e.g. due to exception + struct ClearStackOnExit { + explicit ClearStackOnExit(GenericDocument& d) : d_(d) {} + ~ClearStackOnExit() { d_.ClearStack(); } + private: + GenericDocument& d_; + }; + // callers of the following private Handler functions template friend class GenericReader; // for parsing friend class GenericValue; // for deep copying From 2fcb999749ea793d27effee167de9952375630b7 Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Wed, 9 Jul 2014 13:55:54 +0200 Subject: [PATCH 08/39] add ParseResult --- include/rapidjson/document.h | 16 +++++++--------- include/rapidjson/error/error.h | 22 ++++++++++++++++++++++ include/rapidjson/reader.h | 31 ++++++++++++++----------------- 3 files changed, 43 insertions(+), 26 deletions(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index e397a48..b8d136d 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -1210,7 +1210,7 @@ public: /*! \param allocator Optional allocator for allocating stack memory. \param stackCapacity Initial capacity of stack in bytes. */ - GenericDocument(Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity) : stack_(allocator, stackCapacity), parseErrorCode_(kParseErrorNone), errorOffset_(0) {} + GenericDocument(Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity) : stack_(allocator, stackCapacity), parseResult_() {} //!@name Parse from stream //!@{ @@ -1227,12 +1227,11 @@ public: ValueType::SetNull(); // Remove existing root if exist GenericReader reader(&GetAllocator()); ClearStackOnExit scope(*this); - if (reader.template Parse(is, *this)) { + parseResult_ = reader.template Parse(is, *this); + if (parseResult_) { RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object this->RawAssign(*stack_.template Pop(1)); // Add this-> to prevent issue 13. } - parseErrorCode_ = reader.GetParseErrorCode(); - errorOffset_ = reader.GetErrorOffset(); return *this; } @@ -1328,13 +1327,13 @@ public: //!@{ //! Whether a parse error was occured in the last parsing. - bool HasParseError() const { return parseErrorCode_ != kParseErrorNone; } + bool HasParseError() const { return parseResult_.IsError(); } //! Get the message of parsing error. - ParseErrorCode GetParseError() const { return parseErrorCode_; } + ParseErrorCode GetParseError() const { return parseResult_.Code(); } //! Get the offset in character of the parsing error. - size_t GetErrorOffset() const { return errorOffset_; } + size_t GetErrorOffset() const { return parseResult_.Offset(); } //!@} @@ -1401,8 +1400,7 @@ private: static const size_t kDefaultStackCapacity = 1024; internal::Stack stack_; - ParseErrorCode parseErrorCode_; - size_t errorOffset_; + ParseResult parseResult_; }; //! GenericDocument with UTF8 encoding diff --git a/include/rapidjson/error/error.h b/include/rapidjson/error/error.h index af597d2..765f1c1 100644 --- a/include/rapidjson/error/error.h +++ b/include/rapidjson/error/error.h @@ -59,6 +59,28 @@ enum ParseErrorCode { kParseErrorNumberMissExponent //!< Miss exponent in number. }; +struct ParseResult { + + ParseResult() : code_(kParseErrorNone), offset_(0) {} + ParseResult(ParseErrorCode code, size_t offset) : code_(code), offset_(offset) {} + + ParseErrorCode Code() const { return code_; } + size_t Offset() const { return offset_; } + + operator bool() const { return !IsError(); } + bool IsError() const { return code_ != kParseErrorNone; } + + bool operator==(const ParseResult& that) const { return code_ == that.code_; } + bool operator==(ParseErrorCode code) const { return code_ == code; } + friend bool operator==(ParseErrorCode code, const ParseResult & err) { return code == err.code_; } + + void Clear() { Set(kParseErrorNone); } + void Set(ParseErrorCode code, size_t offset = 0) { code_ = code; offset_ = offset; } + +private: + ParseErrorCode code_; + size_t offset_; +}; //! Function pointer type of GetParseError(). /*! This is the prototype for GetParseError_X(), where X is a locale. diff --git a/include/rapidjson/reader.h b/include/rapidjson/reader.h index 00ee43e..72ebad9 100644 --- a/include/rapidjson/reader.h +++ b/include/rapidjson/reader.h @@ -38,8 +38,7 @@ RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant #define RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset) \ RAPIDJSON_MULTILINEMACRO_BEGIN \ RAPIDJSON_ASSERT(!HasParseError()); /* Error can only be assigned once */ \ - parseErrorCode_ = parseErrorCode; \ - errorOffset_ = offset; \ + parseResult_.Set(parseErrorCode,offset); \ RAPIDJSON_MULTILINEMACRO_END #endif @@ -51,7 +50,7 @@ RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant RAPIDJSON_MULTILINEMACRO_END #endif -#include "error/error.h" // ParseErrorCode +#include "error/error.h" // ParseErrorCode, ParseResult namespace rapidjson { @@ -273,7 +272,7 @@ public: /*! \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) */ - GenericReader(Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity) : stack_(allocator, stackCapacity), parseErrorCode_(kParseErrorNone), errorOffset_(0) {} + GenericReader(Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity) : stack_(allocator, stackCapacity), parseResult_() {} //! Parse JSON text. /*! \tparam parseFlags Combination of \ref ParseFlag. @@ -284,16 +283,15 @@ public: \return Whether the parsing is successful. */ template - bool Parse(InputStream& is, Handler& handler) { - parseErrorCode_ = kParseErrorNone; - errorOffset_ = 0; + ParseResult Parse(InputStream& is, Handler& handler) { + parseResult_.Clear(); ClearStackOnExit scope(*this); SkipWhitespace(is); if (is.Peek() == '\0') { RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentEmpty, is.Tell()); - RAPIDJSON_PARSE_ERROR_EARLY_RETURN(false); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); } else { switch (is.Peek()) { @@ -301,17 +299,17 @@ public: case '[': ParseArray(is, handler); break; default: RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentRootNotObjectOrArray, is.Tell()); } - RAPIDJSON_PARSE_ERROR_EARLY_RETURN(false); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); SkipWhitespace(is); if (is.Peek() != '\0') { RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentRootNotSingular, is.Tell()); - RAPIDJSON_PARSE_ERROR_EARLY_RETURN(false); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); } } - return true; + return parseResult_; } //! Parse JSON text (with \ref kParseDefaultFlags) @@ -322,15 +320,15 @@ public: \return Whether the parsing is successful. */ template - bool Parse(InputStream& is, Handler& handler) { + ParseResult Parse(InputStream& is, Handler& handler) { return Parse(is, handler); } - bool HasParseError() const { return parseErrorCode_ != kParseErrorNone; } + bool HasParseError() const { return parseResult_.IsError(); } - ParseErrorCode GetParseErrorCode() const { return parseErrorCode_; } + ParseErrorCode GetParseErrorCode() const { return parseResult_.Code(); } - size_t GetErrorOffset() const { return errorOffset_; } + size_t GetErrorOffset() const { return parseResult_.Offset(); } private: // Prohibit copy constructor & assignment operator. @@ -755,8 +753,7 @@ private: static const size_t kDefaultStackCapacity = 256; //!< Default stack capacity in bytes for storing a single decoded string. internal::Stack stack_; //!< A stack for storing decoded string temporarily during non-destructive parsing. - ParseErrorCode parseErrorCode_; - size_t errorOffset_; + ParseResult parseResult_; }; // class GenericReader //! Reader with UTF8 encoding and default allocator. From 418a5829b311c3a0b1f4dc67ea7c26f240a57b12 Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Wed, 9 Jul 2014 21:19:13 +0200 Subject: [PATCH 09/39] update documentation of ParseResult and related functions --- include/rapidjson/document.h | 6 +++--- include/rapidjson/error/error.h | 21 +++++++++++++++++++++ include/rapidjson/reader.h | 3 +++ 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index b8d136d..042a2a3 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -1326,13 +1326,13 @@ public: //!@name Handling parse errors //!@{ - //! Whether a parse error was occured in the last parsing. + //! Whether a parse error has occured in the last parsing. bool HasParseError() const { return parseResult_.IsError(); } - //! Get the message of parsing error. + //! Get the \ref ParseErrorCode of last parsing. ParseErrorCode GetParseError() const { return parseResult_.Code(); } - //! Get the offset in character of the parsing error. + //! Get the position of last parsing error in input, 0 otherwise. size_t GetErrorOffset() const { return parseResult_.Offset(); } //!@} diff --git a/include/rapidjson/error/error.h b/include/rapidjson/error/error.h index 765f1c1..829e2b9 100644 --- a/include/rapidjson/error/error.h +++ b/include/rapidjson/error/error.h @@ -59,22 +59,43 @@ enum ParseErrorCode { kParseErrorNumberMissExponent //!< Miss exponent in number. }; +//! Result of parsing (wraps ParseErrorCode) +/*! + \code + Document doc; + ParseResult ok = doc.Parse("[42]"); + if (!ok) { + fprintf(stderr, "JSON parse error: %s (%u)", + GetParseError_En(ok.Code()), ok.Offset()); + exit(EXIT_FAILURE); + } + \endcode + \see GenericReader::Parse, GenericDocument::Parse +*/ struct ParseResult { + //! Default constructor, no error. ParseResult() : code_(kParseErrorNone), offset_(0) {} + //! Constructor to set an error. ParseResult(ParseErrorCode code, size_t offset) : code_(code), offset_(offset) {} + //! Get the error code. ParseErrorCode Code() const { return code_; } + //! Get the error offset, if \ref IsError(), 0 otherwise. size_t Offset() const { return offset_; } + //! Conversion to \c bool, returns \c true, iff !\ref IsError(). operator bool() const { return !IsError(); } + //! Whether the result is an error. bool IsError() const { return code_ != kParseErrorNone; } bool operator==(const ParseResult& that) const { return code_ == that.code_; } bool operator==(ParseErrorCode code) const { return code_ == code; } friend bool operator==(ParseErrorCode code, const ParseResult & err) { return code == err.code_; } + //! Reset error code. void Clear() { Set(kParseErrorNone); } + //! Update error code and offset. void Set(ParseErrorCode code, size_t offset = 0) { code_ = code; offset_ = offset; } private: diff --git a/include/rapidjson/reader.h b/include/rapidjson/reader.h index 72ebad9..0d77316 100644 --- a/include/rapidjson/reader.h +++ b/include/rapidjson/reader.h @@ -324,10 +324,13 @@ public: return Parse(is, handler); } + //! Whether a parse error has occured in the last parsing. bool HasParseError() const { return parseResult_.IsError(); } + //! Get the \ref ParseErrorCode of last parsing. ParseErrorCode GetParseErrorCode() const { return parseResult_.Code(); } + //! Get the position of last parsing error in input, 0 otherwise. size_t GetErrorOffset() const { return parseResult_.Offset(); } private: From 33e96f866d6cdee10284ed0a3e03c27edd3c606a Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Fri, 11 Jul 2014 13:09:30 +0200 Subject: [PATCH 10/39] .travis.yml: simplify config, prepare for running 32/64 bit configurations --- .travis.yml | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2ae9443..9b9eb50 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,10 +6,12 @@ compiler: env: matrix: - - config=debug64 config_suffix=debug_x64_gmake - - config=release64 config_suffix=release_x64_gmake + - CONF=debug + - CONF=release global: - secure: "CR3yKliFhwQLX+Zs1PCRcGej6jr4DIZsCqs9x6J2NN+U9Aow0gd/uiPBho/utgm+/TmSBji5n8FO/J3ORo34q4gC6EebTEaN4gCHNXVlIBJFw9x+Gs/lML5i8F2AoweFJY334OVaOf9qC8ZVJ8Z1nEwxj77fq3gcSLzRU3pIaS8=" + # prepare for 32/64 bit multi-arch support + - BITS=64 + - secure: "CR3yKliFhwQLX+Zs1PCRcGej6jr4DIZsCqs9x6J2NN+U9Aow0gd/uiPBho/utgm+/TmSBji5n8FO/J3ORo34q4gC6EebTEaN4gCHNXVlIBJFw9x+Gs/lML5i8F2AoweFJY334OVaOf9qC8ZVJ8Z1nEwxj77fq3gcSLzRU3pIaS8=" before_install: - sudo add-apt-repository -y ppa:codegear/release @@ -22,11 +24,11 @@ before_script: - pushd build && premake4 'gmake' && popd script: - - make -C build/gmake -f test.make - - make -C build/gmake -f example.make + - make -C build/gmake -f test.make config=${CONF}${BITS} + - make -C build/gmake -f example.make config=${CONF}${BITS} - pushd bin - - ./unittest_${config_suffix} - - valgrind --leak-check=full --error-exitcode=1 ./unittest_${config_suffix} - - if [ "$config" = "release64" ]; then ./perftest_${config_suffix}; fi + - ./unittest_${CONF}_x${BITS}_gmake + - valgrind --leak-check=full --error-exitcode=1 ./unittest_${CONF}_x${BITS}_gmake + - if [ "$CONF" = "release" ]; then ./perftest_${CONF}_x${BITS}_gmake; fi - popd - ./build/travis-doxygen.sh; From 5050f18743c5786ef7d1327bf5b9688cd2e42d12 Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Fri, 11 Jul 2014 13:19:45 +0200 Subject: [PATCH 11/39] travis-doxygen.sh: add GH_TOKEN sanity check --- build/travis-doxygen.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build/travis-doxygen.sh b/build/travis-doxygen.sh index c63d783..0c817b3 100755 --- a/build/travis-doxygen.sh +++ b/build/travis-doxygen.sh @@ -81,8 +81,11 @@ gh_pages_push() { # check for secure variables [ "${TRAVIS_SECURE_ENV_VARS}" = "true" ] || \ skip "Secure variables not available, not updating GitHub pages." + # check for GitHub access token [ "${GH_TOKEN+set}" = set ] || \ skip "GitHub access token not available, not updating GitHub pages." + [ "${#GH_TOKEN}" -eq 40 ] || \ + abort "GitHub token invalid: found ${#GH_TOKEN} characters, expected 40." cd "${TRAVIS_BUILD_DIR}/doc/html"; # setup credentials (hide in "set -x" mode) From 33da6610f9e5dc8ea8f02f18d248f6cc592e220e Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Fri, 11 Jul 2014 13:29:50 +0200 Subject: [PATCH 12/39] travis-doxygen.sh: temporarily ignore failing GH page update --- build/travis-doxygen.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build/travis-doxygen.sh b/build/travis-doxygen.sh index 0c817b3..dabab33 100755 --- a/build/travis-doxygen.sh +++ b/build/travis-doxygen.sh @@ -92,7 +92,8 @@ gh_pages_push() { git config core.askpass /bin/true ( set +x ; git config credential.${GHPAGES_URL}.username "${GH_TOKEN}" ) # push to GitHub - git push origin gh-pages + git push origin gh-pages || \ + skip "GitHub pages update failed, temporarily ignored." } doxygen_install From fd9bc121553bfe1f9e7c3dd70918fe90350db799 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Sat, 12 Jul 2014 18:20:16 +0800 Subject: [PATCH 13/39] Update token --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 9b9eb50..8359d81 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,7 @@ env: global: # prepare for 32/64 bit multi-arch support - BITS=64 - - secure: "CR3yKliFhwQLX+Zs1PCRcGej6jr4DIZsCqs9x6J2NN+U9Aow0gd/uiPBho/utgm+/TmSBji5n8FO/J3ORo34q4gC6EebTEaN4gCHNXVlIBJFw9x+Gs/lML5i8F2AoweFJY334OVaOf9qC8ZVJ8Z1nEwxj77fq3gcSLzRU3pIaS8=" + - secure: " secure: "HrsaCb+N66EG1HR+LWH1u51SjaJyRwJEDzqJGYMB7LJ/bfqb9mWKF1fLvZGk46W5t7TVaXRDD5KHFx9DPWvKn4gRUVkwTHEy262ah5ORh8M6n/6VVVajeV/AYt2C0sswdkDBDO4Xq+xy5gdw3G8s1A4Inbm73pUh+6vx+7ltBbk=" before_install: - sudo add-apt-repository -y ppa:codegear/release From 75d7bfe3be4499ea4f4893b928ce83a200d9082c Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Sat, 12 Jul 2014 18:24:13 +0800 Subject: [PATCH 14/39] Revert "Update token" This reverts commit fd9bc121553bfe1f9e7c3dd70918fe90350db799. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 8359d81..9b9eb50 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,7 @@ env: global: # prepare for 32/64 bit multi-arch support - BITS=64 - - secure: " secure: "HrsaCb+N66EG1HR+LWH1u51SjaJyRwJEDzqJGYMB7LJ/bfqb9mWKF1fLvZGk46W5t7TVaXRDD5KHFx9DPWvKn4gRUVkwTHEy262ah5ORh8M6n/6VVVajeV/AYt2C0sswdkDBDO4Xq+xy5gdw3G8s1A4Inbm73pUh+6vx+7ltBbk=" + - secure: "CR3yKliFhwQLX+Zs1PCRcGej6jr4DIZsCqs9x6J2NN+U9Aow0gd/uiPBho/utgm+/TmSBji5n8FO/J3ORo34q4gC6EebTEaN4gCHNXVlIBJFw9x+Gs/lML5i8F2AoweFJY334OVaOf9qC8ZVJ8Z1nEwxj77fq3gcSLzRU3pIaS8=" before_install: - sudo add-apt-repository -y ppa:codegear/release From 75bee4cd76932108bb03968d7d076f34ab261fe9 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Sat, 12 Jul 2014 18:24:32 +0800 Subject: [PATCH 15/39] Update token --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 9b9eb50..c15e2c6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,7 @@ env: global: # prepare for 32/64 bit multi-arch support - BITS=64 - - secure: "CR3yKliFhwQLX+Zs1PCRcGej6jr4DIZsCqs9x6J2NN+U9Aow0gd/uiPBho/utgm+/TmSBji5n8FO/J3ORo34q4gC6EebTEaN4gCHNXVlIBJFw9x+Gs/lML5i8F2AoweFJY334OVaOf9qC8ZVJ8Z1nEwxj77fq3gcSLzRU3pIaS8=" + - secure: "HrsaCb+N66EG1HR+LWH1u51SjaJyRwJEDzqJGYMB7LJ/bfqb9mWKF1fLvZGk46W5t7TVaXRDD5KHFx9DPWvKn4gRUVkwTHEy262ah5ORh8M6n/6VVVajeV/AYt2C0sswdkDBDO4Xq+xy5gdw3G8s1A4Inbm73pUh+6vx+7ltBbk=" before_install: - sudo add-apt-repository -y ppa:codegear/release From ded7dc723d0609111f5c2df0ff32eeaf0894b2d9 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Sat, 12 Jul 2014 18:30:19 +0800 Subject: [PATCH 16/39] Travis config trial: using original name/email --- build/travis-doxygen.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/travis-doxygen.sh b/build/travis-doxygen.sh index dabab33..925545b 100755 --- a/build/travis-doxygen.sh +++ b/build/travis-doxygen.sh @@ -64,8 +64,8 @@ gh_pages_prepare() git clone --single-branch -b gh-pages ${GHPAGES_URL} html cd html # setup git config (with defaults) - git config user.name "${GIT_NAME-travis}" - git config user.email "${GIT_EMAIL-"travis@localhost"}" + git config user.name "${GIT_NAME}" + git config user.email "${GIT_EMAIL}" # clean working dir rm -f .git/index git clean -df From ca05d851838a54a9df9eaab10641a87a6dec2e20 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Sat, 12 Jul 2014 18:34:29 +0800 Subject: [PATCH 17/39] Revert "Travis config trial: using original name/email" This reverts commit ded7dc723d0609111f5c2df0ff32eeaf0894b2d9. --- build/travis-doxygen.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/travis-doxygen.sh b/build/travis-doxygen.sh index 925545b..dabab33 100755 --- a/build/travis-doxygen.sh +++ b/build/travis-doxygen.sh @@ -64,8 +64,8 @@ gh_pages_prepare() git clone --single-branch -b gh-pages ${GHPAGES_URL} html cd html # setup git config (with defaults) - git config user.name "${GIT_NAME}" - git config user.email "${GIT_EMAIL}" + git config user.name "${GIT_NAME-travis}" + git config user.email "${GIT_EMAIL-"travis@localhost"}" # clean working dir rm -f .git/index git clean -df From 6225092355e3087c3e73928f54cc1073c55067d6 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Sat, 12 Jul 2014 18:42:26 +0800 Subject: [PATCH 18/39] Travis config trial: git config --global --- build/travis-doxygen.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/travis-doxygen.sh b/build/travis-doxygen.sh index dabab33..ad50b3f 100755 --- a/build/travis-doxygen.sh +++ b/build/travis-doxygen.sh @@ -64,8 +64,8 @@ gh_pages_prepare() git clone --single-branch -b gh-pages ${GHPAGES_URL} html cd html # setup git config (with defaults) - git config user.name "${GIT_NAME-travis}" - git config user.email "${GIT_EMAIL-"travis@localhost"}" + git config --global user.name "${GIT_NAME-travis}" + git config --global user.email "${GIT_EMAIL-"travis@localhost"}" # clean working dir rm -f .git/index git clean -df From 3c8a9234399f03d1a4e41b0ef136593dc2667b2c Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Sat, 12 Jul 2014 23:07:41 +0800 Subject: [PATCH 19/39] Add simplereader, simplewriter examples. Also modify premake to add all projects in example folder. --- build/premake4.lua | 36 ++++++--------------------- example/simplereader/simplereader.cpp | 32 ++++++++++++++++++++++++ example/simplewriter/simplewriter.cpp | 33 ++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 29 deletions(-) create mode 100644 example/simplereader/simplereader.cpp create mode 100644 example/simplewriter/simplewriter.cpp diff --git a/build/premake4.lua b/build/premake4.lua index 0b5dbb1..4f7d77f 100644 --- a/build/premake4.lua +++ b/build/premake4.lua @@ -156,32 +156,10 @@ solution "example" configuration "gmake" buildoptions "-Werror -Wall -Wextra -Weffc++ -Wswitch-default" - project "condense" - kind "ConsoleApp" - files "../example/condense/*" - setTargetObjDir("../bin") - - project "pretty" - kind "ConsoleApp" - files "../example/pretty/*" - setTargetObjDir("../bin") - - project "prettyauto" - kind "ConsoleApp" - files "../example/prettyauto/*" - setTargetObjDir("../bin") - - project "tutorial" - kind "ConsoleApp" - files "../example/tutorial/*" - setTargetObjDir("../bin") - - project "serialize" - kind "ConsoleApp" - files "../example/serialize/*" - setTargetObjDir("../bin") - - project "simpledom" - kind "ConsoleApp" - files "../example/simpledom/*" - setTargetObjDir("../bin") + local examplepaths = os.matchdirs("../example/*") + for _, examplepath in ipairs(examplepaths) do + project(path.getname(examplepath)) + kind "ConsoleApp" + files(examplepath .. "/*") + setTargetObjDir("../bin") + end diff --git a/example/simplereader/simplereader.cpp b/example/simplereader/simplereader.cpp new file mode 100644 index 0000000..70df6ba --- /dev/null +++ b/example/simplereader/simplereader.cpp @@ -0,0 +1,32 @@ +#include "rapidjson/reader.h" +#include + +using namespace rapidjson; +using namespace std; + +struct MyHandler { + bool Null() { cout << "Null()" << endl; return true; } + bool Bool(bool b) { cout << "Bool(" << boolalpha << b << ")" << endl; return true; } + bool Int(int i) { cout << "Int(" << i << ")" << endl; return true; } + bool Uint(unsigned u) { cout << "Uint(" << u << ")" << endl; return true; } + bool Int64(int64_t i) { cout << "Int64(" << i << ")" << endl; return true; } + bool Uint64(uint64_t u) { cout << "Uint64(" << u << ")" << endl; return true; } + bool Double(double d) { cout << "Double(" << d << ")" << endl; return true; } + bool String(const char* str, SizeType length, bool copy) { + cout << "String(" << str << ", " << length << ", " << boolalpha << copy << ")" << endl; + return true; + } + bool StartObject() { cout << "StartObject()" << endl; return true; } + bool EndObject(SizeType memberCount) { cout << "EndObject(" << memberCount << ")" << endl; return true; } + bool StartArray() { cout << "StartArray()" << endl; return true; } + bool EndArray(SizeType elementCount) { cout << "EndArray(" << elementCount << ")" << endl; return true; } +}; + +void main() { + const char json[] = " { \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3, 4] } "; + + MyHandler handler; + Reader reader; + StringStream ss(json); + reader.Parse(ss, handler); +} diff --git a/example/simplewriter/simplewriter.cpp b/example/simplewriter/simplewriter.cpp new file mode 100644 index 0000000..98da2cb --- /dev/null +++ b/example/simplewriter/simplewriter.cpp @@ -0,0 +1,33 @@ +#include "rapidjson/writer.h" +#include "rapidjson/stringbuffer.h" +#include + +using namespace rapidjson; +using namespace std; + +void main() { + StringBuffer s; + Writer writer(s); + + writer.StartObject(); + writer.String("hello"); + writer.String("world"); + writer.String("t"); + writer.Bool(true); + writer.String("f"); + writer.Bool(false); + writer.String("n"); + writer.Null(); + writer.String("i"); + writer.Uint(123); + writer.String("pi"); + writer.Double(3.1416); + writer.String("a"); + writer.StartArray(); + for (unsigned i = 0; i < 4; i++) + writer.Uint(i); + writer.EndArray(); + writer.EndObject(); + + cout << s.GetString() << endl; +} From 05811e73d4607afa6d6372bfa5479e873916d447 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Sat, 12 Jul 2014 23:29:23 +0800 Subject: [PATCH 20/39] Update SAX documentation --- doc/sax.md | 64 +++++++++++++++++++++++++++++------------------------- 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/doc/sax.md b/doc/sax.md index c0a1af0..119af2d 100644 --- a/doc/sax.md +++ b/doc/sax.md @@ -50,7 +50,7 @@ EndArray(4) EndObject(7) ~~~~~~~~~~ -These events can be easily match up with the JSON, except some event parameters need further explanation. Let's see the code which produces exactly the same output as above: +These events can be easily match up with the JSON, except some event parameters need further explanation. Let's see the simplereader example which produces exactly the same output as above: ~~~~~~~~~~cpp #include "rapidjson/reader.h" @@ -60,26 +60,28 @@ using namespace rapidjson; using namespace std; struct MyHandler { - void Null() { cout << "Null()" << endl; } - void Bool(bool b) { cout << "Bool(" << (b ? "true" : "false") << ")" << endl; } - void Int(int i) { cout << "Int(" << i << ")" << endl; } - void Uint(unsigned u) { cout << "Uint(" << u << ")" << endl; } - void Int64(int64_t i) { cout << "Int64(" << i << ")" << endl; } - void Uint64(uint64_t u) { cout << "Uint64(" << u << ")" << endl; } - void Double(double d) { { cout << "Double(" << d << ")" << endl; } - void String(const char* str, SizeType length, bool copy) { - cout << "String(" << str << ", " << length << ", " << (b ? "true" : "false") << ")" << endl; } - void StartObject() { cout << "StartObject()" << endl; } - void EndObject(SizeType memberCount) { cout << "EndObject(" << memberCount << ")" << endl; } - void StartArray() { cout << "StartArray()" << endl; } - void EndArray(SizeType elementCount) { cout << "EndArray(" << elementCount << ")" << endl; } + bool Null() { cout << "Null()" << endl; return true; } + bool Bool(bool b) { cout << "Bool(" << boolalpha << b << ")" << endl; return true; } + bool Int(int i) { cout << "Int(" << i << ")" << endl; return true; } + bool Uint(unsigned u) { cout << "Uint(" << u << ")" << endl; return true; } + bool Int64(int64_t i) { cout << "Int64(" << i << ")" << endl; return true; } + bool Uint64(uint64_t u) { cout << "Uint64(" << u << ")" << endl; return true; } + bool Double(double d) { cout << "Double(" << d << ")" << endl; return true; } + bool String(const char* str, SizeType length, bool copy) { + cout << "String(" << str << ", " << length << ", " << boolalpha << copy << ")" << endl; + return true; + } + bool StartObject() { cout << "StartObject()" << endl; return true; } + bool EndObject(SizeType memberCount) { cout << "EndObject(" << memberCount << ")" << endl; return true; } + bool StartArray() { cout << "StartArray()" << endl; return true; } + bool EndArray(SizeType elementCount) { cout << "EndArray(" << elementCount << ")" << endl; return true; } }; void main() { - const char* json = "..."; + const char json[] = " { \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3, 4] } "; MyHandler handler; - Reader reader; + Reader reader; StringStream ss(json); reader.Parse(ss, handler); } @@ -93,20 +95,18 @@ As the previous example showed, user needs to implement a handler, which consume ~~~~~~~~~~cpp concept Handler { - typename Ch; - - void Null(); - void Bool(bool b); - void Int(int i); - void Uint(unsigned i); - void Int64(int64_t i); - void Uint64(uint64_t i); - void Double(double d); - void String(const Ch* str, SizeType length, bool copy); - void StartObject(); - void EndObject(SizeType memberCount); - void StartArray(); - void EndArray(SizeType elementCount); + bool Null(); + bool Bool(bool b); + bool Int(int i); + bool Uint(unsigned i); + bool Int64(int64_t i); + bool Uint64(uint64_t i); + bool Double(double d); + bool String(const Ch* str, SizeType length, bool copy); + bool StartObject(); + bool EndObject(SizeType memberCount); + bool StartArray(); + bool EndArray(SizeType elementCount); }; ~~~~~~~~~~ @@ -122,6 +122,10 @@ When the `Reader` encounters the beginning of an object, it calls `StartObject() Array is similar to object but simpler. At the beginning of an array, the `Reader` calls `BeginArary()`. If there is elements, it calls functions according to the types of element. Similarly, in the last call `EndArray(SizeType elementCount)`, the parameter `elementCount` is just an aid for the handler. +Every handler functions returns a `bool`. Normally it should returns `true`. If the handler encounters an error, it can return `false` to notify event publisher to stop further processing. + +For example, when we parse a JSON with `Reader` and the handler detected that the JSON does not conform to the required schema, then the handler can return `false` and let the `Reader` stop further parsing. And the `Reader` will be in error state with error code `kParseErrorTermination`. + ## GenericReader {#GenericReader} As mentioned before, `Reader` is a typedef of a template class `GenericReader`: From 75cee948d44876f22f7215b9bd64733c3d7fee51 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Sat, 12 Jul 2014 23:41:01 +0800 Subject: [PATCH 21/39] Fixes main() return should be int in new examples --- example/simplereader/simplereader.cpp | 4 +++- example/simplewriter/simplewriter.cpp | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/example/simplereader/simplereader.cpp b/example/simplereader/simplereader.cpp index 70df6ba..ed2bd3b 100644 --- a/example/simplereader/simplereader.cpp +++ b/example/simplereader/simplereader.cpp @@ -22,11 +22,13 @@ struct MyHandler { bool EndArray(SizeType elementCount) { cout << "EndArray(" << elementCount << ")" << endl; return true; } }; -void main() { +int main() { const char json[] = " { \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3, 4] } "; MyHandler handler; Reader reader; StringStream ss(json); reader.Parse(ss, handler); + + return 0; } diff --git a/example/simplewriter/simplewriter.cpp b/example/simplewriter/simplewriter.cpp index 98da2cb..98e5b2c 100644 --- a/example/simplewriter/simplewriter.cpp +++ b/example/simplewriter/simplewriter.cpp @@ -5,7 +5,7 @@ using namespace rapidjson; using namespace std; -void main() { +int main() { StringBuffer s; Writer writer(s); @@ -30,4 +30,6 @@ void main() { writer.EndObject(); cout << s.GetString() << endl; + + return 0; } From c69610239a0ec1cd0e9631dcf297b25b60de9362 Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Sat, 12 Jul 2014 18:44:31 +0200 Subject: [PATCH 22/39] error.h: add kParseErrorTermination (from #61) --- include/rapidjson/error/error.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/include/rapidjson/error/error.h b/include/rapidjson/error/error.h index 829e2b9..b183917 100644 --- a/include/rapidjson/error/error.h +++ b/include/rapidjson/error/error.h @@ -52,11 +52,13 @@ enum ParseErrorCode { kParseErrorStringUnicodeSurrogateInvalid, //!< The surrogate pair in string is invalid. kParseErrorStringEscapeInvalid, //!< Invalid escape character in string. kParseErrorStringMissQuotationMark, //!< Missing a closing quotation mark in string. - kParseErrorStringInvalidEncoding, //!< Invalid encoidng in string. + kParseErrorStringInvalidEncoding, //!< Invalid encoding in string. kParseErrorNumberTooBig, //!< Number too big to be stored in double. kParseErrorNumberMissFraction, //!< Miss fraction part in number. - kParseErrorNumberMissExponent //!< Miss exponent in number. + kParseErrorNumberMissExponent, //!< Miss exponent in number. + + kParseErrorTermination //!< Parsing was terminated. }; //! Result of parsing (wraps ParseErrorCode) From ff5713faf032a6a1210c4d7313f45bfe3254e135 Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Sat, 12 Jul 2014 18:45:49 +0200 Subject: [PATCH 23/39] error/en.h: fix typo, add termination string --- include/rapidjson/error/en.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/rapidjson/error/en.h b/include/rapidjson/error/en.h index 81637ee..6317957 100644 --- a/include/rapidjson/error/en.h +++ b/include/rapidjson/error/en.h @@ -32,12 +32,14 @@ inline const RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErro case kParseErrorStringUnicodeSurrogateInvalid: return RAPIDJSON_ERROR_STRING("The surrogate pair in string is invalid."); case kParseErrorStringEscapeInvalid: return RAPIDJSON_ERROR_STRING("Invalid escape character in string."); case kParseErrorStringMissQuotationMark: return RAPIDJSON_ERROR_STRING("Missing a closing quotation mark in string."); - case kParseErrorStringInvalidEncoding: return RAPIDJSON_ERROR_STRING("Invalid encoidng in string."); + case kParseErrorStringInvalidEncoding: return RAPIDJSON_ERROR_STRING("Invalid encoding in string."); case kParseErrorNumberTooBig: return RAPIDJSON_ERROR_STRING("Number too big to be stored in double."); case kParseErrorNumberMissFraction: return RAPIDJSON_ERROR_STRING("Miss fraction part in number."); case kParseErrorNumberMissExponent: return RAPIDJSON_ERROR_STRING("Miss exponent in number."); + case kParseErrorTermination: return RAPIDJSON_ERROR_STRING("Terminate parsing due to Handler error."); + default: return RAPIDJSON_ERROR_STRING("Unknown error."); } From 025c516f827845c2510f761b203fce7ae3b669a1 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Sun, 13 Jul 2014 01:00:03 +0800 Subject: [PATCH 24/39] Add kParseErrorTermination English error message. --- include/rapidjson/error/en.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/rapidjson/error/en.h b/include/rapidjson/error/en.h index 81637ee..291823d 100644 --- a/include/rapidjson/error/en.h +++ b/include/rapidjson/error/en.h @@ -38,6 +38,8 @@ inline const RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErro case kParseErrorNumberMissFraction: return RAPIDJSON_ERROR_STRING("Miss fraction part in number."); case kParseErrorNumberMissExponent: return RAPIDJSON_ERROR_STRING("Miss exponent in number."); + case kParseErrorTermination: return RAPIDJSON_ERROR_STRING("Parsing was terminated."); + default: return RAPIDJSON_ERROR_STRING("Unknown error."); } From e78654235ff5435568dc34b42645d6e17d4ba837 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Sun, 13 Jul 2014 01:06:22 +0800 Subject: [PATCH 25/39] Revert "Add kParseErrorTermination English error message." This reverts commit 025c516f827845c2510f761b203fce7ae3b669a1. --- include/rapidjson/error/en.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/rapidjson/error/en.h b/include/rapidjson/error/en.h index 291823d..81637ee 100644 --- a/include/rapidjson/error/en.h +++ b/include/rapidjson/error/en.h @@ -38,8 +38,6 @@ inline const RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErro case kParseErrorNumberMissFraction: return RAPIDJSON_ERROR_STRING("Miss fraction part in number."); case kParseErrorNumberMissExponent: return RAPIDJSON_ERROR_STRING("Miss exponent in number."); - case kParseErrorTermination: return RAPIDJSON_ERROR_STRING("Parsing was terminated."); - default: return RAPIDJSON_ERROR_STRING("Unknown error."); } From 9e3ed44e859a8768665aeb47ee70b6d3ff9b4f5d Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Sun, 13 Jul 2014 01:10:25 +0800 Subject: [PATCH 26/39] Suppress VS2013 warnings due to #64 --- include/rapidjson/document.h | 2 ++ include/rapidjson/reader.h | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 41dff72..4448600 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -1350,6 +1350,8 @@ private: explicit ClearStackOnExit(GenericDocument& d) : d_(d) {} ~ClearStackOnExit() { d_.ClearStack(); } private: + ClearStackOnExit(const ClearStackOnExit&); + ClearStackOnExit& operator=(const ClearStackOnExit&); GenericDocument& d_; }; diff --git a/include/rapidjson/reader.h b/include/rapidjson/reader.h index 39afcd8..022a2de 100644 --- a/include/rapidjson/reader.h +++ b/include/rapidjson/reader.h @@ -348,6 +348,8 @@ private: ~ClearStackOnExit() { r_.ClearStack(); } private: GenericReader& r_; + ClearStackOnExit(const ClearStackOnExit&); + ClearStackOnExit& operator=(const ClearStackOnExit&); }; // Parse object: { string : value, ... } @@ -611,7 +613,7 @@ private: } // Parse int: zero / ( digit1-9 *DIGIT ) - unsigned i; + unsigned i = 0; bool try64bit = false; if (s.Peek() == '0') { i = 0; From a5c1324da99074b8c581a6a136341eb3edbe0faa Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Sun, 13 Jul 2014 01:17:38 +0800 Subject: [PATCH 27/39] Add messagereader example --- example/messagereader/messagereader.cpp | 86 +++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 example/messagereader/messagereader.cpp diff --git a/example/messagereader/messagereader.cpp b/example/messagereader/messagereader.cpp new file mode 100644 index 0000000..344b170 --- /dev/null +++ b/example/messagereader/messagereader.cpp @@ -0,0 +1,86 @@ +// Reading a message JSON with Reader (SAX-style API). +// The JSON should be an object with key-string pairs. + +#include "rapidjson/reader.h" +#include "rapidjson/error/en.h" +#include +#include +#include + +using namespace std; +using namespace rapidjson; + +typedef map MessageMap; + +struct MessageHandler : public BaseReaderHandler<> { + MessageHandler() : state_(kExpectObjectStart) {} + + bool StartObject() { + switch (state_) { + case kExpectObjectStart: + state_ = kExpectNameOrObjectEnd; + return true; + default: + return false; + } + } + + bool String(const char* str, SizeType length, bool) { + switch (state_) { + case kExpectNameOrObjectEnd: + name_ = string(str, length); + state_ = kExpectValue; + return true; + case kExpectValue: + messages_.insert(MessageMap::value_type(name_, string(str, length))); + state_ = kExpectNameOrObjectEnd; + return true; + default: + return false; + } + } + + bool EndObject(SizeType) { return state_ == kExpectNameOrObjectEnd; } + + bool Default() { return false; } // All other events are invalid. + + MessageMap messages_; + enum State { + kExpectObjectStart, + kExpectNameOrObjectEnd, + kExpectValue, + }state_; + std::string name_; +}; + +void ParseMessages(const char* json, MessageMap& messages) { + Reader reader; + MessageHandler handler; + StringStream ss(json); + if (reader.Parse(ss, handler)) + messages.swap(handler.messages_); // Only change it if success. + else { + ParseErrorCode e = reader.GetParseErrorCode(); + size_t o = reader.GetErrorOffset(); + cout << "Error: " << GetParseError_En(e) << endl;; + cout << " at offset " << o << " near '" << string(json).substr(o, 10) << "...'" << endl; + } +} + +int main() { + MessageMap messages; + + const char* json1 = "{ \"greeting\" : \"Hello!\", \"farewell\" : \"bye-bye!\" }"; + cout << json1 << endl; + ParseMessages(json1, messages); + + for (MessageMap::const_iterator itr = messages.begin(); itr != messages.end(); ++itr) + cout << itr->first << ": " << itr->second << endl; + + cout << endl << "Parse a JSON with invalid schema." << endl; + const char* json2 = "{ \"greeting\" : \"Hello!\", \"farewell\" : \"bye-bye!\", \"foo\" : {} }"; + cout << json2 << endl; + ParseMessages(json2, messages); + + return 0; +} From 4575f9343152bbf1f2d11cde7dd902b925f7a469 Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Sat, 12 Jul 2014 19:02:20 +0200 Subject: [PATCH 28/39] MacOS X doesn't have '/bin/true' --- build/travis-doxygen.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/travis-doxygen.sh b/build/travis-doxygen.sh index ad50b3f..1282a6f 100755 --- a/build/travis-doxygen.sh +++ b/build/travis-doxygen.sh @@ -89,7 +89,7 @@ gh_pages_push() { cd "${TRAVIS_BUILD_DIR}/doc/html"; # setup credentials (hide in "set -x" mode) - git config core.askpass /bin/true + git config core.askpass 'bash -c ":"' ( set +x ; git config credential.${GHPAGES_URL}.username "${GH_TOKEN}" ) # push to GitHub git push origin gh-pages || \ From 2875b576169ad285b4ad65fa68c21da8b87b3ba5 Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Sat, 12 Jul 2014 19:02:56 +0200 Subject: [PATCH 29/39] Revert "Travis config trial: git config --global" Avoids messing with global config when running the script manually on a developer machine. This reverts commit 6225092355e3087c3e73928f54cc1073c55067d6. --- build/travis-doxygen.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/travis-doxygen.sh b/build/travis-doxygen.sh index 1282a6f..9f4b49d 100755 --- a/build/travis-doxygen.sh +++ b/build/travis-doxygen.sh @@ -64,8 +64,8 @@ gh_pages_prepare() git clone --single-branch -b gh-pages ${GHPAGES_URL} html cd html # setup git config (with defaults) - git config --global user.name "${GIT_NAME-travis}" - git config --global user.email "${GIT_EMAIL-"travis@localhost"}" + git config user.name "${GIT_NAME-travis}" + git config user.email "${GIT_EMAIL-"travis@localhost"}" # clean working dir rm -f .git/index git clean -df From c1afdc8df21d4cfb3282cfd7013250647cc6f205 Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Sat, 12 Jul 2014 19:56:30 +0200 Subject: [PATCH 30/39] travis-doxygen.sh: allow overriding GitHub repo from environment --- .travis.yml | 1 + build/travis-doxygen.sh | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index c15e2c6..2aab9a8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,6 +9,7 @@ env: - CONF=debug - CONF=release global: + - GITHUB_REPO='miloyip/rapidjson' # prepare for 32/64 bit multi-arch support - BITS=64 - secure: "HrsaCb+N66EG1HR+LWH1u51SjaJyRwJEDzqJGYMB7LJ/bfqb9mWKF1fLvZGk46W5t7TVaXRDD5KHFx9DPWvKn4gRUVkwTHEy262ah5ORh8M6n/6VVVajeV/AYt2C0sswdkDBDO4Xq+xy5gdw3G8s1A4Inbm73pUh+6vx+7ltBbk=" diff --git a/build/travis-doxygen.sh b/build/travis-doxygen.sh index 9f4b49d..894fca3 100755 --- a/build/travis-doxygen.sh +++ b/build/travis-doxygen.sh @@ -10,8 +10,8 @@ DOXYGEN_TAR=${DOXYGEN_VER}.linux.bin.tar.gz DOXYGEN_URL="http://ftp.stack.nl/pub/users/dimitri/${DOXYGEN_TAR}" DOXYGEN_BIN="/usr/local/bin/doxygen" -GHPAGES_REPO="miloyip/rapidjson" -GHPAGES_URL="https://github.com/${GHPAGES_REPO}" +: ${GITHUB_REPO:="miloyip/rapidjson"} +GITHUB_URL="https://github.com/${GITHUB_REPO}" skip() { echo "$@" 1>&2 @@ -61,7 +61,7 @@ gh_pages_prepare() cd "${TRAVIS_BUILD_DIR}/doc"; [ ! -d "html" ] || \ abort "Doxygen target directory already exists." - git clone --single-branch -b gh-pages ${GHPAGES_URL} html + git clone --single-branch -b gh-pages ${GITHUB_URL} html cd html # setup git config (with defaults) git config user.name "${GIT_NAME-travis}" @@ -90,7 +90,7 @@ gh_pages_push() { cd "${TRAVIS_BUILD_DIR}/doc/html"; # setup credentials (hide in "set -x" mode) git config core.askpass 'bash -c ":"' - ( set +x ; git config credential.${GHPAGES_URL}.username "${GH_TOKEN}" ) + ( set +x ; git config credential.${GITHUB_URL}.username "${GH_TOKEN}" ) # push to GitHub git push origin gh-pages || \ skip "GitHub pages update failed, temporarily ignored." From be478343a5438f10d862345a384f6d17bfc74c49 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Sun, 13 Jul 2014 03:21:38 +0800 Subject: [PATCH 31/39] Update SAX documentation: writer, techniques --- doc/sax.md | 212 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 183 insertions(+), 29 deletions(-) diff --git a/doc/sax.md b/doc/sax.md index 119af2d..a0e928c 100644 --- a/doc/sax.md +++ b/doc/sax.md @@ -50,7 +50,7 @@ EndArray(4) EndObject(7) ~~~~~~~~~~ -These events can be easily match up with the JSON, except some event parameters need further explanation. Let's see the simplereader example which produces exactly the same output as above: +These events can be easily match up with the JSON, except some event parameters need further explanation. Let's see the `simplereader` example which produces exactly the same output as above: ~~~~~~~~~~cpp #include "rapidjson/reader.h" @@ -170,8 +170,117 @@ If an error occurs during parsing, it will return `false`. User can also calls ` # Writer {#Writer} +`Reader` converts (parses) JSON into events. `Writer` does exactly the opposite. It converts events into JSON. + +`Writer` is very easy to use. If your application only need to converts some data into JSON, it may be a good choice of using `Writer` directly, instead of building a `Document` and then stringifying it with a `Writer`. + +In `simplewriter` example, we do exactly the reverse of `simplereader`. + +~~~~~~~~~~cpp +#include "rapidjson/writer.h" +#include "rapidjson/stringbuffer.h" +#include + +using namespace rapidjson; +using namespace std; + +void main() { + StringBuffer s; + Writer writer(s); + + writer.StartObject(); + writer.String("hello"); + writer.String("world"); + writer.String("t"); + writer.Bool(true); + writer.String("f"); + writer.Bool(false); + writer.String("n"); + writer.Null(); + writer.String("i"); + writer.Uint(123); + writer.String("pi"); + writer.Double(3.1416); + writer.String("a"); + writer.StartArray(); + for (unsigned i = 0; i < 4; i++) + writer.Uint(i); + writer.EndArray(); + writer.EndObject(); + + cout << s.GetString() << endl; +} +~~~~~~~~~~ + +~~~~~~~~~~ +{"hello":"world","t":true,"f":false,"n":null,"i":123,"pi":3.1416,"a":[0,1,2,3]} +~~~~~~~~~~ + +There is two `String()` overloads. One is the same as defined in handler concept with 3 parameters. It can handle string with null characters. Another one is the simpler version used in the above example. + +Note that, the example code does not pass any parameters in `EndArray()` and `EndObject()`. An `SizeType` can be passed but it will be simply ignored by `Writer`. + +You may doubt that, + +> "why not just using `sprintf()` or `std::stringstream` to build a JSON?" + +There are various reasons: +1. `Writer` must output a well-formed JSON. If there is incorrect event sequence (e.g. `Int()` just after `StartObject()`), it generates assertion fail in debug mode. +2. `Writer::String()` can handle string escaping (e.g. converting code point `U+000A` to `\n`) and Unicode transcoding. +3. `Writer` handles number output consistently. For example, user can set precision for `Double()`. +3. `Writer` implements the event handler concept. It can be used to handle events from `Reader`, `Document` or other event publisher. +4. `Writer` can be optimized for different platforms. + +Anyway, using `Writer` API is even simpler than generating a JSON by ad hoc methods. + +## Template {#WriterTemplate} + +`Writer` has a minor design difference to `Reader`. `Writer` is a template class, not a typedef. There is no `GenericWriter`. The following is the declaration. + +~~~~~~~~~~cpp +namespace rapidjson { + +template, typename TargetEncoding = UTF8<>, typename Allocator = CrtAllocator<> > +class Writer { +public: + Writer(OutputStream& os, Allocator* allocator = 0, size_t levelDepth = kDefaultLevelDepth) +// ... +}; + +} // namespace rapidjson +~~~~~~~~~~ + +The `OutputStream` template parameter is the type of output stream. It cannot be deduced and must be specified by user. + +The `SourceEncoding` template parameter specifies the encoding to be used in `String(const Ch*, ...)`. + +The `TargetEncoding` template parameter specifies the encoding in the output stream. + +The last one, `Allocator` is the type of allocator, which is used for allocating internal data structure (a stack). + +Besides, the constructor of `Writer` has a `levelDepth` parameter. This parameter affects the initial memory allocated for storing information per hierarchy level. + +## Precision (#WriterPrecision) +When using `Double()`, the precision of output can be specified, for example: + +~~~~~~~~~~cpp +writer.SetDoublePrecision(4); +writer.StartArary(); +writer.Double(3.14159265359); +writer.EndArray(); +~~~~~~~~~~ +~~~~~~~~~~ +[3.1416] +~~~~~~~~~~ + ## PrettyWriter {#PrettyWriter} +While the output of `Writer` is the most condensed JSON without white-spaces, suitable for network transfer or storage, it is not easily readable by human. + +Therefore, RapidJSON provides a `PrettyWriter`, which adds indentation and line feeds in the output. + +The usage of `PrettyWriter` is exactly the same as `Writer`, expect that `PrettyWriter` provides a `SetIndent(Ch indentChar, unsigned indentCharCount)` function. The default is 4 spaces. + # Techniques {#Techniques} ## Parsing JSON to Custom Data Structure {#CustomDataStructure} @@ -180,52 +289,59 @@ If an error occurs during parsing, it will return `false`. User can also calls ` User may uses `Reader` to build other data structures directly. This eliminates building of DOM, thus reducing memory and improving performance. -Example: +In the following `messagereader` example, `ParseMessages()` parses a JSON which should be an object with key-string pairs. + ~~~~~~~~~~cpp -// Note: Ad hoc, not yet tested. +#include "rapidjson/reader.h" +#include "rapidjson/error/en.h" +#include +#include +#include + using namespace std; using namespace rapidjson; typedef map MessageMap; -struct MessageHandler : public GenericBaseHandler<> { - MessageHandler() : mState(kExpectStart) { - } - - bool Default() { - return false; +struct MessageHandler : public BaseReaderHandler<> { + MessageHandler() : state_(kExpectObjectStart) { } bool StartObject() { - if (!kBeforeStart) + switch (state_) { + case kExpectObjectStart: + state_ = kExpectNameOrObjectEnd; + return true; + default: return false; - mState = mExpectName; - return true; + } } - bool String(const Ch* str, SizeType length, bool copy) { - if (mState == kExpectName) { + bool String(const char* str, SizeType length, bool) { + switch (state_) { + case kExpectNameOrObjectEnd: name_ = string(str, length); + state_ = kExpectValue; return true; - } - else if (mState == kExpectValue) { + case kExpectValue: messages_.insert(MessageMap::value_type(name_, string(str, length))); + state_ = kExpectNameOrObjectEnd; return true; - } - else + default: return false; + } } - bool EndObject() { - return mState == kExpectName; - } + bool EndObject(SizeType) { return state_ == kExpectNameOrObjectEnd; } + + bool Default() { return false; } // All other events are invalid. MessageMap messages_; enum State { kExpectObjectStart, - kExpectName, + kExpectNameOrObjectEnd, kExpectValue, - }mState; + }state_; std::string name_; }; @@ -234,19 +350,57 @@ void ParseMessages(const char* json, MessageMap& messages) { MessageHandler handler; StringStream ss(json); if (reader.Parse(ss, handler)) - messages.swap(handler.messages_); + messages.swap(handler.messages_); // Only change it if success. + else { + ParseErrorCode e = reader.GetParseErrorCode(); + size_t o = reader.GetErrorOffset(); + cout << "Error: " << GetParseError_En(e) << endl;; + cout << " at offset " << o << " near '" << string(json).substr(o, 10) << "...'" << endl; + } } -main() { +int main() { MessageMap messages; - ParseMessages("{ \"greeting\" : \"Hello!\", \"farewell\" : \"bye-bye!\" }", messages); + + const char* json1 = "{ \"greeting\" : \"Hello!\", \"farewell\" : \"bye-bye!\" }"; + cout << json1 << endl; + ParseMessages(json1, messages); + + for (MessageMap::const_iterator itr = messages.begin(); itr != messages.end(); ++itr) + cout << itr->first << ": " << itr->second << endl; + + cout << endl << "Parse a JSON with invalid schema." << endl; + const char* json2 = "{ \"greeting\" : \"Hello!\", \"farewell\" : \"bye-bye!\", \"foo\" : {} }"; + cout << json2 << endl; + ParseMessages(json2, messages); + + return 0; } ~~~~~~~~~~ -~~~~~~~~~~cpp -// Parse a NxM array -const char* json = "[3, 4, [1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]" ~~~~~~~~~~ +{ "greeting" : "Hello!", "farewell" : "bye-bye!" } +farewell: bye-bye! +greeting: Hello! + +Parse a JSON with invalid schema. +{ "greeting" : "Hello!", "farewell" : "bye-bye!", "foo" : {} } +Error: Terminate parsing due to Handler error. + at offset 59 near '} }...' +~~~~~~~~~~ + +The first JSON (`json1`) was successfully parsed into `MessageMap`. Since it is a `std::map`, the print out are sorted by the key, which is different from the JSON's order. + +In the second JSON (`json2`), `foo`'s value is an empty object. As it is an object, `MessageHandler::StartObject()` will be called. However, at that moment `state_ = kExpectValue`, so that function returns `false` and cause the parsing process be terminated. The error code is `kParseErrorTermination`. ## Filtering of JSON {#Filtering} +As mentioned earlier, `Writer` can handle the events published by `Reader`. `condense` example simply set a `Writer` as handler of a `Reader`, so it can remove all white-spaces in JSON. `pretty` example uses the same relationship, but replacing `Writer` by `PrettyWriter`. So `pretty` can be used to reformat a JSON with indentation and line feed. + +Actually, we can add intermediate layer(s) to filter the contents of JSON via these SAX-style API. For example, `capitalize` example capitalize all strings in a JSON. + +~~~~~~~~~~cpp +TODO +~~~~~~~~~~ + +More complicated filters can be developed. However, since SAX-style API can only provide information about a single event at a time, user may need to book-keeping the contextual information (e.g. the path from root value, storage of other related values). Some processing may be easier to be implemented in DOM than SAX. \ No newline at end of file From a73ed78fd6d8c6cc1e565e0085b6e4a8711183b7 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Sun, 13 Jul 2014 03:28:09 +0800 Subject: [PATCH 32/39] Suppress effc++ warning in messagereader --- example/messagereader/messagereader.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/example/messagereader/messagereader.cpp b/example/messagereader/messagereader.cpp index 344b170..f5317c2 100644 --- a/example/messagereader/messagereader.cpp +++ b/example/messagereader/messagereader.cpp @@ -12,8 +12,13 @@ using namespace rapidjson; typedef map MessageMap; +#if defined(__GNUC__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + struct MessageHandler : public BaseReaderHandler<> { - MessageHandler() : state_(kExpectObjectStart) {} + MessageHandler() : messages_(), state_(kExpectObjectStart), name_() {} bool StartObject() { switch (state_) { @@ -53,6 +58,10 @@ struct MessageHandler : public BaseReaderHandler<> { std::string name_; }; +#if defined(__GNUC__) +RAPIDJSON_DIAG_POP +#endif + void ParseMessages(const char* json, MessageMap& messages) { Reader reader; MessageHandler handler; From c203f682e9a2b063e3dd34d3e52c184cf14e13d9 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Sun, 13 Jul 2014 03:51:18 +0800 Subject: [PATCH 33/39] Add capitalize example --- example/capitalize/capitalize.cpp | 61 +++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 example/capitalize/capitalize.cpp diff --git a/example/capitalize/capitalize.cpp b/example/capitalize/capitalize.cpp new file mode 100644 index 0000000..25bef3a --- /dev/null +++ b/example/capitalize/capitalize.cpp @@ -0,0 +1,61 @@ +// JSON condenser exmaple + +// This example parses JSON from stdin with validation, +// and re-output the JSON content to stdout with all string capitalized, and without whitespace. + +#include "rapidjson/reader.h" +#include "rapidjson/writer.h" +#include "rapidjson/filereadstream.h" +#include "rapidjson/filewritestream.h" +#include "rapidjson/error/en.h" +#include +#include + +using namespace rapidjson; + +template +struct CapitalizeFilter { + CapitalizeFilter(OutputHandler& out) : out_(out), buffer_() {} + + bool Null() { return out_.Null(); } + bool Bool(bool b) { return out_.Bool(b); } + bool Int(int i) { return out_.Int(i); } + bool Uint(unsigned u) { return out_.Uint(u); } + bool Int64(int64_t i) { return out_.Int64(i); } + bool Uint64(uint64_t u) { return out_.Uint64(u); } + bool Double(double d) { return out_.Double(d); } + bool String(const char* str, SizeType length, bool) { + buffer_.clear(); + for (SizeType i = 0; i < length; i++) + buffer_.push_back(std::toupper(str[i])); + return out_.String(&buffer_.front(), length, true); // true = output handler need to copy the string + } + bool StartObject() { return out_.StartObject(); } + bool EndObject(SizeType memberCount) { return out_.EndObject(memberCount); } + bool StartArray() { return out_.StartArray(); } + bool EndArray(SizeType elementCount) { return out_.EndArray(elementCount); } + + OutputHandler& out_; + std::vector buffer_; +}; + +int main(int, char*[]) { + // Prepare JSON reader and input stream. + Reader reader; + char readBuffer[65536]; + FileReadStream is(stdin, readBuffer, sizeof(readBuffer)); + + // Prepare JSON writer and output stream. + char writeBuffer[65536]; + FileWriteStream os(stdout, writeBuffer, sizeof(writeBuffer)); + Writer writer(os); + + // JSON reader parse from the input stream and let writer generate the output. + CapitalizeFilter > filter(writer); + if (!reader.Parse(is, filter)) { + fprintf(stderr, "\nError(%u): %s\n", (unsigned)reader.GetErrorOffset(), GetParseError_En(reader.GetParseErrorCode())); + return 1; + } + + return 0; +} From 4998f1ca9ebce2d9456026ec0f193584152c108c Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Sun, 13 Jul 2014 04:01:11 +0800 Subject: [PATCH 34/39] Add capitalize example to sax documentation --- doc/sax.md | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 72 insertions(+), 1 deletion(-) diff --git a/doc/sax.md b/doc/sax.md index a0e928c..df3ff53 100644 --- a/doc/sax.md +++ b/doc/sax.md @@ -400,7 +400,78 @@ As mentioned earlier, `Writer` can handle the events published by `Reader`. `con Actually, we can add intermediate layer(s) to filter the contents of JSON via these SAX-style API. For example, `capitalize` example capitalize all strings in a JSON. ~~~~~~~~~~cpp -TODO +#include "rapidjson/reader.h" +#include "rapidjson/writer.h" +#include "rapidjson/filereadstream.h" +#include "rapidjson/filewritestream.h" +#include "rapidjson/error/en.h" +#include +#include + +using namespace rapidjson; + +template +struct CapitalizeFilter { + CapitalizeFilter(OutputHandler& out) : out_(out), buffer_() { + } + + bool Null() { return out_.Null(); } + bool Bool(bool b) { return out_.Bool(b); } + bool Int(int i) { return out_.Int(i); } + bool Uint(unsigned u) { return out_.Uint(u); } + bool Int64(int64_t i) { return out_.Int64(i); } + bool Uint64(uint64_t u) { return out_.Uint64(u); } + bool Double(double d) { return out_.Double(d); } + bool String(const char* str, SizeType length, bool) { + buffer_.clear(); + for (SizeType i = 0; i < length; i++) + buffer_.push_back(std::toupper(str[i])); + return out_.String(&buffer_.front(), length, true); // true = output handler need to copy the string + } + bool StartObject() { return out_.StartObject(); } + bool EndObject(SizeType memberCount) { return out_.EndObject(memberCount); } + bool StartArray() { return out_.StartArray(); } + bool EndArray(SizeType elementCount) { return out_.EndArray(elementCount); } + + OutputHandler& out_; + std::vector buffer_; +}; + +int main(int, char*[]) { + // Prepare JSON reader and input stream. + Reader reader; + char readBuffer[65536]; + FileReadStream is(stdin, readBuffer, sizeof(readBuffer)); + + // Prepare JSON writer and output stream. + char writeBuffer[65536]; + FileWriteStream os(stdout, writeBuffer, sizeof(writeBuffer)); + Writer writer(os); + + // JSON reader parse from the input stream and let writer generate the output. + CapitalizeFilter > filter(writer); + if (!reader.Parse(is, filter)) { + fprintf(stderr, "\nError(%u): %s\n", (unsigned)reader.GetErrorOffset(), GetParseError_En(reader.GetParseErrorCode())); + return 1; + } + + return 0; +} +~~~~~~~~~~ + +Note that, it is incorrect to simply capitalize the JSON as a string. For example: +~~~~~~~~~~js +["Hello\\nWorld"] +~~~~~~~~~~ + +Simply capitalizing the whole JSON would contain incorrect escape character: +~~~~~~~~~~js +["HELLO\\NWORLD"] +~~~~~~~~~~ + +The correct result by `capitalize`: +~~~~~~~~~~js +["HELLO\\nWORLD"] ~~~~~~~~~~ More complicated filters can be developed. However, since SAX-style API can only provide information about a single event at a time, user may need to book-keeping the contextual information (e.g. the path from root value, storage of other related values). Some processing may be easier to be implemented in DOM than SAX. \ No newline at end of file From 24a9cced453042a33b7b3b0d60bb30a8dadbe247 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Sun, 13 Jul 2014 12:04:37 +0800 Subject: [PATCH 35/39] Suppress VC2013 warning in capitalize --- example/capitalize/capitalize.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/example/capitalize/capitalize.cpp b/example/capitalize/capitalize.cpp index 25bef3a..69b07d8 100644 --- a/example/capitalize/capitalize.cpp +++ b/example/capitalize/capitalize.cpp @@ -37,6 +37,10 @@ struct CapitalizeFilter { OutputHandler& out_; std::vector buffer_; + +private: + CapitalizeFilter(const CapitalizeFilter&); + CapitalizeFilter& operator=(const CapitalizeFilter&); }; int main(int, char*[]) { From 4dafa2ac42a6d5c772fe7ea76156c682ea547ef9 Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Sat, 12 Jul 2014 20:47:55 +0200 Subject: [PATCH 36/39] travis-doxygen.sh: try to explicitly implement an empty GIT_ASKPASS --- build/travis-doxygen.sh | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/build/travis-doxygen.sh b/build/travis-doxygen.sh index 894fca3..214ba21 100755 --- a/build/travis-doxygen.sh +++ b/build/travis-doxygen.sh @@ -11,7 +11,12 @@ DOXYGEN_URL="http://ftp.stack.nl/pub/users/dimitri/${DOXYGEN_TAR}" DOXYGEN_BIN="/usr/local/bin/doxygen" : ${GITHUB_REPO:="miloyip/rapidjson"} -GITHUB_URL="https://github.com/${GITHUB_REPO}" +GITHUB_CLONE="git://github.com" +GITHUB_PUSH="https://ssl.sorgh.de/authdump.php" +GITHUB_URL="${GITHUB_PUSH}/${GITHUB_REPO}" + +# if not set, ignore password +: ${GIT_ASKPASS:="${TRAVIS_BUILD_DIR}/gh_ignore_askpass.sh"} skip() { echo "$@" 1>&2 @@ -61,7 +66,8 @@ gh_pages_prepare() cd "${TRAVIS_BUILD_DIR}/doc"; [ ! -d "html" ] || \ abort "Doxygen target directory already exists." - git clone --single-branch -b gh-pages ${GITHUB_URL} html + git --version + git clone --single-branch -b gh-pages "${GITHUB_CLONE}/${GITHUB_REPO}" html cd html # setup git config (with defaults) git config user.name "${GIT_NAME-travis}" @@ -77,6 +83,15 @@ gh_pages_commit() { git diff-index --quiet HEAD || git commit -m "Automatic doxygen build"; } +gh_setup_askpass() { + cat > ${GIT_ASKPASS} < Date: Sun, 13 Jul 2014 11:48:37 +0200 Subject: [PATCH 37/39] travis-doxygen.sh: directly setup .git-credentials Instead of fiddling with the non-working GIT_ASKPASS/core.askpass setup, simply store the required Git credentials directly in ~/.git-credentials, which is used by Git's "store" credential helper. Works on https://travis-ci.org/pah/rapidjson. --- build/travis-doxygen.sh | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/build/travis-doxygen.sh b/build/travis-doxygen.sh index 214ba21..239afb3 100755 --- a/build/travis-doxygen.sh +++ b/build/travis-doxygen.sh @@ -11,12 +11,12 @@ DOXYGEN_URL="http://ftp.stack.nl/pub/users/dimitri/${DOXYGEN_TAR}" DOXYGEN_BIN="/usr/local/bin/doxygen" : ${GITHUB_REPO:="miloyip/rapidjson"} -GITHUB_CLONE="git://github.com" -GITHUB_PUSH="https://ssl.sorgh.de/authdump.php" -GITHUB_URL="${GITHUB_PUSH}/${GITHUB_REPO}" +GITHUB_HOST="github.com" +GITHUB_CLONE="git://${GITHUB_HOST}/${GITHUB_REPO}" +GITHUB_URL="https://${GITHUB_HOST}/${GITHUB_PUSH-${GITHUB_REPO}}" # if not set, ignore password -: ${GIT_ASKPASS:="${TRAVIS_BUILD_DIR}/gh_ignore_askpass.sh"} +#GIT_ASKPASS="${TRAVIS_BUILD_DIR}/gh_ignore_askpass.sh" skip() { echo "$@" 1>&2 @@ -67,7 +67,7 @@ gh_pages_prepare() [ ! -d "html" ] || \ abort "Doxygen target directory already exists." git --version - git clone --single-branch -b gh-pages "${GITHUB_CLONE}/${GITHUB_REPO}" html + git clone --single-branch -b gh-pages "${GITHUB_CLONE}" html cd html # setup git config (with defaults) git config user.name "${GIT_NAME-travis}" @@ -105,11 +105,11 @@ gh_pages_push() { cd "${TRAVIS_BUILD_DIR}/doc/html"; # setup credentials (hide in "set -x" mode) git remote set-url --push origin "${GITHUB_URL}" - [ -x "${GIT_ASKPASS}" ] || gh_setup_askpass - echo ${GIT_ASKPASS} - export GIT_ASKPASS git config credential.helper 'store' - ( set +x ; git config credential.username "${GH_TOKEN}" ) + # ( set +x ; git config credential.username "${GH_TOKEN}" ) + ( set +x ; \ + echo "https://${GH_TOKEN}:@${GITHUB_HOST}" > ${HOME}/.git-credentials ; \ + chmod go-rw ${HOME}/.git-credentials ) # push to GitHub git push origin gh-pages || \ skip "GitHub pages update failed, temporarily ignored." From 8d8ce9e9c5580df7aa49665b174004f35a88b72a Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Sun, 13 Jul 2014 11:54:24 +0200 Subject: [PATCH 38/39] travis-doxygen.sh: don't ignore Doxygen push error --- build/travis-doxygen.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/build/travis-doxygen.sh b/build/travis-doxygen.sh index 239afb3..74fa528 100755 --- a/build/travis-doxygen.sh +++ b/build/travis-doxygen.sh @@ -111,8 +111,7 @@ gh_pages_push() { echo "https://${GH_TOKEN}:@${GITHUB_HOST}" > ${HOME}/.git-credentials ; \ chmod go-rw ${HOME}/.git-credentials ) # push to GitHub - git push origin gh-pages || \ - skip "GitHub pages update failed, temporarily ignored." + git push origin gh-pages } doxygen_install From 2e0b3de8d68758b2866fff5f047c893b8a1c4290 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Sun, 13 Jul 2014 21:44:08 +0800 Subject: [PATCH 39/39] Minor update of SAX doc, add about IsComplete() and Reset() --- doc/sax.md | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/doc/sax.md b/doc/sax.md index df3ff53..9ad2e4b 100644 --- a/doc/sax.md +++ b/doc/sax.md @@ -24,7 +24,7 @@ For example, here is a JSON. } ~~~~~~~~~~ -While a `Reader` parses the JSON, it will publish the following events to the handler sequentially: +While a `Reader` parses this JSON, it publishes the following events to the handler sequentially: ~~~~~~~~~~ BeginObject() @@ -50,7 +50,7 @@ EndArray(4) EndObject(7) ~~~~~~~~~~ -These events can be easily match up with the JSON, except some event parameters need further explanation. Let's see the `simplereader` example which produces exactly the same output as above: +These events can be easily matched with the JSON, except some event parameters need further explanation. Let's see the `simplereader` example which produces exactly the same output as above: ~~~~~~~~~~cpp #include "rapidjson/reader.h" @@ -91,10 +91,10 @@ Note that, RapidJSON uses template to statically bind the `Reader` type and the ## Handler {#Handler} -As the previous example showed, user needs to implement a handler, which consumes the events (function calls) from `Reader`. The handler concept has the following member type and member functions. +As the previous example showed, user needs to implement a handler, which consumes the events (function calls) from `Reader`. The handler must contain the following member functions. ~~~~~~~~~~cpp -concept Handler { +class Handler { bool Null(); bool Bool(bool b); bool Int(int i); @@ -172,7 +172,7 @@ If an error occurs during parsing, it will return `false`. User can also calls ` `Reader` converts (parses) JSON into events. `Writer` does exactly the opposite. It converts events into JSON. -`Writer` is very easy to use. If your application only need to converts some data into JSON, it may be a good choice of using `Writer` directly, instead of building a `Document` and then stringifying it with a `Writer`. +`Writer` is very easy to use. If your application only need to converts some data into JSON, it may be a good choice to use `Writer` directly, instead of building a `Document` and then stringifying it with a `Writer`. In `simplewriter` example, we do exactly the reverse of `simplereader`. @@ -220,16 +220,14 @@ There is two `String()` overloads. One is the same as defined in handler concept Note that, the example code does not pass any parameters in `EndArray()` and `EndObject()`. An `SizeType` can be passed but it will be simply ignored by `Writer`. -You may doubt that, - -> "why not just using `sprintf()` or `std::stringstream` to build a JSON?" +You may doubt that, why not just using `sprintf()` or `std::stringstream` to build a JSON? There are various reasons: 1. `Writer` must output a well-formed JSON. If there is incorrect event sequence (e.g. `Int()` just after `StartObject()`), it generates assertion fail in debug mode. 2. `Writer::String()` can handle string escaping (e.g. converting code point `U+000A` to `\n`) and Unicode transcoding. 3. `Writer` handles number output consistently. For example, user can set precision for `Double()`. -3. `Writer` implements the event handler concept. It can be used to handle events from `Reader`, `Document` or other event publisher. -4. `Writer` can be optimized for different platforms. +4. `Writer` implements the event handler concept. It can be used to handle events from `Reader`, `Document` or other event publisher. +5. `Writer` can be optimized for different platforms. Anyway, using `Writer` API is even simpler than generating a JSON by ad hoc methods. @@ -261,6 +259,7 @@ The last one, `Allocator` is the type of allocator, which is used for allocating Besides, the constructor of `Writer` has a `levelDepth` parameter. This parameter affects the initial memory allocated for storing information per hierarchy level. ## Precision (#WriterPrecision) + When using `Double()`, the precision of output can be specified, for example: ~~~~~~~~~~cpp @@ -281,6 +280,12 @@ Therefore, RapidJSON provides a `PrettyWriter`, which adds indentation and line The usage of `PrettyWriter` is exactly the same as `Writer`, expect that `PrettyWriter` provides a `SetIndent(Ch indentChar, unsigned indentCharCount)` function. The default is 4 spaces. +## Completeness and Reset {#CompletenessReset} + +A `Writer` can only output a single JSON, which can be either an object or array at the root. Once the last matching `EndObject()` or `EndArray()` event is handled, the output JSON is well-formed and complete. User can detect this state by calling `Writer::IsComplete()`. + +When a JSON is complete, the `Writer` cannot accept any new events. Otherwise the output will be invalid (i.e. having more than one root). To reuse the `Writer` object, user can call `Writer::Reset(OutputStream& os)` to reset all internal states of the `Writer` with a new output stream. + # Techniques {#Techniques} ## Parsing JSON to Custom Data Structure {#CustomDataStructure} @@ -389,7 +394,7 @@ Error: Terminate parsing due to Handler error. at offset 59 near '} }...' ~~~~~~~~~~ -The first JSON (`json1`) was successfully parsed into `MessageMap`. Since it is a `std::map`, the print out are sorted by the key, which is different from the JSON's order. +The first JSON (`json1`) was successfully parsed into `MessageMap`. Since `MessageMap` is a `std::map`, the printing order are sorted by the key. This order is different from the JSON's order. In the second JSON (`json2`), `foo`'s value is an empty object. As it is an object, `MessageHandler::StartObject()` will be called. However, at that moment `state_ = kExpectValue`, so that function returns `false` and cause the parsing process be terminated. The error code is `kParseErrorTermination`. @@ -460,18 +465,18 @@ int main(int, char*[]) { ~~~~~~~~~~ Note that, it is incorrect to simply capitalize the JSON as a string. For example: -~~~~~~~~~~js -["Hello\\nWorld"] +~~~~~~~~~~ +["Hello\nWorld"] ~~~~~~~~~~ Simply capitalizing the whole JSON would contain incorrect escape character: -~~~~~~~~~~js -["HELLO\\NWORLD"] +~~~~~~~~~~ +["HELLO\NWORLD"] ~~~~~~~~~~ The correct result by `capitalize`: -~~~~~~~~~~js -["HELLO\\nWORLD"] +~~~~~~~~~~ +["HELLO\nWORLD"] ~~~~~~~~~~ -More complicated filters can be developed. However, since SAX-style API can only provide information about a single event at a time, user may need to book-keeping the contextual information (e.g. the path from root value, storage of other related values). Some processing may be easier to be implemented in DOM than SAX. \ No newline at end of file +More complicated filters can be developed. However, since SAX-style API can only provide information about a single event at a time, user may need to book-keeping the contextual information (e.g. the path from root value, storage of other related values). Some processing may be easier to be implemented in DOM than SAX.