Commit 389fe87c authored by miloyip's avatar miloyip

Merge branch 'master' into issue23errorcode

Conflicts:
	example/condense/condense.cpp
	include/rapidjson/reader.h
	test/unittest/readertest.cpp
parents c14c5ff2 d8ed6095
......@@ -21,6 +21,8 @@ before_script:
- cd "${TRAVIS_BUILD_DIR}"
script:
- make -C build/gmake -f test.make unittest
- make -C build/gmake -f test.make
- make -C build/gmake -f example.make
- cd bin
- ./unittest_${config_suffix}
- ./perftest_${config_suffix}
......@@ -149,8 +149,8 @@ solution "example"
configuration "vs*"
defines { "_CRT_SECURE_NO_WARNINGS" }
configuration "gmake"
buildoptions "-Weverything"
-- configuration "gmake"
-- buildoptions "-Weverything"
project "condense"
kind "ConsoleApp"
......@@ -176,3 +176,8 @@ solution "example"
kind "ConsoleApp"
files "../example/serialize/*"
setTargetObjDir("../bin")
project "simpledom"
kind "ConsoleApp"
files "../example/simpledom/*"
setTargetObjDir("../bin")
%.pdf: %.dot
dot $< -Tpdf -o $@
%.png: %.dot
dot $< -Tpng -o $@
DOTFILES = $(basename $(wildcard *.dot))
all: $(addsuffix .png, $(DOTFILES)) #$(addsuffix .pdf, $(DOTFILES))
digraph {
compound=true
fontname="Inconsolata, Consolas"
fontsize=10
margin="0,0"
ranksep=0.2
penwidth=0.5
node [fontname="Inconsolata, Consolas", fontsize=10, penwidth=0.5]
edge [fontname="Inconsolata, Consolas", fontsize=10, arrowhead=normal]
{
node [shape=record, fontsize="8", margin="0.04", height=0.2, color=gray]
srcjson [label="\{|p|r|o|j|e|c|t|\"|:|\"|r|a|p|i|d|j|s|o|n|\"|,|\"|s|t|a|r|s|\"|:|1|0|\}"]
dstjson [label="\{|p|r|o|j|e|c|t|\"|:|\"|r|a|p|i|d|j|s|o|n|\"|,|\"|s|t|a|r|s|\"|:|1|1|\}"]
}
{
node [shape="box", style="filled", fillcolor="gray95"]
Document2 [label="(Modified) Document"]
Writer
}
subgraph cluster1 {
margin="10,10"
labeljust="left"
label = "Document"
style=filled
fillcolor=gray95
node [shape=Mrecord, style=filled, colorscheme=spectral7]
root [label="{object|}", fillcolor=3]
{
project [label="{string|\"project\"}", fillcolor=5]
rapidjson [label="{string|\"rapidjson\"}", fillcolor=5]
stars [label="{string|\"stars\"}", fillcolor=5]
ten [label="{number|10}", fillcolor=6]
}
edge [arrowhead=vee]
root -> { project, stars }
edge [arrowhead="none"]
project -> rapidjson
stars -> ten
}
srcjson -> root [label=" Parse()", lhead="cluster1"]
ten -> Document2 [label=" Increase \"stars\"", ltail="cluster1" ]
Document2 -> Writer [label=" Traverse DOM by Accept()"]
Writer -> dstjson [label=" Output to StringBuffer"]
}
\ No newline at end of file
# RapidJSON Features
## General
* Cross-platform
* Compilers: Visual Studio, gcc, clang, etc.
* Architectures: x86, x64, ARM, etc.
* Operating systems: Windows, Mac OS X, Linux, iOS, Android, etc.
* Easy installation
* Header files only library. Just copy the headers to your project.
* Self-contained, minimal dependences
* No STL, BOOST, etc.
* Only included `<cstdio>`, `<cstdlib>`, `<cstring>`, `<inttypes.h>`, `<new>`, `<stdint.h>`. *
* Without C++ exception, RTTI
* High performance
* Use template and inline functions to reduce function call overheads.
* Optional SSE2/SSE4.1 support.
## Standard compliance
* RapidJSON should be fully RFC4627/ECMA-404 compliance.
* Support unicode surrogate.
* Support null character (`"\u0000"`)
* For example, `["Hello\u0000World"]` can be parsed and handled gracefully. There is API for getting/setting lengths of string.
## Unicode
* Support UTF-8, UTF-16, UTF-32 encodings, including little endian and big endian.
* These encodings are used in input/output streams and in-memory representation.
* Support automatic detection of encodings in input stream.
* Support transcoding between encodings internally.
* For example, you can read a UTF-8 file and let RapidJSON transcode the JSON strings into UTF-16 in the DOM.
* Support encoding validation internally.
* For example, you can read a UTF-8 file, and let RapidJSON check whether all JSON strings are valid UTF-8 byte sequence.
* Support custom character types.
* By default the character types are `char` for UTF8, `wchar_t` for UTF16, `uint32_t` for UTF32.
* Support custom encodings.
## API styles
* SAX (Simple API for XML) style API
* Similar to [SAX](http://en.wikipedia.org/wiki/Simple_API_for_XML), RapidJSON provides a event sequential access parser API (`GenericReader`). It also provides a generator API (`GenericWriter`) which consumes the same set of events.
* DOM (Document Object Model) style API
* Similar to [DOM](http://en.wikipedia.org/wiki/Document_Object_Model) for HTML/XML, RapidJSON can parse JSON into a DOM representation (`GenericDocument`), for easy manipulation, and finally stringify back to JSON if needed.
* The DOM style API (`GenericDocument`) is actually implemented with SAX style API (`GenericReader`). SAX is faster but sometimes DOM is easier. Users can pick their choices according to scenarios.
## DOM (Document)
* Support insitu parsing.
* Parse JSON string values in-place at the source JSON, and then the DOM points to addresses of those strings.
* Faster than convention parsing: no allocation for strings, no copy (if string does not contain escapes), cache-friendly.
* Support 32-bit/64-bit signed/unsigned integer and `double` for JSON number type.
* RapidJSON checks range of numerical values for conversions.
## SAX (Reader)
* Support comprehensive error code if parsing failed.
* Support localizable error message.
## SAX (Writer)
* Support PrettyWriter for adding newlines and indentations.
* Support custom precision for floating point values.
## Stream
* Support `GenericStringBuffer` for storing the output JSON as string.
* Support `FileReadStream`/`FileWriteStream` for input/output `FILE` object.
* Support custom streams.
## Memory
* Minimize memory overheads for DOM.
* Each JSON value occupies exactly 16/20 bytes for most 32/64-bit machines (excluding text string).
* Support fast default allocator.
* A stack-based allocator (allocate sequentially, prohibit to free individual allocations, suitable for parsing).
* User can provide a pre-allocated buffer. (Possible to parse a number of JSONs without any CRT allocation)
* Support standard CRT(C-runtime) allocator.
* Support custom allocators.
......@@ -23,7 +23,7 @@ int main(int, char*[]) {
Writer<FileWriteStream> writer(os);
// JSON reader parse from the input stream and let writer generate the output.
if (!reader.Parse<0>(is, writer)) {
if (!reader.Parse(is, writer)) {
fprintf(stderr, "\nError(%u): %s\n", (unsigned)reader.GetErrorOffset(), GetParseError_En(reader.GetParseErrorCode()));
return 1;
}
......
// JSON simple example
// This example does not handle errors.
#include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"
#include <iostream>
using namespace rapidjson;
int main() {
// 1. Parse a JSON string into DOM.
const char* json = "{\"project\":\"rapidjson\",\"stars\":10}";
Document d;
d.Parse(json);
// 2. Modify it by DOM.
Value& s = d["stars"];
s.SetInt(s.GetInt() + 1);
// 3. Stringify the DOM
StringBuffer buffer;
Writer<StringBuffer> writer(buffer);
d.Accept(writer);
// Output {"project":"rapidjson","stars":11}
std::cout << buffer.GetString() << std::endl;
return 0;
}
......@@ -19,14 +19,14 @@ int main(int, char*[]) {
#if 0
// "normal" parsing, decode strings to new buffers. Can use other input stream via ParseStream().
if (document.Parse<0>(json).HasParseError())
if (document.Parse(json).HasParseError())
return 1;
#else
// In-situ parsing, decode strings directly in the source string. Source must be string.
{
char buffer[sizeof(json)];
memcpy(buffer, json, sizeof(json));
if (document.ParseInsitu<0>(buffer).HasParseError())
if (document.ParseInsitu(buffer).HasParseError())
return 1;
}
#endif
......
......@@ -202,7 +202,7 @@ public:
*/
template <typename SourceAllocator>
GenericValue& CopyFrom(const GenericValue<Encoding,SourceAllocator>& rhs, Allocator& allocator) {
RAPIDJSON_ASSERT((void*)this != (void*)&rhs);
RAPIDJSON_ASSERT((void*)this != (void const*)&rhs);
this->~GenericValue();
new (this) GenericValue(rhs,allocator);
return *this;
......@@ -818,6 +818,11 @@ public:
return ParseStream<parseFlags,Encoding,InputStream>(is);
}
template <typename InputStream>
GenericDocument& ParseStream(InputStream& is) {
return ParseStream<0, Encoding, InputStream>(is);
}
//! Parse JSON text from a mutable string.
/*! \tparam parseFlags Combination of ParseFlag.
\param str Mutable zero-terminated string to be parsed.
......@@ -834,6 +839,10 @@ public:
return ParseInsitu<parseFlags, Encoding>(str);
}
GenericDocument& ParseInsitu(Ch* str) {
return ParseInsitu<0, Encoding>(str);
}
//! Parse JSON text from a read-only string.
/*! \tparam parseFlags Combination of ParseFlag (must not contain kParseInsituFlag).
\param str Read-only zero-terminated string to be parsed.
......@@ -850,6 +859,10 @@ public:
return Parse<parseFlags, Encoding>(str);
}
GenericDocument& Parse(const Ch* str) {
return Parse<0>(str);
}
//! Whether a parse error was occured in the last parsing.
bool HasParseError() const { return parseErrorCode_ != kParseErrorNone; }
......
......@@ -69,6 +69,11 @@ private:
bool eof_;
};
template<>
struct StreamTraits<FileReadStream> {
typedef FileReadStream StreamCopyType; // Enable stream copy optimization.
};
} // namespace rapidjson
#endif // RAPIDJSON_FILESTREAM_H_
......@@ -193,6 +193,24 @@ concept Stream {
\endcode
*/
//! Provides additional information for stream.
/*!
By using traits pattern, this type provides a default configuration for stream.
For custom stream, this type can be specialized for other configuration.
See TEST(Reader, CustomStringStream) in readertest.cpp for example.
*/
template<typename Stream>
struct StreamTraits {
//! Whether to make local copy of stream for optimization during parsing.
/*!
If it is defined as Stream&, it will not make a local copy.
If it is defined as Stream, it will make a local copy for optimization.
By default, for safety, streams do not use local copy optimization, i.e. it is defined as Stream&.
Stream that can be copied fast should specialize this, like StreamTraits<StringStream>.
*/
typedef Stream& StreamCopyType;
};
//! Put N copies of a character to a stream.
template<typename Stream, typename Ch>
inline void PutN(Stream& stream, Ch c, size_t n) {
......@@ -225,6 +243,11 @@ struct GenericStringStream {
const Ch* head_; //!< Original head of the string.
};
template <typename Encoding>
struct StreamTraits<GenericStringStream<Encoding> > {
typedef GenericStringStream<Encoding> StreamCopyType; // Enable stream copy optimization.
};
typedef GenericStringStream<UTF8<> > StringStream;
///////////////////////////////////////////////////////////////////////////////
......@@ -256,6 +279,11 @@ struct GenericInsituStringStream {
Ch* head_;
};
template <typename Encoding>
struct StreamTraits<GenericInsituStringStream<Encoding> > {
typedef GenericInsituStringStream<Encoding> StreamCopyType; // Enable stream copy optimization.
};
typedef GenericInsituStringStream<UTF8<> > InsituStringStream;
///////////////////////////////////////////////////////////////////////////////
......
......@@ -135,7 +135,7 @@ struct BaseReaderHandler {
*/
template<typename InputStream>
void SkipWhitespace(InputStream& is) {
InputStream s = is; // Use a local copy for optimization
typename StreamTraits<InputStream>::StreamCopyType s = is; // Use a local copy for optimization
while (s.Peek() == ' ' || s.Peek() == '\n' || s.Peek() == '\r' || s.Peek() == '\t')
s.Take();
is = s;
......@@ -283,8 +283,15 @@ public:
return !HasParseError();
}
template <typename InputStream, typename Handler>
bool Parse(InputStream& is, Handler& handler) {
return Parse<0>(is, handler);
}
bool HasParseError() const { return parseErrorCode_ != kParseErrorNone; }
ParseErrorCode GetParseErrorCode() const { return parseErrorCode_; }
size_t GetErrorOffset() const { return errorOffset_; }
private:
......@@ -399,7 +406,7 @@ private:
// Helper function to parse four hexidecimal digits in \uXXXX in ParseString().
template<typename InputStream>
unsigned ParseHex4(InputStream& is) {
InputStream s = is; // Use a local copy for optimization
typename StreamTraits<InputStream>::StreamCopyType s = is; // Use a local copy for optimization
unsigned codepoint = 0;
for (int i = 0; i < 4; i++) {
Ch c = s.Take();
......@@ -440,7 +447,7 @@ private:
// Parse string and generate String event. Different code paths for kParseInsituFlag.
template<unsigned parseFlags, typename InputStream, typename Handler>
void ParseString(InputStream& is, Handler& handler) {
InputStream s = is; // Local copy for optimization
typename StreamTraits<InputStream>::StreamCopyType s = is; // Local copy for optimization
if (parseFlags & kParseInsituFlag) {
Ch *head = s.PutBegin();
ParseStringToStream<parseFlags, SourceEncoding, SourceEncoding>(s, s);
......@@ -520,7 +527,7 @@ private:
template<unsigned parseFlags, typename InputStream, typename Handler>
void ParseNumber(InputStream& is, Handler& handler) {
InputStream s = is; // Local copy for optimization
typename StreamTraits<InputStream>::StreamCopyType s = is; // Local copy for optimization
// Parse minus
bool minus = false;
if (s.Peek() == '-') {
......
......@@ -16,7 +16,9 @@ Rapidjson is a JSON parser and generator for C++. It was inspired by [rapidxml](
* Rapidjson is memory friendly. Each JSON value occupies exactly 16/20 bytes for most 32/64-bit machines (excluding text string). By default it uses a fast memory allocator, and the parser allocates memory compactly during parsing.
For the full features please refer to the user guide.
* Rapidjson is Unicode friendly. It supports UTF-8, UTF-16, UTF-32 (LE & BE), and their detection, validataton and transcoding internally. For example, you can read a UTF-8 file and let rapidjson transcode the JSON strings into UTF-16 in the DOM. It also supports surrogates and "\u0000" (null character).
More features can be read [here](doc/features.md).
JSON(JavaScript Object Notation) is a light-weight data exchange format. Rapidjson should be in fully compliance with RFC4627/ECMA-404. More information about JSON can be obtained at
* [Introducing JSON](http://json.org/)
......@@ -39,9 +41,51 @@ Rapidjson is a header-only C++ library. Just copy the `rapidjson/include/rapidjs
To build the tests and examples:
1. Obtain [premake4] (http://industriousone.com/premake/download).
1. Obtain [premake4](http://industriousone.com/premake/download).
2. Copy premake4 executable to rapidjson/build (or system path)
3. Run `rapidjson/build/premake.bat` on Windows, `rapidjson/build/premake.sh` on Linux or other platforms
4. On Windows, build the solution at `rapidjson/build/vs2008/` or `/vs2010/`
5. On other platforms, run GNU make at `rapidjson/build/gmake/` (e.g., `make -f test.make config=release32`, `make -f example.make config=debug32`)
6. On success, the executable are generated at `rapidjson/bin`
## Usage at a glance
This simple example parses a JSON string into a document (DOM), make a simple modification of the DOM, and finally stringify the DOM to a JSON string.
```cpp
// example/simpledom/simpledom.cpp
#include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"
#include <iostream>
using namespace rapidjson;
int main() {
// 1. Parse a JSON string into DOM.
const char* json = "{\"project\":\"rapidjson\",\"stars\":10}";
Document d;
d.Parse(json);
// 2. Modify it by DOM.
Value& s = d["stars"];
s.SetInt(s.GetInt() + 1);
// 3. Stringify the DOM
StringBuffer buffer;
Writer<StringBuffer> writer(buffer);
d.Accept(writer);
// Output {"project":"rapidjson","stars":11}
std::cout << buffer.GetString() << std::endl;
return 0;
}
```
Note that this exmample did not handle potential errors.
The following diagram shows the process.
![simpledom](doc/diagram/simpledom.png?raw=true)
More [examples](example/) are avaliable.
......@@ -28,7 +28,7 @@ public:
temp_ = (char *)malloc(length_ + 1);
// Parse as a document
EXPECT_FALSE(doc_.Parse<0>(json_).IsNull());
EXPECT_FALSE(doc_.Parse(json_).IsNull());
}
virtual void TearDown() {
......@@ -66,7 +66,7 @@ TEST_F(RapidJson, SIMD_SUFFIX(ReaderParse_DummyHandler)) {
StringStream s(json_);
BaseReaderHandler<> h;
Reader reader;
EXPECT_TRUE(reader.Parse<0>(s, h));
EXPECT_TRUE(reader.Parse(s, h));
}
}
......@@ -88,7 +88,7 @@ TEST_F(RapidJson, SIMD_SUFFIX(DoucmentParseInsitu_MemoryPoolAllocator)) {
//MemoryPoolAllocator<> allocator(userBuffer, userBufferSize);
//Document doc(&allocator);
Document doc;
doc.ParseInsitu<0>(temp_);
doc.ParseInsitu(temp_);
ASSERT_TRUE(doc.IsObject());
//if (i == 0) {
// size_t size = doc.GetAllocator().Size();
......@@ -110,7 +110,7 @@ TEST_F(RapidJson, SIMD_SUFFIX(DoucmentParse_MemoryPoolAllocator)) {
//MemoryPoolAllocator<> allocator(userBuffer, userBufferSize);
//Document doc(&allocator);
Document doc;
doc.Parse<0>(json_);
doc.Parse(json_);
ASSERT_TRUE(doc.IsObject());
//if (i == 0) {
// size_t size = doc.GetAllocator().Size();
......@@ -128,7 +128,7 @@ TEST_F(RapidJson, SIMD_SUFFIX(DoucmentParse_CrtAllocator)) {
for (size_t i = 0; i < kTrialCount; i++) {
memcpy(temp_, json_, length_ + 1);
GenericDocument<UTF8<>, CrtAllocator> doc;
doc.Parse<0>(temp_);
doc.Parse(temp_);
ASSERT_TRUE(doc.IsObject());
}
}
......@@ -234,7 +234,7 @@ TEST_F(RapidJson, internal_Pow10) {
TEST_F(RapidJson, SIMD_SUFFIX(Whitespace)) {
for (size_t i = 0; i < kTrialCount; i++) {
Document doc;
ASSERT_TRUE(doc.Parse<0>(whitespace_).IsArray());
ASSERT_TRUE(doc.Parse(whitespace_).IsArray());
}
}
......@@ -279,7 +279,7 @@ TEST_F(RapidJson, SIMD_SUFFIX(ReaderParse_DummyHandler_FileReadStream)) {
FileReadStream s(fp, buffer, sizeof(buffer));
BaseReaderHandler<> h;
Reader reader;
reader.Parse<0>(s, h);
reader.Parse(s, h);
fclose(fp);
}
}
......
......@@ -8,7 +8,7 @@ using namespace rapidjson;
TEST(Document, Parse) {
Document doc;
doc.Parse<0>(" { \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3, 4] } ");
doc.Parse(" { \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3, 4] } ");
EXPECT_TRUE(doc.IsObject());
......@@ -59,7 +59,7 @@ struct OutputStringStream : public std::ostringstream {
TEST(Document, AcceptWriter) {
Document doc;
doc.Parse<0>(" { \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3, 4] } ");
doc.Parse(" { \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3, 4] } ");
OutputStringStream os;
Writer<OutputStringStream> writer(os);
......
......@@ -42,7 +42,7 @@ TEST(JsonChecker, Reader) {
}
GenericDocument<UTF8<>, CrtAllocator> document; // Use Crt allocator to check exception-safety (no memory leak)
if (!document.Parse<0>((const char*)json).HasParseError())
if (!document.Parse((const char*)json).HasParseError())
FAIL();
//printf("%s(%u):%s\n", filename, (unsigned)document.GetErrorOffset(), document.GetParseError());
free(json);
......@@ -63,7 +63,7 @@ TEST(JsonChecker, Reader) {
}
GenericDocument<UTF8<>, CrtAllocator> document; // Use Crt allocator to check exception-safety (no memory leak)
document.Parse<0>((const char*)json);
document.Parse((const char*)json);
EXPECT_TRUE(!document.HasParseError());
free(json);
}
......
......@@ -603,3 +603,51 @@ TEST(Reader, SkipWhitespace) {
EXPECT_EQ(expected[i], ss.Take());
}
}
// Test implementing a stream without copy stream optimization.
// Clone from GenericStringStream except that copy constructor is disabled.
template <typename Encoding>
class CustomStringStream {
public:
typedef typename Encoding::Ch Ch;
CustomStringStream(const Ch *src) : src_(src), head_(src) {}
Ch Peek() const { return *src_; }
Ch Take() { return *src_++; }
size_t Tell() const { return static_cast<size_t>(src_ - head_); }
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
void Put(Ch) { RAPIDJSON_ASSERT(false); }
void Flush() { RAPIDJSON_ASSERT(false); }
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
private:
// Not support copy constructor.
CustomStringStream(const CustomStringStream&);
const Ch* src_; //!< Current read position.
const Ch* head_; //!< Original head of the string.
};
// If the following code is compiled, it should generate compilation error as predicted.
// Because CustomStringStream<> is not copyable via making copy constructor private.
#if 0
namespace rapidjson {
template <typename Encoding>
struct StreamTraits<CustomStringStream<Encoding> > {
typedef CustomStringStream<Encoding> StreamCopyType;
};
} // namespace rapdijson
#endif
TEST(Reader, CustomStringStream) {
const char* json = "{ \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3] } ";
CustomStringStream<UTF8<char> > s(json);
ParseObjectHandler h;
Reader reader;
reader.ParseObject<0>(s, h);
EXPECT_EQ(20u, h.step_);
}
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