Commit d4da07c5 authored by thebusytypist's avatar thebusytypist

Merge upstream/master.

parents face7240 2e0b3de8
...@@ -6,10 +6,13 @@ compiler: ...@@ -6,10 +6,13 @@ compiler:
env: env:
matrix: matrix:
- config=debug64 config_suffix=debug_x64_gmake - CONF=debug
- config=release64 config_suffix=release_x64_gmake - CONF=release
global: 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: before_install:
- sudo add-apt-repository -y ppa:codegear/release - sudo add-apt-repository -y ppa:codegear/release
...@@ -22,11 +25,11 @@ before_script: ...@@ -22,11 +25,11 @@ before_script:
- pushd build && premake4 'gmake' && popd - pushd build && premake4 'gmake' && popd
script: script:
- make -C build/gmake -f test.make - make -C build/gmake -f test.make config=${CONF}${BITS}
- make -C build/gmake -f example.make - make -C build/gmake -f example.make config=${CONF}${BITS}
- pushd bin - pushd bin
- ./unittest_${config_suffix} - ./unittest_${CONF}_x${BITS}_gmake
- valgrind --leak-check=full --error-exitcode=1 ./unittest_${config_suffix} - valgrind --leak-check=full --error-exitcode=1 ./unittest_${CONF}_x${BITS}_gmake
- if [ "$config" = "release64" ]; then ./perftest_${config_suffix}; fi - if [ "$CONF" = "release" ]; then ./perftest_${CONF}_x${BITS}_gmake; fi
- popd - popd
- ./build/travis-doxygen.sh; - ./build/travis-doxygen.sh;
...@@ -156,32 +156,10 @@ solution "example" ...@@ -156,32 +156,10 @@ solution "example"
configuration "gmake" configuration "gmake"
buildoptions "-Werror -Wall -Wextra -Weffc++ -Wswitch-default" buildoptions "-Werror -Wall -Wextra -Weffc++ -Wswitch-default"
project "condense" local examplepaths = os.matchdirs("../example/*")
kind "ConsoleApp" for _, examplepath in ipairs(examplepaths) do
files "../example/condense/*" project(path.getname(examplepath))
setTargetObjDir("../bin") kind "ConsoleApp"
files(examplepath .. "/*")
project "pretty" setTargetObjDir("../bin")
kind "ConsoleApp" end
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")
...@@ -10,8 +10,13 @@ DOXYGEN_TAR=${DOXYGEN_VER}.linux.bin.tar.gz ...@@ -10,8 +10,13 @@ DOXYGEN_TAR=${DOXYGEN_VER}.linux.bin.tar.gz
DOXYGEN_URL="http://ftp.stack.nl/pub/users/dimitri/${DOXYGEN_TAR}" DOXYGEN_URL="http://ftp.stack.nl/pub/users/dimitri/${DOXYGEN_TAR}"
DOXYGEN_BIN="/usr/local/bin/doxygen" DOXYGEN_BIN="/usr/local/bin/doxygen"
GHPAGES_REPO="miloyip/rapidjson" : ${GITHUB_REPO:="miloyip/rapidjson"}
GHPAGES_URL="https://github.com/${GHPAGES_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"
skip() { skip() {
echo "$@" 1>&2 echo "$@" 1>&2
...@@ -61,7 +66,8 @@ gh_pages_prepare() ...@@ -61,7 +66,8 @@ gh_pages_prepare()
cd "${TRAVIS_BUILD_DIR}/doc"; cd "${TRAVIS_BUILD_DIR}/doc";
[ ! -d "html" ] || \ [ ! -d "html" ] || \
abort "Doxygen target directory already exists." 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 cd html
# setup git config (with defaults) # setup git config (with defaults)
git config user.name "${GIT_NAME-travis}" git config user.name "${GIT_NAME-travis}"
...@@ -77,17 +83,33 @@ gh_pages_commit() { ...@@ -77,17 +83,33 @@ gh_pages_commit() {
git diff-index --quiet HEAD || git commit -m "Automatic doxygen build"; git diff-index --quiet HEAD || git commit -m "Automatic doxygen build";
} }
gh_setup_askpass() {
cat > ${GIT_ASKPASS} <<EOF
#!/bin/bash
echo
exit 0
EOF
chmod a+x "$GIT_ASKPASS"
}
gh_pages_push() { gh_pages_push() {
# check for secure variables # check for secure variables
[ "${TRAVIS_SECURE_ENV_VARS}" = "true" ] || \ [ "${TRAVIS_SECURE_ENV_VARS}" = "true" ] || \
skip "Secure variables not available, not updating GitHub pages." skip "Secure variables not available, not updating GitHub pages."
# check for GitHub access token
[ "${GH_TOKEN+set}" = set ] || \ [ "${GH_TOKEN+set}" = set ] || \
skip "GitHub access token not available, not updating GitHub pages." 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"; cd "${TRAVIS_BUILD_DIR}/doc/html";
# setup credentials (hide in "set -x" mode) # setup credentials (hide in "set -x" mode)
git config core.askpass /bin/true git remote set-url --push origin "${GITHUB_URL}"
( set +x ; git config credential.${GHPAGES_URL}.username "${GH_TOKEN}" ) git config credential.helper 'store'
# ( 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 # push to GitHub
git push origin gh-pages git push origin gh-pages
} }
......
# SAX # 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 <iostream>
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 <typename SourceEncoding, typename TargetEncoding, typename Allocator = MemoryPoolAllocator<> >
class GenericReader {
// ...
};
typedef GenericReader<UTF8<>, 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<UTF8<>, 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 <unsigned parseFlags, typename InputStream, typename Handler>
bool Parse(InputStream& is, Handler& handler);
// with parseFlags = kDefaultParseFlags
template <typename InputStream, typename Handler>
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 <iostream>
using namespace rapidjson;
using namespace std;
void main() {
StringBuffer s;
Writer<StringBuffer> 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 OutputStream, typename SourceEncoding = UTF8<>, 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 <iostream>
#include <string>
#include <map>
using namespace std;
using namespace rapidjson;
typedef map<string, string> 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 <vector>
#include <cctype>
using namespace rapidjson;
template<typename OutputHandler>
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<char> 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<FileWriteStream> writer(os);
// JSON reader parse from the input stream and let writer generate the output.
CapitalizeFilter<Writer<FileWriteStream> > 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.
// 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 <vector>
#include <cctype>
using namespace rapidjson;
template<typename OutputHandler>
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<char> 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<FileWriteStream> writer(os);
// JSON reader parse from the input stream and let writer generate the output.
CapitalizeFilter<Writer<FileWriteStream> > filter(writer);
if (!reader.Parse(is, filter)) {
fprintf(stderr, "\nError(%u): %s\n", (unsigned)reader.GetErrorOffset(), GetParseError_En(reader.GetParseErrorCode()));
return 1;
}
return 0;
}
// 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 <iostream>
#include <string>
#include <map>
using namespace std;
using namespace rapidjson;
typedef map<string, string> 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;
}
#include "rapidjson/reader.h"
#include <iostream>
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;
}
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"
#include <iostream>
using namespace rapidjson;
using namespace std;
int main() {
StringBuffer s;
Writer<StringBuffer> 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;
}
...@@ -501,7 +501,8 @@ public: ...@@ -501,7 +501,8 @@ public:
\see GenericStringRef, operator=(T) \see GenericStringRef, operator=(T)
*/ */
GenericValue& operator=(StringRefType str) { GenericValue& operator=(StringRefType str) {
return (*this).operator=<StringRefType>(str); GenericValue s(str);
return *this = s;
} }
//! Assignment with primitive types. //! Assignment with primitive types.
...@@ -519,9 +520,8 @@ public: ...@@ -519,9 +520,8 @@ public:
template <typename T> template <typename T>
RAPIDJSON_DISABLEIF_RETURN(internal::IsPointer<T>,GenericValue&) RAPIDJSON_DISABLEIF_RETURN(internal::IsPointer<T>,GenericValue&)
operator=(T value) { operator=(T value) {
this->~GenericValue(); GenericValue v(value);
new (this) GenericValue(value); return *this = v;
return *this;
} }
//! Deep-copy assignment from Value //! Deep-copy assignment from Value
...@@ -1015,44 +1015,45 @@ int z = a[0u].GetInt(); // This works too. ...@@ -1015,44 +1015,45 @@ int z = a[0u].GetInt(); // This works too.
\param handler An object implementing concept Handler. \param handler An object implementing concept Handler.
*/ */
template <typename Handler> template <typename Handler>
const GenericValue& Accept(Handler& handler) const { bool Accept(Handler& handler) const {
switch(GetType()) { switch(GetType()) {
case kNullType: handler.Null(); break; case kNullType: return handler.Null();
case kFalseType: handler.Bool(false); break; case kFalseType: return handler.Bool(false);
case kTrueType: handler.Bool(true); break; case kTrueType: return handler.Bool(true);
case kObjectType: case kObjectType:
handler.StartObject(); if (!handler.StartObject())
return false;
for (ConstMemberIterator m = MemberBegin(); m != MemberEnd(); ++m) { for (ConstMemberIterator m = MemberBegin(); m != MemberEnd(); ++m) {
handler.String(m->name.data_.s.str, m->name.data_.s.length, (m->name.flags_ & kCopyFlag) != 0); if (!handler.String(m->name.data_.s.str, m->name.data_.s.length, (m->name.flags_ & kCopyFlag) != 0))
m->value.Accept(handler); return false;
if (!m->value.Accept(handler))
return false;
} }
handler.EndObject(data_.o.size); return handler.EndObject(data_.o.size);
break;
case kArrayType: case kArrayType:
handler.StartArray(); if (!handler.StartArray())
return false;
for (GenericValue* v = data_.a.elements; v != data_.a.elements + data_.a.size; ++v) for (GenericValue* v = data_.a.elements; v != data_.a.elements + data_.a.size; ++v)
v->Accept(handler); if (!v->Accept(handler))
handler.EndArray(data_.a.size); return false;
break; return handler.EndArray(data_.a.size);
case kStringType: case kStringType:
handler.String(data_.s.str, data_.s.length, (flags_ & kCopyFlag) != 0); return handler.String(data_.s.str, data_.s.length, (flags_ & kCopyFlag) != 0);
break;
case kNumberType: case kNumberType:
if (IsInt()) handler.Int(data_.n.i.i); if (IsInt()) return handler.Int(data_.n.i.i);
else if (IsUint()) handler.Uint(data_.n.u.u); else if (IsUint()) return handler.Uint(data_.n.u.u);
else if (IsInt64()) handler.Int64(data_.n.i64); else if (IsInt64()) return handler.Int64(data_.n.i64);
else if (IsUint64()) handler.Uint64(data_.n.u64); else if (IsUint64()) return handler.Uint64(data_.n.u64);
else handler.Double(data_.n.d); else return handler.Double(data_.n.d);
break;
default: default:
RAPIDJSON_ASSERT(false); RAPIDJSON_ASSERT(false);
} }
return *this; return false;
} }
private: private:
...@@ -1210,7 +1211,7 @@ public: ...@@ -1210,7 +1211,7 @@ public:
/*! \param allocator Optional allocator for allocating stack memory. /*! \param allocator Optional allocator for allocating stack memory.
\param stackCapacity Initial capacity of stack in bytes. \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 //!@name Parse from stream
//!@{ //!@{
...@@ -1226,16 +1227,11 @@ public: ...@@ -1226,16 +1227,11 @@ public:
GenericDocument& ParseStream(InputStream& is) { GenericDocument& ParseStream(InputStream& is) {
ValueType::SetNull(); // Remove existing root if exist ValueType::SetNull(); // Remove existing root if exist
GenericReader<SourceEncoding, Encoding, Allocator> reader(&GetAllocator()); GenericReader<SourceEncoding, Encoding, Allocator> reader(&GetAllocator());
if (reader.template Parse<parseFlags>(is, *this)) { ClearStackOnExit scope(*this);
parseResult_ = reader.template Parse<parseFlags>(is, *this);
if (parseResult_) {
RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object
this->RawAssign(*stack_.template Pop<ValueType>(1)); // Add this-> to prevent issue 13. this->RawAssign(*stack_.template Pop<ValueType>(1)); // Add this-> to prevent issue 13.
parseErrorCode_ = kParseErrorNone;
errorOffset_ = 0;
}
else {
parseErrorCode_ = reader.GetParseErrorCode();
errorOffset_ = reader.GetErrorOffset();
ClearStack();
} }
return *this; return *this;
} }
...@@ -1331,14 +1327,14 @@ public: ...@@ -1331,14 +1327,14 @@ public:
//!@name Handling parse errors //!@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 parseErrorCode_ != kParseErrorNone; } bool HasParseError() const { return parseResult_.IsError(); }
//! Get the message of parsing error. //! Get the \ref ParseErrorCode of last parsing.
ParseErrorCode GetParseError() const { return parseErrorCode_; } 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 errorOffset_; } size_t GetErrorOffset() const { return parseResult_.Offset(); }
//!@} //!@}
...@@ -1349,38 +1345,51 @@ public: ...@@ -1349,38 +1345,51 @@ public:
size_t GetStackCapacity() const { return stack_.GetCapacity(); } size_t GetStackCapacity() const { return stack_.GetCapacity(); }
private: 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 // callers of the following private Handler functions
template <typename,typename,typename> friend class GenericReader; // for parsing template <typename,typename,typename> friend class GenericReader; // for parsing
friend class GenericValue<Encoding,Allocator>; // for deep copying friend class GenericValue<Encoding,Allocator>; // for deep copying
// Implementation of Handler // Implementation of Handler
void Null() { new (stack_.template Push<ValueType>()) ValueType(); } bool Null() { new (stack_.template Push<ValueType>()) ValueType(); return true; }
void Bool(bool b) { new (stack_.template Push<ValueType>()) ValueType(b); } bool Bool(bool b) { new (stack_.template Push<ValueType>()) ValueType(b); return true; }
void Int(int i) { new (stack_.template Push<ValueType>()) ValueType(i); } bool Int(int i) { new (stack_.template Push<ValueType>()) ValueType(i); return true; }
void Uint(unsigned i) { new (stack_.template Push<ValueType>()) ValueType(i); } bool Uint(unsigned i) { new (stack_.template Push<ValueType>()) ValueType(i); return true; }
void Int64(int64_t i) { new (stack_.template Push<ValueType>()) ValueType(i); } bool Int64(int64_t i) { new (stack_.template Push<ValueType>()) ValueType(i); return true; }
void Uint64(uint64_t i) { new (stack_.template Push<ValueType>()) ValueType(i); } bool Uint64(uint64_t i) { new (stack_.template Push<ValueType>()) ValueType(i); return true; }
void Double(double d) { new (stack_.template Push<ValueType>()) ValueType(d); } bool Double(double d) { new (stack_.template Push<ValueType>()) ValueType(d); return true; }
void String(const Ch* str, SizeType length, bool copy) { bool String(const Ch* str, SizeType length, bool copy) {
if (copy) if (copy)
new (stack_.template Push<ValueType>()) ValueType(str, length, GetAllocator()); new (stack_.template Push<ValueType>()) ValueType(str, length, GetAllocator());
else else
new (stack_.template Push<ValueType>()) ValueType(str, length); new (stack_.template Push<ValueType>()) ValueType(str, length);
return true;
} }
void StartObject() { new (stack_.template Push<ValueType>()) ValueType(kObjectType); } bool StartObject() { new (stack_.template Push<ValueType>()) ValueType(kObjectType); return true; }
void EndObject(SizeType memberCount) { bool EndObject(SizeType memberCount) {
typename ValueType::Member* members = stack_.template Pop<typename ValueType::Member>(memberCount); typename ValueType::Member* members = stack_.template Pop<typename ValueType::Member>(memberCount);
stack_.template Top<ValueType>()->SetObjectRaw(members, (SizeType)memberCount, GetAllocator()); stack_.template Top<ValueType>()->SetObjectRaw(members, (SizeType)memberCount, GetAllocator());
return true;
} }
void StartArray() { new (stack_.template Push<ValueType>()) ValueType(kArrayType); } bool StartArray() { new (stack_.template Push<ValueType>()) ValueType(kArrayType); return true; }
void EndArray(SizeType elementCount) { bool EndArray(SizeType elementCount) {
ValueType* elements = stack_.template Pop<ValueType>(elementCount); ValueType* elements = stack_.template Pop<ValueType>(elementCount);
stack_.template Top<ValueType>()->SetArrayRaw(elements, elementCount, GetAllocator()); stack_.template Top<ValueType>()->SetArrayRaw(elements, elementCount, GetAllocator());
return true;
} }
private: private:
...@@ -1397,8 +1406,7 @@ private: ...@@ -1397,8 +1406,7 @@ private:
static const size_t kDefaultStackCapacity = 1024; static const size_t kDefaultStackCapacity = 1024;
internal::Stack<Allocator> stack_; internal::Stack<Allocator> stack_;
ParseErrorCode parseErrorCode_; ParseResult parseResult_;
size_t errorOffset_;
}; };
//! GenericDocument with UTF8 encoding //! GenericDocument with UTF8 encoding
......
...@@ -32,12 +32,13 @@ inline const RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErro ...@@ -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 kParseErrorStringUnicodeSurrogateInvalid: return RAPIDJSON_ERROR_STRING("The surrogate pair in string is invalid.");
case kParseErrorStringEscapeInvalid: return RAPIDJSON_ERROR_STRING("Invalid escape character in string."); 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 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 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 kParseErrorNumberMissFraction: return RAPIDJSON_ERROR_STRING("Miss fraction part in number.");
case kParseErrorNumberMissExponent: return RAPIDJSON_ERROR_STRING("Miss exponent 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."); case kParseErrorUnspecificSyntaxError: return RAPIDJSON_ERROR_STRING("Unspecific syntax error.");
default: default:
......
#ifndef RAPIDJSON_ERROR_ERROR_H__ #ifndef RAPIDJSON_ERROR_ERROR_H__
#define RAPIDJSON_ERROR_ERROR_H__ #define RAPIDJSON_ERROR_ERROR_H__
#include "../reader.h" // ParseErrorCode
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// RAPIDJSON_ERROR_CHARTYPE // RAPIDJSON_ERROR_CHARTYPE
...@@ -29,6 +27,85 @@ ...@@ -29,6 +27,85 @@
namespace rapidjson { 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(). //! Function pointer type of GetParseError().
/*! This is the prototype for GetParseError_X(), where X is a locale. /*! This is the prototype for GetParseError_X(), where X is a locale.
User can dynamically change locale in runtime, e.g.: User can dynamically change locale in runtime, e.g.:
......
...@@ -51,29 +51,27 @@ public: ...@@ -51,29 +51,27 @@ public:
*/ */
//@{ //@{
PrettyWriter& Null() { PrettyPrefix(kNullType); Base::WriteNull(); return *this; } bool Null() { PrettyPrefix(kNullType); return Base::WriteNull(); }
PrettyWriter& Bool(bool b) { PrettyPrefix(b ? kTrueType : kFalseType); Base::WriteBool(b); return *this; } bool Bool(bool b) { PrettyPrefix(b ? kTrueType : kFalseType); return Base::WriteBool(b); }
PrettyWriter& Int(int i) { PrettyPrefix(kNumberType); Base::WriteInt(i); return *this; } bool Int(int i) { PrettyPrefix(kNumberType); return Base::WriteInt(i); }
PrettyWriter& Uint(unsigned u) { PrettyPrefix(kNumberType); Base::WriteUint(u); return *this; } bool Uint(unsigned u) { PrettyPrefix(kNumberType); return Base::WriteUint(u); }
PrettyWriter& Int64(int64_t i64) { PrettyPrefix(kNumberType); Base::WriteInt64(i64); return *this; } bool Int64(int64_t i64) { PrettyPrefix(kNumberType); return Base::WriteInt64(i64); }
PrettyWriter& Uint64(uint64_t u64) { PrettyPrefix(kNumberType); Base::WriteUint64(u64); return *this; } bool Uint64(uint64_t u64) { PrettyPrefix(kNumberType); return Base::WriteUint64(u64); }
PrettyWriter& Double(double d) { PrettyPrefix(kNumberType); Base::WriteDouble(d); return *this; } 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; (void)copy;
PrettyPrefix(kStringType); PrettyPrefix(kStringType);
Base::WriteString(str, length); return Base::WriteString(str, length);
return *this;
} }
PrettyWriter& StartObject() { bool StartObject() {
PrettyPrefix(kObjectType); PrettyPrefix(kObjectType);
new (Base::level_stack_.template Push<typename Base::Level>()) typename Base::Level(false); new (Base::level_stack_.template Push<typename Base::Level>()) typename Base::Level(false);
Base::WriteStartObject(); return Base::WriteStartObject();
return *this;
} }
PrettyWriter& EndObject(SizeType memberCount = 0) { bool EndObject(SizeType memberCount = 0) {
(void)memberCount; (void)memberCount;
RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level));
RAPIDJSON_ASSERT(!Base::level_stack_.template Top<typename Base::Level>()->inArray); RAPIDJSON_ASSERT(!Base::level_stack_.template Top<typename Base::Level>()->inArray);
...@@ -83,20 +81,20 @@ public: ...@@ -83,20 +81,20 @@ public:
Base::os_.Put('\n'); Base::os_.Put('\n');
WriteIndent(); WriteIndent();
} }
Base::WriteEndObject(); if (!Base::WriteEndObject())
return false;
if (Base::level_stack_.Empty()) // end of json text if (Base::level_stack_.Empty()) // end of json text
Base::os_.Flush(); Base::os_.Flush();
return *this; return true;
} }
PrettyWriter& StartArray() { bool StartArray() {
PrettyPrefix(kArrayType); PrettyPrefix(kArrayType);
new (Base::level_stack_.template Push<typename Base::Level>()) typename Base::Level(true); new (Base::level_stack_.template Push<typename Base::Level>()) typename Base::Level(true);
Base::WriteStartArray(); return Base::WriteStartArray();
return *this;
} }
PrettyWriter& EndArray(SizeType memberCount = 0) { bool EndArray(SizeType memberCount = 0) {
(void)memberCount; (void)memberCount;
RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level));
RAPIDJSON_ASSERT(Base::level_stack_.template Top<typename Base::Level>()->inArray); RAPIDJSON_ASSERT(Base::level_stack_.template Top<typename Base::Level>()->inArray);
...@@ -106,10 +104,11 @@ public: ...@@ -106,10 +104,11 @@ public:
Base::os_.Put('\n'); Base::os_.Put('\n');
WriteIndent(); WriteIndent();
} }
Base::WriteEndArray(); if (!Base::WriteEndArray())
return false;
if (Base::level_stack_.Empty()) // end of json text if (Base::level_stack_.Empty()) // end of json text
Base::os_.Flush(); Base::os_.Flush();
return *this; return true;
} }
//@} //@}
...@@ -118,12 +117,15 @@ public: ...@@ -118,12 +117,15 @@ public:
//@{ //@{
//! Simpler but slower overload. //! 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() //! Overridden for fluent API, see \ref Writer::Double()
PrettyWriter& Double(double d, int precision) { bool Double(double d, int precision) {
int oldPrecision = Base::GetDoublePrecision(); int oldPrecision = Base::GetDoublePrecision();
return SetDoublePrecision(precision).Double(d).SetDoublePrecision(oldPrecision); SetDoublePrecision(precision);
bool ret = Double(d);
SetDoublePrecision(oldPrecision);
return ret;
} }
//@} //@}
......
...@@ -24,12 +24,21 @@ RAPIDJSON_DIAG_PUSH ...@@ -24,12 +24,21 @@ RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant
#endif #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 #ifndef RAPIDJSON_PARSE_ERROR_NORETURN
#define RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset) \ #define RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset) \
RAPIDJSON_MULTILINEMACRO_BEGIN \ RAPIDJSON_MULTILINEMACRO_BEGIN \
RAPIDJSON_ASSERT(!HasParseError()); /* Error can only be assigned once */ \ RAPIDJSON_ASSERT(!HasParseError()); /* Error can only be assigned once */ \
parseErrorCode_ = parseErrorCode; \ parseResult_.Set(parseErrorCode,offset); \
errorOffset_ = offset; \
RAPIDJSON_MULTILINEMACRO_END RAPIDJSON_MULTILINEMACRO_END
#endif #endif
...@@ -37,10 +46,12 @@ RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant ...@@ -37,10 +46,12 @@ RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant
#define RAPIDJSON_PARSE_ERROR(parseErrorCode, offset) \ #define RAPIDJSON_PARSE_ERROR(parseErrorCode, offset) \
RAPIDJSON_MULTILINEMACRO_BEGIN \ RAPIDJSON_MULTILINEMACRO_BEGIN \
RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset); \ RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset); \
return; \ RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; \
RAPIDJSON_MULTILINEMACRO_END RAPIDJSON_MULTILINEMACRO_END
#endif #endif
#include "error/error.h" // ParseErrorCode, ParseResult
namespace rapidjson { namespace rapidjson {
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
...@@ -56,56 +67,29 @@ enum ParseFlag { ...@@ -56,56 +67,29 @@ enum ParseFlag {
kParseIterativeFlag = 4 //!< Iterative(constant complexity in terms of function call stack size) parsing. kParseIterativeFlag = 4 //!< Iterative(constant complexity in terms of function call stack size) parsing.
}; };
//! 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 // Handler
/*! \class rapidjson::Handler /*! \class rapidjson::Handler
\brief Concept for receiving events from GenericReader upon parsing. \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 \code
concept Handler { concept Handler {
typename Ch; typename Ch;
void Null(); bool Null();
void Bool(bool b); bool Bool(bool b);
void Int(int i); bool Int(int i);
void Uint(unsigned i); bool Uint(unsigned i);
void Int64(int64_t i); bool Int64(int64_t i);
void Uint64(uint64_t i); bool Uint64(uint64_t i);
void Double(double d); bool Double(double d);
void String(const Ch* str, SizeType length, bool copy); bool String(const Ch* str, SizeType length, bool copy);
void StartObject(); bool StartObject();
void EndObject(SizeType memberCount); bool EndObject(SizeType memberCount);
void StartArray(); bool StartArray();
void EndArray(SizeType elementCount); bool EndArray(SizeType elementCount);
}; };
\endcode \endcode
*/ */
...@@ -120,19 +104,19 @@ template<typename Encoding = UTF8<> > ...@@ -120,19 +104,19 @@ template<typename Encoding = UTF8<> >
struct BaseReaderHandler { struct BaseReaderHandler {
typedef typename Encoding::Ch Ch; typedef typename Encoding::Ch Ch;
void Default() {} bool Default() { return true; }
void Null() { Default(); } bool Null() { return Default(); }
void Bool(bool) { Default(); } bool Bool(bool) { return Default(); }
void Int(int) { Default(); } bool Int(int) { return Default(); }
void Uint(unsigned) { Default(); } bool Uint(unsigned) { return Default(); }
void Int64(int64_t) { Default(); } bool Int64(int64_t) { return Default(); }
void Uint64(uint64_t) { Default(); } bool Uint64(uint64_t) { return Default(); }
void Double(double) { Default(); } bool Double(double) { return Default(); }
void String(const Ch*, SizeType, bool) { Default(); } bool String(const Ch*, SizeType, bool) { return Default(); }
void StartObject() { Default(); } bool StartObject() { return Default(); }
void EndObject(SizeType) { Default(); } bool EndObject(SizeType) { return Default(); }
void StartArray() { Default(); } bool StartArray() { return Default(); }
void EndArray(SizeType) { Default(); } bool EndArray(SizeType) { return Default(); }
}; };
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
...@@ -291,7 +275,7 @@ public: ...@@ -291,7 +275,7 @@ public:
/*! \param allocator Optional allocator for allocating stack memory. (Only use for non-destructive parsing) /*! \param allocator Optional allocator for allocating stack memory. (Only use for non-destructive parsing)
\param stackCapacity stack capacity in bytes for storing a single decoded string. (Only use for non-destructive parsing) \param stackCapacity stack capacity in bytes for storing a single decoded string. (Only use for non-destructive parsing)
*/ */
GenericReader(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. //! Parse JSON text.
/*! \tparam parseFlags Combination of \ref ParseFlag. /*! \tparam parseFlags Combination of \ref ParseFlag.
...@@ -302,35 +286,37 @@ public: ...@@ -302,35 +286,37 @@ public:
\return Whether the parsing is successful. \return Whether the parsing is successful.
*/ */
template <unsigned parseFlags, typename InputStream, typename Handler> template <unsigned parseFlags, typename InputStream, typename Handler>
bool Parse(InputStream& is, Handler& handler) { ParseResult Parse(InputStream& is, Handler& handler) {
parseErrorCode_ = kParseErrorNone;
errorOffset_ = 0;
if (parseFlags & kParseIterativeFlag) if (parseFlags & kParseIterativeFlag)
return IterativeParse<parseFlags>(is, handler); return IterativeParse<parseFlags>(is, handler);
parseResult_.Clear();
ClearStackOnExit scope(*this);
SkipWhitespace(is); SkipWhitespace(is);
if (is.Peek() == '\0') if (is.Peek() == '\0') {
RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentEmpty, is.Tell()); RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentEmpty, is.Tell());
RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_);
}
else { else {
switch (is.Peek()) { switch (is.Peek()) {
case '{': ParseObject<parseFlags>(is, handler); break; case '{': ParseObject<parseFlags>(is, handler); break;
case '[': ParseArray<parseFlags>(is, handler); break; case '[': ParseArray<parseFlags>(is, handler); break;
default: RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentRootNotObjectOrArray, is.Tell()); default: RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentRootNotObjectOrArray, is.Tell());
} }
if (HasParseError()) RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_);
goto out;
SkipWhitespace(is); SkipWhitespace(is);
if (is.Peek() != '\0') if (is.Peek() != '\0') {
RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentRootNotSingular, is.Tell()); RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentRootNotSingular, is.Tell());
RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_);
}
} }
out: return parseResult_;
stack_.Clear();
return !HasParseError();
} }
//! Parse JSON text (with \ref kParseDefaultFlags) //! Parse JSON text (with \ref kParseDefaultFlags)
...@@ -341,32 +327,51 @@ public: ...@@ -341,32 +327,51 @@ public:
\return Whether the parsing is successful. \return Whether the parsing is successful.
*/ */
template <typename InputStream, typename Handler> template <typename InputStream, typename Handler>
bool Parse(InputStream& is, Handler& handler) { ParseResult Parse(InputStream& is, Handler& handler) {
return Parse<kParseDefaultFlags>(is, handler); return Parse<kParseDefaultFlags>(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: private:
// Prohibit copy constructor & assignment operator. // Prohibit copy constructor & assignment operator.
GenericReader(const GenericReader&); GenericReader(const GenericReader&);
GenericReader& operator=(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, ... } // Parse object: { string : value, ... }
template<unsigned parseFlags, typename InputStream, typename Handler> template<unsigned parseFlags, typename InputStream, typename Handler>
void ParseObject(InputStream& is, Handler& handler) { void ParseObject(InputStream& is, Handler& handler) {
RAPIDJSON_ASSERT(is.Peek() == '{'); RAPIDJSON_ASSERT(is.Peek() == '{');
is.Take(); // Skip '{' is.Take(); // Skip '{'
handler.StartObject();
if (!handler.StartObject())
RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
SkipWhitespace(is); SkipWhitespace(is);
if (is.Peek() == '}') { if (is.Peek() == '}') {
is.Take(); is.Take();
handler.EndObject(0); // empty object if (!handler.EndObject(0)) // empty object
RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
return; return;
} }
...@@ -375,8 +380,7 @@ private: ...@@ -375,8 +380,7 @@ private:
RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell());
ParseString<parseFlags>(is, handler); ParseString<parseFlags>(is, handler);
if (HasParseError()) RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
return;
SkipWhitespace(is); SkipWhitespace(is);
...@@ -386,16 +390,19 @@ private: ...@@ -386,16 +390,19 @@ private:
SkipWhitespace(is); SkipWhitespace(is);
ParseValue<parseFlags>(is, handler); ParseValue<parseFlags>(is, handler);
if (HasParseError()) RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
return;
SkipWhitespace(is); SkipWhitespace(is);
++memberCount; ++memberCount;
switch(is.Take()) { switch (is.Take()) {
case ',': SkipWhitespace(is); break; 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()); default: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell());
} }
} }
...@@ -406,26 +413,33 @@ private: ...@@ -406,26 +413,33 @@ private:
void ParseArray(InputStream& is, Handler& handler) { void ParseArray(InputStream& is, Handler& handler) {
RAPIDJSON_ASSERT(is.Peek() == '['); RAPIDJSON_ASSERT(is.Peek() == '[');
is.Take(); // Skip '[' is.Take(); // Skip '['
handler.StartArray();
if (!handler.StartArray())
RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
SkipWhitespace(is); SkipWhitespace(is);
if (is.Peek() == ']') { if (is.Peek() == ']') {
is.Take(); is.Take();
handler.EndArray(0); // empty array if (!handler.EndArray(0)) // empty array
RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
return; return;
} }
for (SizeType elementCount = 0;;) { for (SizeType elementCount = 0;;) {
ParseValue<parseFlags>(is, handler); ParseValue<parseFlags>(is, handler);
if (HasParseError()) RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
return;
++elementCount; ++elementCount;
SkipWhitespace(is); SkipWhitespace(is);
switch (is.Take()) { switch (is.Take()) {
case ',': SkipWhitespace(is); break; 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()); default: RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell());
} }
} }
...@@ -436,8 +450,10 @@ private: ...@@ -436,8 +450,10 @@ private:
RAPIDJSON_ASSERT(is.Peek() == 'n'); RAPIDJSON_ASSERT(is.Peek() == 'n');
is.Take(); is.Take();
if (is.Take() == 'u' && is.Take() == 'l' && is.Take() == 'l') if (is.Take() == 'u' && is.Take() == 'l' && is.Take() == 'l') {
handler.Null(); if (!handler.Null())
RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
}
else else
RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell() - 1); RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell() - 1);
} }
...@@ -447,10 +463,12 @@ private: ...@@ -447,10 +463,12 @@ private:
RAPIDJSON_ASSERT(is.Peek() == 't'); RAPIDJSON_ASSERT(is.Peek() == 't');
is.Take(); is.Take();
if (is.Take() == 'r' && is.Take() == 'u' && is.Take() == 'e') if (is.Take() == 'r' && is.Take() == 'u' && is.Take() == 'e') {
handler.Bool(true); if (!handler.Bool(true))
RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
}
else else
RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell() - 1);
} }
template<unsigned parseFlags, typename InputStream, typename Handler> template<unsigned parseFlags, typename InputStream, typename Handler>
...@@ -458,8 +476,10 @@ private: ...@@ -458,8 +476,10 @@ private:
RAPIDJSON_ASSERT(is.Peek() == 'f'); RAPIDJSON_ASSERT(is.Peek() == 'f');
is.Take(); is.Take();
if (is.Take() == 'a' && is.Take() == 'l' && is.Take() == 's' && is.Take() == 'e') if (is.Take() == 'a' && is.Take() == 'l' && is.Take() == 's' && is.Take() == 'e') {
handler.Bool(false); if (!handler.Bool(false))
RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
}
else else
RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell() - 1); RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell() - 1);
} }
...@@ -480,7 +500,7 @@ private: ...@@ -480,7 +500,7 @@ private:
codepoint -= 'a' - 10; codepoint -= 'a' - 10;
else { else {
RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorStringUnicodeEscapeInvalidHex, is.Tell() - 1); RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorStringUnicodeEscapeInvalidHex, is.Tell() - 1);
return 0; RAPIDJSON_PARSE_ERROR_EARLY_RETURN(0);
} }
} }
return codepoint; return codepoint;
...@@ -512,18 +532,18 @@ private: ...@@ -512,18 +532,18 @@ private:
if (parseFlags & kParseInsituFlag) { if (parseFlags & kParseInsituFlag) {
typename InputStream::Ch *head = s.PutBegin(); typename InputStream::Ch *head = s.PutBegin();
ParseStringToStream<parseFlags, SourceEncoding, SourceEncoding>(s, s); ParseStringToStream<parseFlags, SourceEncoding, SourceEncoding>(s, s);
if (HasParseError()) RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
return;
size_t length = s.PutEnd(head) - 1; size_t length = s.PutEnd(head) - 1;
RAPIDJSON_ASSERT(length <= 0xFFFFFFFF); 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 { else {
StackStream stackStream(stack_); StackStream stackStream(stack_);
ParseStringToStream<parseFlags, SourceEncoding, TargetEncoding>(s, stackStream); ParseStringToStream<parseFlags, SourceEncoding, TargetEncoding>(s, stackStream);
if (HasParseError()) RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID;
return; if (!handler.String(stack_.template Pop<typename TargetEncoding::Ch>(stackStream.length_), stackStream.length_ - 1, true))
handler.String(stack_.template Pop<typename TargetEncoding::Ch>(stackStream.length_), stackStream.length_ - 1, true); RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell());
} }
} }
...@@ -598,7 +618,7 @@ private: ...@@ -598,7 +618,7 @@ private:
} }
// Parse int: zero / ( digit1-9 *DIGIT ) // Parse int: zero / ( digit1-9 *DIGIT )
unsigned i; unsigned i = 0;
bool try64bit = false; bool try64bit = false;
if (s.Peek() == '0') { if (s.Peek() == '0') {
i = 0; i = 0;
...@@ -725,6 +745,7 @@ private: ...@@ -725,6 +745,7 @@ private:
} }
// Finish parsing, call event according to the type of number. // Finish parsing, call event according to the type of number.
bool cont = true;
if (useDouble) { if (useDouble) {
int expSum = exp + expFrac; int expSum = exp + expFrac;
if (expSum < -308) { if (expSum < -308) {
...@@ -735,22 +756,24 @@ private: ...@@ -735,22 +756,24 @@ private:
else else
d *= internal::Pow10(expSum); d *= internal::Pow10(expSum);
handler.Double(minus ? -d : d); cont = handler.Double(minus ? -d : d);
} }
else { else {
if (try64bit) { if (try64bit) {
if (minus) if (minus)
handler.Int64(-(int64_t)i64); cont = handler.Int64(-(int64_t)i64);
else else
handler.Uint64(i64); cont = handler.Uint64(i64);
} }
else { else {
if (minus) if (minus)
handler.Int(-(int)i); cont = handler.Int(-(int)i);
else else
handler.Uint(i); cont = handler.Uint(i);
} }
} }
if (!cont)
RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell());
} }
// Parse any JSON value // Parse any JSON value
...@@ -992,6 +1015,7 @@ private: ...@@ -992,6 +1015,7 @@ private:
IterativeParsingState Transit(IterativeParsingState src, IterativeParsingToken token, IterativeParsingState dst, InputStream& is, Handler& handler) { IterativeParsingState Transit(IterativeParsingState src, IterativeParsingToken token, IterativeParsingState dst, InputStream& is, Handler& handler) {
int c = 0; int c = 0;
IterativeParsingState n; IterativeParsingState n;
bool hr;
switch (dst) { switch (dst) {
case IterativeParsingStartState: case IterativeParsingStartState:
...@@ -1021,10 +1045,16 @@ private: ...@@ -1021,10 +1045,16 @@ private:
*stack_.template Push<int>(1) = 0; *stack_.template Push<int>(1) = 0;
// Call handler // Call handler
if (dst == IterativeParsingObjectInitialState) if (dst == IterativeParsingObjectInitialState)
handler.StartObject(); hr = handler.StartObject();
else else
handler.StartArray(); hr = handler.StartArray();
return dst; // On handler short circuits the parsing.
if (!hr) {
RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell());
return IterativeParsingErrorState;
}
else
return dst;
case IterativeParsingMemberKeyState: case IterativeParsingMemberKeyState:
ParseString<parseFlags>(is, handler); ParseString<parseFlags>(is, handler);
...@@ -1077,8 +1107,14 @@ private: ...@@ -1077,8 +1107,14 @@ private:
if (n == IterativeParsingStartState) if (n == IterativeParsingStartState)
n = IterativeParsingFinishState; n = IterativeParsingFinishState;
// Call handler // Call handler
handler.EndObject(c); hr = handler.EndObject(c);
return n; // On handler short circuits the parsing.
if (!hr) {
RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell());
return IterativeParsingErrorState;
}
else
return n;
case IterativeParsingArrayFinishState: case IterativeParsingArrayFinishState:
is.Take(); is.Take();
...@@ -1093,8 +1129,14 @@ private: ...@@ -1093,8 +1129,14 @@ private:
if (n == IterativeParsingStartState) if (n == IterativeParsingStartState)
n = IterativeParsingFinishState; n = IterativeParsingFinishState;
// Call handler // Call handler
handler.EndArray(c); hr = handler.EndArray(c);
return n; // On handler short circuits the parsing.
if (!hr) {
RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell());
return IterativeParsingErrorState;
}
else
return n;
default: default:
RAPIDJSON_ASSERT(false); RAPIDJSON_ASSERT(false);
...@@ -1135,7 +1177,9 @@ private: ...@@ -1135,7 +1177,9 @@ private:
} }
template <unsigned parseFlags, typename InputStream, typename Handler> template <unsigned parseFlags, typename InputStream, typename Handler>
bool IterativeParse(InputStream& is, Handler& handler) { ParseResult IterativeParse(InputStream& is, Handler& handler) {
parseResult_.Clear();
ClearStackOnExit scope(*this);
IterativeParsingState state = IterativeParsingStartState; IterativeParsingState state = IterativeParsingStartState;
SkipWhitespace(is); SkipWhitespace(is);
...@@ -1156,15 +1200,13 @@ private: ...@@ -1156,15 +1200,13 @@ private:
// Handle the end of file. // Handle the end of file.
if (state != IterativeParsingFinishState) if (state != IterativeParsingFinishState)
HandleError(state, is); HandleError(state, is);
stack_.Clear(); return parseResult_;
return state == IterativeParsingFinishState;
} }
static const size_t kDefaultStackCapacity = 256; //!< Default stack capacity in bytes for storing a single decoded string. static const size_t kDefaultStackCapacity = 256; //!< Default stack capacity in bytes for storing a single decoded string.
internal::Stack<Allocator> stack_; //!< A stack for storing decoded string temporarily during non-destructive parsing. internal::Stack<Allocator> stack_; //!< A stack for storing decoded string temporarily during non-destructive parsing.
ParseErrorCode parseErrorCode_; ParseResult parseResult_;
size_t errorOffset_;
}; // class GenericReader }; // class GenericReader
//! Reader with UTF8 encoding and default allocator. //! Reader with UTF8 encoding and default allocator.
......
...@@ -64,12 +64,12 @@ public: ...@@ -64,12 +64,12 @@ public:
*/ */
//@{ //@{
Writer& Null() { Prefix(kNullType); WriteNull(); return *this; } bool Null() { Prefix(kNullType); return WriteNull(); }
Writer& Bool(bool b) { Prefix(b ? kTrueType : kFalseType); WriteBool(b); return *this; } bool Bool(bool b) { Prefix(b ? kTrueType : kFalseType); return WriteBool(b); }
Writer& Int(int i) { Prefix(kNumberType); WriteInt(i); return *this; } bool Int(int i) { Prefix(kNumberType); return WriteInt(i); }
Writer& Uint(unsigned u) { Prefix(kNumberType); WriteUint(u); return *this; } bool Uint(unsigned u) { Prefix(kNumberType); return WriteUint(u); }
Writer& Int64(int64_t i64) { Prefix(kNumberType); WriteInt64(i64); return *this; } bool Int64(int64_t i64) { Prefix(kNumberType); return WriteInt64(i64); }
Writer& Uint64(uint64_t u64) { Prefix(kNumberType); WriteUint64(u64); return *this; } bool Uint64(uint64_t u64) { Prefix(kNumberType); return WriteUint64(u64); }
//! Writes the given \c double value to the stream //! Writes the given \c double value to the stream
/*! /*!
...@@ -80,51 +80,48 @@ public: ...@@ -80,51 +80,48 @@ public:
writer.SetDoublePrecision(12).Double(M_PI); writer.SetDoublePrecision(12).Double(M_PI);
\endcode \endcode
\param d The value to be written. \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; (void)copy;
Prefix(kStringType); Prefix(kStringType);
WriteString(str, length); return WriteString(str, length);
return *this;
} }
Writer& StartObject() { bool StartObject() {
Prefix(kObjectType); Prefix(kObjectType);
new (level_stack_.template Push<Level>()) Level(false); new (level_stack_.template Push<Level>()) Level(false);
WriteStartObject(); return WriteStartObject();
return *this;
} }
Writer& EndObject(SizeType memberCount = 0) { bool EndObject(SizeType memberCount = 0) {
(void)memberCount; (void)memberCount;
RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level));
RAPIDJSON_ASSERT(!level_stack_.template Top<Level>()->inArray); RAPIDJSON_ASSERT(!level_stack_.template Top<Level>()->inArray);
level_stack_.template Pop<Level>(1); level_stack_.template Pop<Level>(1);
WriteEndObject(); bool ret = WriteEndObject();
if (level_stack_.Empty()) // end of json text if (level_stack_.Empty()) // end of json text
os_.Flush(); os_.Flush();
return *this; return ret;
} }
Writer& StartArray() { bool StartArray() {
Prefix(kArrayType); Prefix(kArrayType);
new (level_stack_.template Push<Level>()) Level(true); new (level_stack_.template Push<Level>()) Level(true);
WriteStartArray(); return WriteStartArray();
return *this;
} }
Writer& EndArray(SizeType elementCount = 0) { bool EndArray(SizeType elementCount = 0) {
(void)elementCount; (void)elementCount;
RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level));
RAPIDJSON_ASSERT(level_stack_.template Top<Level>()->inArray); RAPIDJSON_ASSERT(level_stack_.template Top<Level>()->inArray);
level_stack_.template Pop<Level>(1); level_stack_.template Pop<Level>(1);
WriteEndArray(); bool ret = WriteEndArray();
if (level_stack_.Empty()) // end of json text if (level_stack_.Empty()) // end of json text
os_.Flush(); os_.Flush();
return *this; return ret;
} }
//@} //@}
...@@ -138,15 +135,18 @@ public: ...@@ -138,15 +135,18 @@ public:
\see Double(), SetDoublePrecision(), GetDoublePrecision() \see Double(), SetDoublePrecision(), GetDoublePrecision()
\param d The value to be written \param d The value to be written
\param precision The number of significant digits for this value \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(); int oldPrecision = GetDoublePrecision();
return SetDoublePrecision(precision).Double(d).SetDoublePrecision(oldPrecision); SetDoublePrecision(precision);
bool ret = Double(d);
SetDoublePrecision(oldPrecision);
return ret;
} }
//! Simpler but slower overload. //! 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: ...@@ -160,28 +160,29 @@ protected:
static const size_t kDefaultLevelDepth = 32; static const size_t kDefaultLevelDepth = 32;
void WriteNull() { bool WriteNull() {
os_.Put('n'); os_.Put('u'); os_.Put('l'); os_.Put('l'); os_.Put('n'); os_.Put('u'); os_.Put('l'); os_.Put('l'); return true;
} }
void WriteBool(bool b) { bool WriteBool(bool b) {
if (b) { if (b) {
os_.Put('t'); os_.Put('r'); os_.Put('u'); os_.Put('e'); os_.Put('t'); os_.Put('r'); os_.Put('u'); os_.Put('e');
} }
else { else {
os_.Put('f'); os_.Put('a'); os_.Put('l'); os_.Put('s'); os_.Put('e'); 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) { if (i < 0) {
os_.Put('-'); os_.Put('-');
i = -i; i = -i;
} }
WriteUint((unsigned)i); return WriteUint((unsigned)i);
} }
void WriteUint(unsigned u) { bool WriteUint(unsigned u) {
char buffer[10]; char buffer[10];
char *p = buffer; char *p = buffer;
do { do {
...@@ -193,17 +194,19 @@ protected: ...@@ -193,17 +194,19 @@ protected:
--p; --p;
os_.Put(*p); os_.Put(*p);
} while (p != buffer); } while (p != buffer);
return true;
} }
void WriteInt64(int64_t i64) { bool WriteInt64(int64_t i64) {
if (i64 < 0) { if (i64 < 0) {
os_.Put('-'); os_.Put('-');
i64 = -i64; i64 = -i64;
} }
WriteUint64((uint64_t)i64); WriteUint64((uint64_t)i64);
return true;
} }
void WriteUint64(uint64_t u64) { bool WriteUint64(uint64_t u64) {
char buffer[20]; char buffer[20];
char *p = buffer; char *p = buffer;
do { do {
...@@ -215,6 +218,7 @@ protected: ...@@ -215,6 +218,7 @@ protected:
--p; --p;
os_.Put(*p); os_.Put(*p);
} while (p != buffer); } while (p != buffer);
return true;
} }
#ifdef _MSC_VER #ifdef _MSC_VER
...@@ -224,16 +228,17 @@ protected: ...@@ -224,16 +228,17 @@ protected:
#endif #endif
//! \todo Optimization with custom double-to-string converter. //! \todo Optimization with custom double-to-string converter.
void WriteDouble(double d) { bool WriteDouble(double d) {
char buffer[100]; char buffer[100];
int ret = RAPIDJSON_SNPRINTF(buffer, sizeof(buffer), "%.*g", doublePrecision_, d); int ret = RAPIDJSON_SNPRINTF(buffer, sizeof(buffer), "%.*g", doublePrecision_, d);
RAPIDJSON_ASSERT(ret >= 1); RAPIDJSON_ASSERT(ret >= 1);
for (int i = 0; i < ret; i++) for (int i = 0; i < ret; i++)
os_.Put(buffer[i]); os_.Put(buffer[i]);
return true;
} }
#undef RAPIDJSON_SNPRINTF #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 hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
static const char escape[256] = { static const char escape[256] = {
#define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 #define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
...@@ -266,12 +271,13 @@ protected: ...@@ -266,12 +271,13 @@ protected:
Transcoder<SourceEncoding, TargetEncoding>::Transcode(is, os_); Transcoder<SourceEncoding, TargetEncoding>::Transcode(is, os_);
} }
os_.Put('\"'); os_.Put('\"');
return true;
} }
void WriteStartObject() { os_.Put('{'); } bool WriteStartObject() { os_.Put('{'); return true; }
void WriteEndObject() { os_.Put('}'); } bool WriteEndObject() { os_.Put('}'); return true; }
void WriteStartArray() { os_.Put('['); } bool WriteStartArray() { os_.Put('['); return true; }
void WriteEndArray() { os_.Put(']'); } bool WriteEndArray() { os_.Put(']'); return true; }
void Prefix(Type type) { void Prefix(Type type) {
(void)type; (void)type;
......
...@@ -8,7 +8,11 @@ ...@@ -8,7 +8,11 @@
#define TEST_PLATFORM 0 #define TEST_PLATFORM 0
#define TEST_MISC 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_SSE2
#define RAPIDJSON_SSE42 #define RAPIDJSON_SSE42
#endif #endif
......
...@@ -197,8 +197,8 @@ RAPIDJSON_DIAG_OFF(effc++) ...@@ -197,8 +197,8 @@ RAPIDJSON_DIAG_OFF(effc++)
struct ValueCounter : public BaseReaderHandler<> { struct ValueCounter : public BaseReaderHandler<> {
ValueCounter() : count_(1) {} // root ValueCounter() : count_(1) {} // root
void EndObject(SizeType memberCount) { count_ += memberCount * 2; } bool EndObject(SizeType memberCount) { count_ += memberCount * 2; return true; }
void EndArray(SizeType elementCount) { count_ += elementCount; } bool EndArray(SizeType elementCount) { count_ += elementCount; return true; }
SizeType count_; SizeType count_;
}; };
......
...@@ -13,10 +13,10 @@ RAPIDJSON_DIAG_OFF(effc++) ...@@ -13,10 +13,10 @@ RAPIDJSON_DIAG_OFF(effc++)
template<bool expect> template<bool expect>
struct ParseBoolHandler : BaseReaderHandler<> { struct ParseBoolHandler : BaseReaderHandler<> {
ParseBoolHandler() : step_(0) {} 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. // gcc 4.8.x generates warning in EXPECT_EQ(bool, bool) on this gtest version.
// Workaround with EXPECT_TRUE(). // 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_; unsigned step_;
}; };
...@@ -39,8 +39,8 @@ TEST(Reader, ParseFalse) { ...@@ -39,8 +39,8 @@ TEST(Reader, ParseFalse) {
struct ParseIntHandler : BaseReaderHandler<> { struct ParseIntHandler : BaseReaderHandler<> {
ParseIntHandler() : step_(0), actual_() {} ParseIntHandler() : step_(0), actual_() {}
void Default() { FAIL(); } bool Default() { ADD_FAILURE(); return false; }
void Int(int i) { actual_ = i; step_++; } bool Int(int i) { actual_ = i; step_++; return true; }
unsigned step_; unsigned step_;
int actual_; int actual_;
...@@ -48,8 +48,8 @@ struct ParseIntHandler : BaseReaderHandler<> { ...@@ -48,8 +48,8 @@ struct ParseIntHandler : BaseReaderHandler<> {
struct ParseUintHandler : BaseReaderHandler<> { struct ParseUintHandler : BaseReaderHandler<> {
ParseUintHandler() : step_(0), actual_() {} ParseUintHandler() : step_(0), actual_() {}
void Default() { FAIL(); } bool Default() { ADD_FAILURE(); return false; }
void Uint(unsigned i) { actual_ = i; step_++; } bool Uint(unsigned i) { actual_ = i; step_++; return true; }
unsigned step_; unsigned step_;
unsigned actual_; unsigned actual_;
...@@ -57,8 +57,8 @@ struct ParseUintHandler : BaseReaderHandler<> { ...@@ -57,8 +57,8 @@ struct ParseUintHandler : BaseReaderHandler<> {
struct ParseInt64Handler : BaseReaderHandler<> { struct ParseInt64Handler : BaseReaderHandler<> {
ParseInt64Handler() : step_(0), actual_() {} ParseInt64Handler() : step_(0), actual_() {}
void Default() { FAIL(); } bool Default() { ADD_FAILURE(); return false; }
void Int64(int64_t i) { actual_ = i; step_++; } bool Int64(int64_t i) { actual_ = i; step_++; return true; }
unsigned step_; unsigned step_;
int64_t actual_; int64_t actual_;
...@@ -66,8 +66,8 @@ struct ParseInt64Handler : BaseReaderHandler<> { ...@@ -66,8 +66,8 @@ struct ParseInt64Handler : BaseReaderHandler<> {
struct ParseUint64Handler : BaseReaderHandler<> { struct ParseUint64Handler : BaseReaderHandler<> {
ParseUint64Handler() : step_(0), actual_() {} ParseUint64Handler() : step_(0), actual_() {}
void Default() { FAIL(); } bool Default() { ADD_FAILURE(); return false; }
void Uint64(uint64_t i) { actual_ = i; step_++; } bool Uint64(uint64_t i) { actual_ = i; step_++; return true; }
unsigned step_; unsigned step_;
uint64_t actual_; uint64_t actual_;
...@@ -75,8 +75,8 @@ struct ParseUint64Handler : BaseReaderHandler<> { ...@@ -75,8 +75,8 @@ struct ParseUint64Handler : BaseReaderHandler<> {
struct ParseDoubleHandler : BaseReaderHandler<> { struct ParseDoubleHandler : BaseReaderHandler<> {
ParseDoubleHandler() : step_(0), actual_() {} ParseDoubleHandler() : step_(0), actual_() {}
void Default() { FAIL(); } bool Default() { ADD_FAILURE(); return false; }
void Double(double d) { actual_ = d; step_++; } bool Double(double d) { actual_ = d; step_++; return true; }
unsigned step_; unsigned step_;
double actual_; double actual_;
...@@ -194,8 +194,8 @@ struct ParseStringHandler : BaseReaderHandler<Encoding> { ...@@ -194,8 +194,8 @@ struct ParseStringHandler : BaseReaderHandler<Encoding> {
ParseStringHandler(const ParseStringHandler&); ParseStringHandler(const ParseStringHandler&);
ParseStringHandler& operator=(const ParseStringHandler&); ParseStringHandler& operator=(const ParseStringHandler&);
void Default() { FAIL(); } bool Default() { ADD_FAILURE(); return false; }
void String(const typename Encoding::Ch* str, size_t length, bool copy) { bool String(const typename Encoding::Ch* str, size_t length, bool copy) {
EXPECT_EQ(0, str_); EXPECT_EQ(0, str_);
if (copy) { if (copy) {
str_ = (typename Encoding::Ch*)malloc((length + 1) * sizeof(typename Encoding::Ch)); str_ = (typename Encoding::Ch*)malloc((length + 1) * sizeof(typename Encoding::Ch));
...@@ -204,7 +204,8 @@ struct ParseStringHandler : BaseReaderHandler<Encoding> { ...@@ -204,7 +204,8 @@ struct ParseStringHandler : BaseReaderHandler<Encoding> {
else else
str_ = str; str_ = str;
length_ = length; length_ = length;
copy_ = copy; copy_ = copy;
return true;
} }
const typename Encoding::Ch* str_; const typename Encoding::Ch* str_;
...@@ -411,10 +412,10 @@ template <unsigned count> ...@@ -411,10 +412,10 @@ template <unsigned count>
struct ParseArrayHandler : BaseReaderHandler<> { struct ParseArrayHandler : BaseReaderHandler<> {
ParseArrayHandler() : step_(0) {} ParseArrayHandler() : step_(0) {}
void Default() { FAIL(); } bool Default() { ADD_FAILURE(); return false; }
void Uint(unsigned i) { EXPECT_EQ(step_, i); step_++; } bool Uint(unsigned i) { EXPECT_EQ(step_, i); step_++; return true; }
void StartArray() { EXPECT_EQ(0u, step_); step_++; } bool StartArray() { EXPECT_EQ(0u, step_); step_++; return true; }
void EndArray(SizeType) { step_++; } bool EndArray(SizeType) { step_++; return true; }
unsigned step_; unsigned step_;
}; };
...@@ -462,42 +463,42 @@ TEST(Reader, ParseArray_Error) { ...@@ -462,42 +463,42 @@ TEST(Reader, ParseArray_Error) {
struct ParseObjectHandler : BaseReaderHandler<> { struct ParseObjectHandler : BaseReaderHandler<> {
ParseObjectHandler() : step_(0) {} ParseObjectHandler() : step_(0) {}
void Null() { EXPECT_EQ(8u, step_); step_++; } bool Null() { EXPECT_EQ(8u, step_); step_++; return true; }
void Bool(bool b) { bool Bool(bool b) {
switch(step_) { switch(step_) {
case 4: EXPECT_TRUE(b); step_++; break; case 4: EXPECT_TRUE(b); step_++; return true;
case 6: EXPECT_FALSE(b); step_++; break; case 6: EXPECT_FALSE(b); step_++; return true;
default: FAIL(); default: ADD_FAILURE(); return false;
} }
} }
void Int(int i) { bool Int(int i) {
switch(step_) { switch(step_) {
case 10: EXPECT_EQ(123, i); step_++; break; case 10: EXPECT_EQ(123, i); step_++; return true;
case 15: EXPECT_EQ(1, i); step_++; break; case 15: EXPECT_EQ(1, i); step_++; return true;
case 16: EXPECT_EQ(2, i); step_++; break; case 16: EXPECT_EQ(2, i); step_++; return true;
case 17: EXPECT_EQ(3, i); step_++; break; case 17: EXPECT_EQ(3, i); step_++; return true;
default: FAIL(); default: ADD_FAILURE(); return false;
} }
} }
void Uint(unsigned i) { Int(i); } bool Uint(unsigned i) { return Int(i); }
void Double(double d) { EXPECT_EQ(12u, step_); EXPECT_EQ(3.1416, d); step_++; } bool Double(double d) { EXPECT_EQ(12u, step_); EXPECT_EQ(3.1416, d); step_++; return true; }
void String(const char* str, size_t, bool) { bool String(const char* str, size_t, bool) {
switch(step_) { switch(step_) {
case 1: EXPECT_STREQ("hello", str); step_++; break; case 1: EXPECT_STREQ("hello", str); step_++; return true;
case 2: EXPECT_STREQ("world", str); step_++; break; case 2: EXPECT_STREQ("world", str); step_++; return true;
case 3: EXPECT_STREQ("t", str); step_++; break; case 3: EXPECT_STREQ("t", str); step_++; return true;
case 5: EXPECT_STREQ("f", str); step_++; break; case 5: EXPECT_STREQ("f", str); step_++; return true;
case 7: EXPECT_STREQ("n", str); step_++; break; case 7: EXPECT_STREQ("n", str); step_++; return true;
case 9: EXPECT_STREQ("i", str); step_++; break; case 9: EXPECT_STREQ("i", str); step_++; return true;
case 11: EXPECT_STREQ("pi", str); step_++; break; case 11: EXPECT_STREQ("pi", str); step_++; return true;
case 13: EXPECT_STREQ("a", str); step_++; break; case 13: EXPECT_STREQ("a", str); step_++; return true;
default: FAIL(); default: ADD_FAILURE(); return false;
} }
} }
void StartObject() { EXPECT_EQ(0u, step_); step_++; } bool StartObject() { EXPECT_EQ(0u, step_); step_++; return true; }
void EndObject(SizeType memberCount) { EXPECT_EQ(19u, step_); EXPECT_EQ(7u, memberCount); step_++;} bool EndObject(SizeType memberCount) { EXPECT_EQ(19u, step_); EXPECT_EQ(7u, memberCount); step_++; return true; }
void StartArray() { EXPECT_EQ(14u, step_); step_++; } bool StartArray() { EXPECT_EQ(14u, step_); step_++; return true; }
void EndArray(SizeType elementCount) { EXPECT_EQ(18u, step_); EXPECT_EQ(3u, elementCount); step_++;} bool EndArray(SizeType elementCount) { EXPECT_EQ(18u, step_); EXPECT_EQ(3u, elementCount); step_++; return true; }
unsigned step_; unsigned step_;
}; };
...@@ -529,9 +530,9 @@ TEST(Reader, ParseObject) { ...@@ -529,9 +530,9 @@ TEST(Reader, ParseObject) {
struct ParseEmptyObjectHandler : BaseReaderHandler<> { struct ParseEmptyObjectHandler : BaseReaderHandler<> {
ParseEmptyObjectHandler() : step_(0) {} ParseEmptyObjectHandler() : step_(0) {}
void Default() { FAIL(); } bool Default() { ADD_FAILURE(); return false; }
void StartObject() { EXPECT_EQ(0u, step_); step_++; } bool StartObject() { EXPECT_EQ(0u, step_); step_++; return true; }
void EndObject(SizeType) { EXPECT_EQ(1u, step_); step_++; } bool EndObject(SizeType) { EXPECT_EQ(1u, step_); step_++; return true; }
unsigned step_; unsigned step_;
}; };
...@@ -756,42 +757,42 @@ struct IterativeParsingReaderHandler { ...@@ -756,42 +757,42 @@ struct IterativeParsingReaderHandler {
} }
bool IsNullTriggered; bool IsNullTriggered;
void Null() { IsNullTriggered = true; } bool Null() { IsNullTriggered = true; return true; }
bool IsBoolTriggered; bool IsBoolTriggered;
void Bool(bool) { IsBoolTriggered = true; } bool Bool(bool) { IsBoolTriggered = true; return true; }
bool IsIntTriggered; bool IsIntTriggered;
void Int(int) { IsIntTriggered = true; } bool Int(int) { IsIntTriggered = true; return true; }
bool IsUintTriggered; bool IsUintTriggered;
void Uint(unsigned) { IsUintTriggered = true; } bool Uint(unsigned) { IsUintTriggered = true; return true; }
bool IsInt64Triggered; bool IsInt64Triggered;
void Int64(int64_t) { IsInt64Triggered = true; } bool Int64(int64_t) { IsInt64Triggered = true; return true; }
bool IsUint64Triggered; bool IsUint64Triggered;
void Uint64(uint64_t) { IsUint64Triggered = true; } bool Uint64(uint64_t) { IsUint64Triggered = true; return true; }
bool IsDoubleTriggered; bool IsDoubleTriggered;
void Double(double) { IsDoubleTriggered = true; } bool Double(double) { IsDoubleTriggered = true; return true; }
bool IsStringTriggered; bool IsStringTriggered;
void String(const Ch*, SizeType, bool) { IsStringTriggered = true; } bool String(const Ch*, SizeType, bool) { IsStringTriggered = true; return true; }
bool IsStartObjectTriggered; bool IsStartObjectTriggered;
void StartObject() { IsStartObjectTriggered = true; } bool StartObject() { IsStartObjectTriggered = true; return true; }
bool IsEndObjectTriggered; bool IsEndObjectTriggered;
SizeType MemberCount; SizeType MemberCount;
void EndObject(SizeType c) { IsEndObjectTriggered = true; MemberCount = c; } bool EndObject(SizeType c) { IsEndObjectTriggered = true; MemberCount = c; return true; }
bool IsStartArrayTriggered; bool IsStartArrayTriggered;
void StartArray() { IsStartArrayTriggered = true; } bool StartArray() { IsStartArrayTriggered = true; return true; }
bool IsEndArrayTriggered; bool IsEndArrayTriggered;
SizeType ElementCount; 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)\ #define ITERATIVE_PARSING_PREPARE_STATE_UNTIL(text, pos)\
......
...@@ -83,13 +83,13 @@ TEST(Writer,DoublePrecision) { ...@@ -83,13 +83,13 @@ TEST(Writer,DoublePrecision) {
buffer.Clear(); buffer.Clear();
} }
{ // explicit individual double precisions { // explicit individual double precisions
writer.SetDoublePrecision(2) writer.SetDoublePrecision(2);
.StartArray() writer.StartArray();
.Double(1.2345,5) writer.Double(1.2345, 5);
.Double(1.2345678,9) writer.Double(1.2345678, 9);
.Double(0.123456789012,12) writer.Double(0.123456789012, 12);
.Double(1234567.8,8) writer.Double(1234567.8, 8);
.EndArray(); writer.EndArray();
EXPECT_EQ(writer.GetDoublePrecision(), 2); EXPECT_EQ(writer.GetDoublePrecision(), 2);
EXPECT_STREQ(json, buffer.GetString()); EXPECT_STREQ(json, buffer.GetString());
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment