Commit d4da07c5 authored by thebusytypist's avatar thebusytypist

Merge upstream/master.

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