diff --git a/.travis.yml b/.travis.yml index 2ae9443..2aab9a8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,10 +6,13 @@ 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=" + - GITHUB_REPO='miloyip/rapidjson' + # prepare for 32/64 bit multi-arch support + - BITS=64 + - secure: "HrsaCb+N66EG1HR+LWH1u51SjaJyRwJEDzqJGYMB7LJ/bfqb9mWKF1fLvZGk46W5t7TVaXRDD5KHFx9DPWvKn4gRUVkwTHEy262ah5ORh8M6n/6VVVajeV/AYt2C0sswdkDBDO4Xq+xy5gdw3G8s1A4Inbm73pUh+6vx+7ltBbk=" before_install: - sudo add-apt-repository -y ppa:codegear/release @@ -22,11 +25,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; 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/build/travis-doxygen.sh b/build/travis-doxygen.sh index c63d783..74fa528 100755 --- a/build/travis-doxygen.sh +++ b/build/travis-doxygen.sh @@ -10,8 +10,13 @@ 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_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" 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 ${GHPAGES_URL} html + git --version + git clone --single-branch -b gh-pages "${GITHUB_CLONE}" html cd html # setup git config (with defaults) git config user.name "${GIT_NAME-travis}" @@ -77,17 +83,33 @@ gh_pages_commit() { git diff-index --quiet HEAD || git commit -m "Automatic doxygen build"; } +gh_setup_askpass() { + cat > ${GIT_ASKPASS} < ${HOME}/.git-credentials ; \ + chmod go-rw ${HOME}/.git-credentials ) # push to GitHub git push origin gh-pages } diff --git a/doc/sax.md b/doc/sax.md index 6442b6b..9ad2e4b 100644 --- a/doc/sax.md +++ b/doc/sax.md @@ -1,11 +1,482 @@ # 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} -### PrettyWriter +`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 this JSON, it publishes 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 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" +#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); +} +~~~~~~~~~~ + +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 must contain the following member functions. + +~~~~~~~~~~cpp +class Handler { + 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); +}; +~~~~~~~~~~ + +`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. + +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`: + +~~~~~~~~~~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} + +`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 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`. + +~~~~~~~~~~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()`. +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. + +## 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. + +## 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} + +`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. + +In the following `messagereader` example, `ParseMessages()` parses a JSON which should be an object with key-string pairs. + +~~~~~~~~~~cpp +#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; +} +~~~~~~~~~~ + +~~~~~~~~~~ +{ "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 `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`. + +## 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 +#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: +~~~~~~~~~~ +["Hello\nWorld"] +~~~~~~~~~~ + +Simply capitalizing the whole JSON would contain incorrect escape character: +~~~~~~~~~~ +["HELLO\NWORLD"] +~~~~~~~~~~ + +The correct result by `capitalize`: +~~~~~~~~~~ +["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. diff --git a/example/capitalize/capitalize.cpp b/example/capitalize/capitalize.cpp new file mode 100644 index 0000000..69b07d8 --- /dev/null +++ b/example/capitalize/capitalize.cpp @@ -0,0 +1,65 @@ +// 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_; + +private: + CapitalizeFilter(const CapitalizeFilter&); + CapitalizeFilter& operator=(const CapitalizeFilter&); +}; + +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; +} diff --git a/example/messagereader/messagereader.cpp b/example/messagereader/messagereader.cpp new file mode 100644 index 0000000..f5317c2 --- /dev/null +++ b/example/messagereader/messagereader.cpp @@ -0,0 +1,95 @@ +// 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; + +#if defined(__GNUC__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +struct MessageHandler : public BaseReaderHandler<> { + MessageHandler() : messages_(), state_(kExpectObjectStart), name_() {} + + 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_; +}; + +#if defined(__GNUC__) +RAPIDJSON_DIAG_POP +#endif + +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; +} diff --git a/example/simplereader/simplereader.cpp b/example/simplereader/simplereader.cpp new file mode 100644 index 0000000..ed2bd3b --- /dev/null +++ b/example/simplereader/simplereader.cpp @@ -0,0 +1,34 @@ +#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; } +}; + +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 new file mode 100644 index 0000000..98e5b2c --- /dev/null +++ b/example/simplewriter/simplewriter.cpp @@ -0,0 +1,35 @@ +#include "rapidjson/writer.h" +#include "rapidjson/stringbuffer.h" +#include + +using namespace rapidjson; +using namespace std; + +int 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; + + return 0; +} diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 8406598..4448600 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 @@ -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: @@ -1210,7 +1211,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 //!@{ @@ -1226,16 +1227,11 @@ public: GenericDocument& ParseStream(InputStream& is) { ValueType::SetNull(); // Remove existing root if exist GenericReader reader(&GetAllocator()); - if (reader.template Parse(is, *this)) { + ClearStackOnExit scope(*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_ = kParseErrorNone; - errorOffset_ = 0; - } - else { - parseErrorCode_ = reader.GetParseErrorCode(); - errorOffset_ = reader.GetErrorOffset(); - ClearStack(); } return *this; } @@ -1331,14 +1327,14 @@ public: //!@name Handling parse errors //!@{ - //! Whether a parse error was occured in the last parsing. - bool HasParseError() const { return parseErrorCode_ != kParseErrorNone; } + //! Whether a parse error has occured in the last parsing. + bool HasParseError() const { return parseResult_.IsError(); } - //! Get the message of parsing error. - ParseErrorCode GetParseError() const { return parseErrorCode_; } + //! Get the \ref ParseErrorCode of last parsing. + ParseErrorCode GetParseError() const { return parseResult_.Code(); } - //! Get the offset in character of the parsing error. - size_t GetErrorOffset() const { return errorOffset_; } + //! Get the position of last parsing error in input, 0 otherwise. + size_t GetErrorOffset() const { return parseResult_.Offset(); } //!@} @@ -1349,38 +1345,51 @@ 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: + ClearStackOnExit(const ClearStackOnExit&); + ClearStackOnExit& operator=(const ClearStackOnExit&); + GenericDocument& d_; + }; + // callers of the following private Handler functions template friend class GenericReader; // for parsing 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: @@ -1397,8 +1406,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/en.h b/include/rapidjson/error/en.h index a40c5c7..e9120c5 100644 --- a/include/rapidjson/error/en.h +++ b/include/rapidjson/error/en.h @@ -32,12 +32,13 @@ 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."); case kParseErrorUnspecificSyntaxError: return RAPIDJSON_ERROR_STRING("Unspecific syntax error."); default: diff --git a/include/rapidjson/error/error.h b/include/rapidjson/error/error.h index ba45e7e..e5c2b1b 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,85 @@ 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 encoding in string. + + kParseErrorNumberTooBig, //!< Number too big to be stored in double. + kParseErrorNumberMissFraction, //!< Miss fraction part in number. + kParseErrorNumberMissExponent, //!< Miss exponent in number. + + kParseErrorTermination, //!< Parsing was terminated. + kParseErrorUnspecificSyntaxError //!< Unspecific syntax error. +}; + +//! 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: + ParseErrorCode code_; + size_t offset_; +}; + //! 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/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 fe7bedb..454cb6e 100644 --- a/include/rapidjson/reader.h +++ b/include/rapidjson/reader.h @@ -24,12 +24,21 @@ 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 \ RAPIDJSON_ASSERT(!HasParseError()); /* Error can only be assigned once */ \ - parseErrorCode_ = parseErrorCode; \ - errorOffset_ = offset; \ + parseResult_.Set(parseErrorCode,offset); \ RAPIDJSON_MULTILINEMACRO_END #endif @@ -37,10 +46,12 @@ 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 +#include "error/error.h" // ParseErrorCode, ParseResult + namespace rapidjson { /////////////////////////////////////////////////////////////////////////////// @@ -56,56 +67,29 @@ enum ParseFlag { kParseIterativeFlag = 4 //!< Iterative(constant complexity in terms of function call stack size) parsing. }; -//! 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 encoding in string. - - kParseErrorNumberTooBig, //!< Number too big to be stored in double. - kParseErrorNumberMissFraction, //!< Miss fraction part in number. - kParseErrorNumberMissExponent, //!< Miss exponent in number. - - kParseErrorUnspecificSyntaxError //!< Unspecific syntax error. -}; - /////////////////////////////////////////////////////////////////////////////// // Handler /*! \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 */ @@ -120,19 +104,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(); } }; /////////////////////////////////////////////////////////////////////////////// @@ -291,7 +275,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. @@ -302,35 +286,37 @@ public: \return Whether the parsing is successful. */ template - bool Parse(InputStream& is, Handler& handler) { - parseErrorCode_ = kParseErrorNone; - errorOffset_ = 0; - + ParseResult Parse(InputStream& is, Handler& handler) { if (parseFlags & kParseIterativeFlag) return IterativeParse(is, handler); + parseResult_.Clear(); + + 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(parseResult_); + } 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(parseResult_); SkipWhitespace(is); - if (is.Peek() != '\0') + if (is.Peek() != '\0') { RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentRootNotSingular, is.Tell()); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + } } - out: - stack_.Clear(); - return !HasParseError(); + return parseResult_; } //! Parse JSON text (with \ref kParseDefaultFlags) @@ -341,32 +327,51 @@ 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; } + //! Whether a parse error has occured in the last parsing. + bool HasParseError() const { return parseResult_.IsError(); } - ParseErrorCode GetParseErrorCode() const { return parseErrorCode_; } + //! Get the \ref ParseErrorCode of last parsing. + ParseErrorCode GetParseErrorCode() const { return parseResult_.Code(); } - size_t GetErrorOffset() const { return errorOffset_; } + //! Get the position of last parsing error in input, 0 otherwise. + size_t GetErrorOffset() const { return parseResult_.Offset(); } private: // Prohibit copy constructor & assignment operator. 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_; + ClearStackOnExit(const ClearStackOnExit&); + ClearStackOnExit& operator=(const ClearStackOnExit&); + }; + // Parse object: { string : value, ... } template 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; } @@ -375,8 +380,7 @@ private: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); ParseString(is, handler); - if (HasParseError()) - return; + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; SkipWhitespace(is); @@ -386,16 +390,19 @@ private: SkipWhitespace(is); ParseValue(is, handler); - if (HasParseError()) - return; + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; SkipWhitespace(is); ++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()); } } @@ -406,26 +413,33 @@ 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; } for (SizeType elementCount = 0;;) { ParseValue(is, handler); - if (HasParseError()) - return; + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; ++elementCount; SkipWhitespace(is); 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()); } } @@ -436,8 +450,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); } @@ -447,10 +463,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 @@ -458,8 +476,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); } @@ -480,7 +500,7 @@ private: codepoint -= 'a' - 10; else { RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorStringUnicodeEscapeInvalidHex, is.Tell() - 1); - return 0; + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(0); } } return codepoint; @@ -512,18 +532,18 @@ 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); + 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); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + if (!handler.String(stack_.template Pop(stackStream.length_), stackStream.length_ - 1, true)) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell()); } } @@ -598,7 +618,7 @@ private: } // Parse int: zero / ( digit1-9 *DIGIT ) - unsigned i; + unsigned i = 0; bool try64bit = false; if (s.Peek() == '0') { i = 0; @@ -725,6 +745,7 @@ private: } // Finish parsing, call event according to the type of number. + bool cont = true; if (useDouble) { int expSum = exp + expFrac; if (expSum < -308) { @@ -735,22 +756,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 @@ -992,6 +1015,7 @@ private: IterativeParsingState Transit(IterativeParsingState src, IterativeParsingToken token, IterativeParsingState dst, InputStream& is, Handler& handler) { int c = 0; IterativeParsingState n; + bool hr; switch (dst) { case IterativeParsingStartState: @@ -1021,10 +1045,16 @@ private: *stack_.template Push(1) = 0; // Call handler if (dst == IterativeParsingObjectInitialState) - handler.StartObject(); + hr = handler.StartObject(); else - handler.StartArray(); - return dst; + hr = handler.StartArray(); + // On handler short circuits the parsing. + if (!hr) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); + return IterativeParsingErrorState; + } + else + return dst; case IterativeParsingMemberKeyState: ParseString(is, handler); @@ -1077,8 +1107,14 @@ private: if (n == IterativeParsingStartState) n = IterativeParsingFinishState; // Call handler - handler.EndObject(c); - return n; + hr = handler.EndObject(c); + // On handler short circuits the parsing. + if (!hr) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); + return IterativeParsingErrorState; + } + else + return n; case IterativeParsingArrayFinishState: is.Take(); @@ -1093,8 +1129,14 @@ private: if (n == IterativeParsingStartState) n = IterativeParsingFinishState; // Call handler - handler.EndArray(c); - return n; + hr = handler.EndArray(c); + // On handler short circuits the parsing. + if (!hr) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); + return IterativeParsingErrorState; + } + else + return n; default: RAPIDJSON_ASSERT(false); @@ -1135,7 +1177,9 @@ private: } template - bool IterativeParse(InputStream& is, Handler& handler) { + ParseResult IterativeParse(InputStream& is, Handler& handler) { + parseResult_.Clear(); + ClearStackOnExit scope(*this); IterativeParsingState state = IterativeParsingStartState; SkipWhitespace(is); @@ -1156,15 +1200,13 @@ private: // Handle the end of file. if (state != IterativeParsingFinishState) HandleError(state, is); - - stack_.Clear(); - return state == IterativeParsingFinishState; + + return parseResult_; } 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. diff --git a/include/rapidjson/writer.h b/include/rapidjson/writer.h index 9652000..f3b7567 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 be896a6..0b52e8a 100644 --- a/test/perftest/rapidjsontest.cpp +++ b/test/perftest/rapidjsontest.cpp @@ -197,8 +197,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 72dfece..45cbeaa 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() { ADD_FAILURE(); return false; } // 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_; }; @@ -756,42 +757,42 @@ struct IterativeParsingReaderHandler { } bool IsNullTriggered; - void Null() { IsNullTriggered = true; } + bool Null() { IsNullTriggered = true; return true; } bool IsBoolTriggered; - void Bool(bool) { IsBoolTriggered = true; } + bool Bool(bool) { IsBoolTriggered = true; return true; } bool IsIntTriggered; - void Int(int) { IsIntTriggered = true; } + bool Int(int) { IsIntTriggered = true; return true; } bool IsUintTriggered; - void Uint(unsigned) { IsUintTriggered = true; } + bool Uint(unsigned) { IsUintTriggered = true; return true; } bool IsInt64Triggered; - void Int64(int64_t) { IsInt64Triggered = true; } + bool Int64(int64_t) { IsInt64Triggered = true; return true; } bool IsUint64Triggered; - void Uint64(uint64_t) { IsUint64Triggered = true; } + bool Uint64(uint64_t) { IsUint64Triggered = true; return true; } bool IsDoubleTriggered; - void Double(double) { IsDoubleTriggered = true; } + bool Double(double) { IsDoubleTriggered = true; return true; } bool IsStringTriggered; - void String(const Ch*, SizeType, bool) { IsStringTriggered = true; } + bool String(const Ch*, SizeType, bool) { IsStringTriggered = true; return true; } bool IsStartObjectTriggered; - void StartObject() { IsStartObjectTriggered = true; } + bool StartObject() { IsStartObjectTriggered = true; return true; } bool IsEndObjectTriggered; SizeType MemberCount; - void EndObject(SizeType c) { IsEndObjectTriggered = true; MemberCount = c; } + bool EndObject(SizeType c) { IsEndObjectTriggered = true; MemberCount = c; return true; } bool IsStartArrayTriggered; - void StartArray() { IsStartArrayTriggered = true; } + bool StartArray() { IsStartArrayTriggered = true; return true; } bool IsEndArrayTriggered; SizeType ElementCount; - void EndArray(SizeType c) { IsEndArrayTriggered = true; ElementCount = c; } + bool EndArray(SizeType c) { IsEndArrayTriggered = true; ElementCount = c; return true; } }; #define ITERATIVE_PARSING_PREPARE_STATE_UNTIL(text, pos)\ 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());