Add license and change indents from tab to space.

This commit is contained in:
Milo Yip 2014-08-11 22:26:45 +08:00
parent adb3974e4d
commit 0dbcc1cf2e
51 changed files with 9279 additions and 8497 deletions

View File

@ -1,165 +1,165 @@
function setTargetObjDir(outDir) function setTargetObjDir(outDir)
for _, cfg in ipairs(configurations()) do for _, cfg in ipairs(configurations()) do
for _, plat in ipairs(platforms()) do for _, plat in ipairs(platforms()) do
local action = _ACTION or "" local action = _ACTION or ""
local prj = project() local prj = project()
--"_debug_win32_vs2008" --"_debug_win32_vs2008"
local suffix = "_" .. cfg .. "_" .. plat .. "_" .. action local suffix = "_" .. cfg .. "_" .. plat .. "_" .. action
targetPath = outDir targetPath = outDir
suffix = string.lower(suffix) suffix = string.lower(suffix)
local obj_path = "../intermediate/" .. cfg .. "/" .. action .. "/" .. prj.name local obj_path = "../intermediate/" .. cfg .. "/" .. action .. "/" .. prj.name
obj_path = string.lower(obj_path) obj_path = string.lower(obj_path)
configuration {cfg, plat} configuration {cfg, plat}
targetdir(targetPath) targetdir(targetPath)
objdir(obj_path) objdir(obj_path)
targetsuffix(suffix) targetsuffix(suffix)
end end
end end
end end
function linkLib(libBaseName) function linkLib(libBaseName)
for _, cfg in ipairs(configurations()) do for _, cfg in ipairs(configurations()) do
for _, plat in ipairs(platforms()) do for _, plat in ipairs(platforms()) do
local action = _ACTION or "" local action = _ACTION or ""
local prj = project() local prj = project()
local cfgName = cfg local cfgName = cfg
--"_debug_win32_vs2008" --"_debug_win32_vs2008"
local suffix = "_" .. cfgName .. "_" .. plat .. "_" .. action local suffix = "_" .. cfgName .. "_" .. plat .. "_" .. action
libFullName = libBaseName .. string.lower(suffix) libFullName = libBaseName .. string.lower(suffix)
configuration {cfg, plat} configuration {cfg, plat}
links(libFullName) links(libFullName)
end end
end end
end end
solution "test" solution "test"
configurations { "debug", "release" } configurations { "debug", "release" }
platforms { "x32", "x64" } platforms { "x32", "x64" }
location ("./" .. (_ACTION or "")) location ("./" .. (_ACTION or ""))
language "C++" language "C++"
flags { "ExtraWarnings" } flags { "ExtraWarnings" }
configuration "debug" configuration "debug"
defines { "DEBUG" } defines { "DEBUG" }
flags { "Symbols" } flags { "Symbols" }
configuration "release" configuration "release"
defines { "NDEBUG" } defines { "NDEBUG" }
flags { "Optimize" } flags { "Optimize" }
configuration "vs*" configuration "vs*"
defines { "_CRT_SECURE_NO_WARNINGS" } defines { "_CRT_SECURE_NO_WARNINGS" }
configuration "gmake" configuration "gmake"
buildoptions "-msse4.2 -Wall -Wextra" buildoptions "-msse4.2 -Wall -Wextra"
project "gtest" project "gtest"
kind "StaticLib" kind "StaticLib"
defines { "GTEST_HAS_PTHREAD=0" } defines { "GTEST_HAS_PTHREAD=0" }
files { files {
"../thirdparty/gtest/src/gtest-all.cc", "../thirdparty/gtest/src/gtest-all.cc",
"../thirdparty/gtest/src/**.h", "../thirdparty/gtest/src/**.h",
} }
includedirs { includedirs {
"../thirdparty/gtest/", "../thirdparty/gtest/",
"../thirdparty/gtest/include", "../thirdparty/gtest/include",
} }
setTargetObjDir("../thirdparty/lib") setTargetObjDir("../thirdparty/lib")
project "unittest" project "unittest"
kind "ConsoleApp" kind "ConsoleApp"
if _ACTION == "gmake" then if _ACTION == "gmake" then
buildoptions "-Werror -Weffc++ -Wswitch-default" buildoptions "-Werror -Weffc++ -Wswitch-default"
end end
files { files {
"../include/**.h", "../include/**.h",
"../test/unittest/**.cpp", "../test/unittest/**.cpp",
"../test/unittest/**.h", "../test/unittest/**.h",
} }
includedirs { includedirs {
"../include/", "../include/",
"../thirdparty/gtest/include/", "../thirdparty/gtest/include/",
} }
libdirs "../thirdparty/lib" libdirs "../thirdparty/lib"
setTargetObjDir("../bin") setTargetObjDir("../bin")
linkLib "gtest" linkLib "gtest"
links "gtest" links "gtest"
project "perftest" project "perftest"
kind "ConsoleApp" kind "ConsoleApp"
files { files {
"../include/**.h", "../include/**.h",
"../test/perftest/**.cpp", "../test/perftest/**.cpp",
"../test/perftest/**.c", "../test/perftest/**.c",
"../test/perftest/**.h", "../test/perftest/**.h",
} }
includedirs { includedirs {
"../include/", "../include/",
"../thirdparty/gtest/include/", "../thirdparty/gtest/include/",
"../thirdparty/", "../thirdparty/",
"../thirdparty/jsoncpp/include/", "../thirdparty/jsoncpp/include/",
"../thirdparty/libjson/", "../thirdparty/libjson/",
"../thirdparty/yajl/include/", "../thirdparty/yajl/include/",
} }
libdirs "../thirdparty/lib" libdirs "../thirdparty/lib"
setTargetObjDir("../bin") setTargetObjDir("../bin")
linkLib "gtest" linkLib "gtest"
links "gtest" links "gtest"
solution "example" solution "example"
configurations { "debug", "release" } configurations { "debug", "release" }
platforms { "x32", "x64" } platforms { "x32", "x64" }
location ("./" .. (_ACTION or "")) location ("./" .. (_ACTION or ""))
language "C++" language "C++"
flags { "ExtraWarnings" } flags { "ExtraWarnings" }
includedirs "../include/" includedirs "../include/"
configuration "debug" configuration "debug"
defines { "DEBUG" } defines { "DEBUG" }
flags { "Symbols" } flags { "Symbols" }
configuration "release" configuration "release"
defines { "NDEBUG" } defines { "NDEBUG" }
flags { "Optimize", "EnableSSE2" } flags { "Optimize", "EnableSSE2" }
configuration "vs*" configuration "vs*"
defines { "_CRT_SECURE_NO_WARNINGS" } defines { "_CRT_SECURE_NO_WARNINGS" }
configuration "gmake" configuration "gmake"
buildoptions "-Werror -Wall -Wextra -Weffc++ -Wswitch-default" buildoptions "-Werror -Wall -Wextra -Weffc++ -Wswitch-default"
local examplepaths = os.matchdirs("../example/*") local examplepaths = os.matchdirs("../example/*")
for _, examplepath in ipairs(examplepaths) do for _, examplepath in ipairs(examplepaths) do
project(path.getname(examplepath)) project(path.getname(examplepath))
kind "ConsoleApp" kind "ConsoleApp"
files(examplepath .. "/*") files(examplepath .. "/*")
setTargetObjDir("../bin") setTargetObjDir("../bin")
end end

View File

@ -15,51 +15,51 @@ using namespace rapidjson;
template<typename OutputHandler> template<typename OutputHandler>
struct CapitalizeFilter { struct CapitalizeFilter {
CapitalizeFilter(OutputHandler& out) : out_(out), buffer_() {} CapitalizeFilter(OutputHandler& out) : out_(out), buffer_() {}
bool Null() { return out_.Null(); } bool Null() { return out_.Null(); }
bool Bool(bool b) { return out_.Bool(b); } bool Bool(bool b) { return out_.Bool(b); }
bool Int(int i) { return out_.Int(i); } bool Int(int i) { return out_.Int(i); }
bool Uint(unsigned u) { return out_.Uint(u); } bool Uint(unsigned u) { return out_.Uint(u); }
bool Int64(int64_t i) { return out_.Int64(i); } bool Int64(int64_t i) { return out_.Int64(i); }
bool Uint64(uint64_t u) { return out_.Uint64(u); } bool Uint64(uint64_t u) { return out_.Uint64(u); }
bool Double(double d) { return out_.Double(d); } bool Double(double d) { return out_.Double(d); }
bool String(const char* str, SizeType length, bool) { bool String(const char* str, SizeType length, bool) {
buffer_.clear(); buffer_.clear();
for (SizeType i = 0; i < length; i++) for (SizeType i = 0; i < length; i++)
buffer_.push_back(std::toupper(str[i])); buffer_.push_back(std::toupper(str[i]));
return out_.String(&buffer_.front(), length, true); // true = output handler need to copy the string return out_.String(&buffer_.front(), length, true); // true = output handler need to copy the string
} }
bool StartObject() { return out_.StartObject(); } bool StartObject() { return out_.StartObject(); }
bool EndObject(SizeType memberCount) { return out_.EndObject(memberCount); } bool EndObject(SizeType memberCount) { return out_.EndObject(memberCount); }
bool StartArray() { return out_.StartArray(); } bool StartArray() { return out_.StartArray(); }
bool EndArray(SizeType elementCount) { return out_.EndArray(elementCount); } bool EndArray(SizeType elementCount) { return out_.EndArray(elementCount); }
OutputHandler& out_; OutputHandler& out_;
std::vector<char> buffer_; std::vector<char> buffer_;
private: private:
CapitalizeFilter(const CapitalizeFilter&); CapitalizeFilter(const CapitalizeFilter&);
CapitalizeFilter& operator=(const CapitalizeFilter&); CapitalizeFilter& operator=(const CapitalizeFilter&);
}; };
int main(int, char*[]) { int main(int, char*[]) {
// Prepare JSON reader and input stream. // Prepare JSON reader and input stream.
Reader reader; Reader reader;
char readBuffer[65536]; char readBuffer[65536];
FileReadStream is(stdin, readBuffer, sizeof(readBuffer)); FileReadStream is(stdin, readBuffer, sizeof(readBuffer));
// Prepare JSON writer and output stream. // Prepare JSON writer and output stream.
char writeBuffer[65536]; char writeBuffer[65536];
FileWriteStream os(stdout, writeBuffer, sizeof(writeBuffer)); FileWriteStream os(stdout, writeBuffer, sizeof(writeBuffer));
Writer<FileWriteStream> writer(os); Writer<FileWriteStream> writer(os);
// JSON reader parse from the input stream and let writer generate the output. // JSON reader parse from the input stream and let writer generate the output.
CapitalizeFilter<Writer<FileWriteStream> > filter(writer); CapitalizeFilter<Writer<FileWriteStream> > filter(writer);
if (!reader.Parse(is, filter)) { if (!reader.Parse(is, filter)) {
fprintf(stderr, "\nError(%u): %s\n", (unsigned)reader.GetErrorOffset(), GetParseError_En(reader.GetParseErrorCode())); fprintf(stderr, "\nError(%u): %s\n", (unsigned)reader.GetErrorOffset(), GetParseError_En(reader.GetParseErrorCode()));
return 1; return 1;
} }
return 0; return 0;
} }

View File

@ -12,21 +12,21 @@
using namespace rapidjson; using namespace rapidjson;
int main(int, char*[]) { int main(int, char*[]) {
// Prepare JSON reader and input stream. // Prepare JSON reader and input stream.
Reader reader; Reader reader;
char readBuffer[65536]; char readBuffer[65536];
FileReadStream is(stdin, readBuffer, sizeof(readBuffer)); FileReadStream is(stdin, readBuffer, sizeof(readBuffer));
// Prepare JSON writer and output stream. // Prepare JSON writer and output stream.
char writeBuffer[65536]; char writeBuffer[65536];
FileWriteStream os(stdout, writeBuffer, sizeof(writeBuffer)); FileWriteStream os(stdout, writeBuffer, sizeof(writeBuffer));
Writer<FileWriteStream> writer(os); Writer<FileWriteStream> writer(os);
// JSON reader parse from the input stream and let writer generate the output. // JSON reader parse from the input stream and let writer generate the output.
if (!reader.Parse(is, writer)) { if (!reader.Parse(is, writer)) {
fprintf(stderr, "\nError(%u): %s\n", (unsigned)reader.GetErrorOffset(), GetParseError_En(reader.GetParseErrorCode())); fprintf(stderr, "\nError(%u): %s\n", (unsigned)reader.GetErrorOffset(), GetParseError_En(reader.GetParseErrorCode()));
return 1; return 1;
} }
return 0; return 0;
} }

View File

@ -18,38 +18,38 @@ RAPIDJSON_DIAG_OFF(effc++)
#endif #endif
struct MessageHandler : public BaseReaderHandler<> { struct MessageHandler : public BaseReaderHandler<> {
MessageHandler() : messages_(), state_(kExpectObjectStart), name_() {} MessageHandler() : messages_(), state_(kExpectObjectStart), name_() {}
bool StartObject() { bool StartObject() {
switch (state_) { switch (state_) {
case kExpectObjectStart: case kExpectObjectStart:
state_ = kExpectNameOrObjectEnd; state_ = kExpectNameOrObjectEnd;
return true; return true;
default: default:
return false; return false;
} }
} }
bool String(const char* str, SizeType length, bool) { bool String(const char* str, SizeType length, bool) {
switch (state_) { switch (state_) {
case kExpectNameOrObjectEnd: case kExpectNameOrObjectEnd:
name_ = string(str, length); name_ = string(str, length);
state_ = kExpectValue; state_ = kExpectValue;
return true; return true;
case kExpectValue: case kExpectValue:
messages_.insert(MessageMap::value_type(name_, string(str, length))); messages_.insert(MessageMap::value_type(name_, string(str, length)));
state_ = kExpectNameOrObjectEnd; state_ = kExpectNameOrObjectEnd;
return true; return true;
default: default:
return false; return false;
} }
} }
bool EndObject(SizeType) { return state_ == kExpectNameOrObjectEnd; } bool EndObject(SizeType) { return state_ == kExpectNameOrObjectEnd; }
bool Default() { return false; } // All other events are invalid. bool Default() { return false; } // All other events are invalid.
MessageMap messages_; MessageMap messages_;
enum State { enum State {
kExpectObjectStart, kExpectObjectStart,
kExpectNameOrObjectEnd, kExpectNameOrObjectEnd,
@ -66,30 +66,30 @@ void ParseMessages(const char* json, MessageMap& messages) {
Reader reader; Reader reader;
MessageHandler handler; MessageHandler handler;
StringStream ss(json); StringStream ss(json);
if (reader.Parse(ss, handler)) if (reader.Parse(ss, handler))
messages.swap(handler.messages_); // Only change it if success. messages.swap(handler.messages_); // Only change it if success.
else { else {
ParseErrorCode e = reader.GetParseErrorCode(); ParseErrorCode e = reader.GetParseErrorCode();
size_t o = reader.GetErrorOffset(); size_t o = reader.GetErrorOffset();
cout << "Error: " << GetParseError_En(e) << endl;; cout << "Error: " << GetParseError_En(e) << endl;;
cout << " at offset " << o << " near '" << string(json).substr(o, 10) << "...'" << endl; cout << " at offset " << o << " near '" << string(json).substr(o, 10) << "...'" << endl;
} }
} }
int main() { int main() {
MessageMap messages; MessageMap messages;
const char* json1 = "{ \"greeting\" : \"Hello!\", \"farewell\" : \"bye-bye!\" }"; const char* json1 = "{ \"greeting\" : \"Hello!\", \"farewell\" : \"bye-bye!\" }";
cout << json1 << endl; cout << json1 << endl;
ParseMessages(json1, messages); ParseMessages(json1, messages);
for (MessageMap::const_iterator itr = messages.begin(); itr != messages.end(); ++itr) for (MessageMap::const_iterator itr = messages.begin(); itr != messages.end(); ++itr)
cout << itr->first << ": " << itr->second << endl; cout << itr->first << ": " << itr->second << endl;
cout << endl << "Parse a JSON with invalid schema." << endl; cout << endl << "Parse a JSON with invalid schema." << endl;
const char* json2 = "{ \"greeting\" : \"Hello!\", \"farewell\" : \"bye-bye!\", \"foo\" : {} }"; const char* json2 = "{ \"greeting\" : \"Hello!\", \"farewell\" : \"bye-bye!\", \"foo\" : {} }";
cout << json2 << endl; cout << json2 << endl;
ParseMessages(json2, messages); ParseMessages(json2, messages);
return 0; return 0;
} }

View File

@ -10,21 +10,21 @@
using namespace rapidjson; using namespace rapidjson;
int main(int, char*[]) { int main(int, char*[]) {
// Prepare reader and input stream. // Prepare reader and input stream.
Reader reader; Reader reader;
char readBuffer[65536]; char readBuffer[65536];
FileReadStream is(stdin, readBuffer, sizeof(readBuffer)); FileReadStream is(stdin, readBuffer, sizeof(readBuffer));
// Prepare writer and output stream. // Prepare writer and output stream.
char writeBuffer[65536]; char writeBuffer[65536];
FileWriteStream os(stdout, writeBuffer, sizeof(writeBuffer)); FileWriteStream os(stdout, writeBuffer, sizeof(writeBuffer));
PrettyWriter<FileWriteStream> writer(os); PrettyWriter<FileWriteStream> writer(os);
// JSON reader parse from the input stream and let writer generate the output. // JSON reader parse from the input stream and let writer generate the output.
if (!reader.Parse<kParseValidateEncodingFlag>(is, writer)) { if (!reader.Parse<kParseValidateEncodingFlag>(is, writer)) {
fprintf(stderr, "\nError(%u): %s\n", (unsigned)reader.GetErrorOffset(), GetParseError_En(reader.GetParseErrorCode())); fprintf(stderr, "\nError(%u): %s\n", (unsigned)reader.GetErrorOffset(), GetParseError_En(reader.GetParseErrorCode()));
return 1; return 1;
} }
return 0; return 0;
} }

View File

@ -6,7 +6,7 @@
#include "rapidjson/prettywriter.h" #include "rapidjson/prettywriter.h"
#include "rapidjson/filereadstream.h" #include "rapidjson/filereadstream.h"
#include "rapidjson/filewritestream.h" #include "rapidjson/filewritestream.h"
#include "rapidjson/encodedstream.h" // NEW #include "rapidjson/encodedstream.h" // NEW
#include "rapidjson/error/en.h" #include "rapidjson/error/en.h"
#ifdef _WIN32 #ifdef _WIN32
#include <fcntl.h> #include <fcntl.h>
@ -17,40 +17,40 @@ using namespace rapidjson;
int main(int, char*[]) { int main(int, char*[]) {
#ifdef _WIN32 #ifdef _WIN32
// Prevent Windows converting between CR+LF and LF // Prevent Windows converting between CR+LF and LF
_setmode(_fileno(stdin), _O_BINARY); // NEW _setmode(_fileno(stdin), _O_BINARY); // NEW
_setmode(_fileno(stdout), _O_BINARY); // NEW _setmode(_fileno(stdout), _O_BINARY); // NEW
#endif #endif
// Prepare reader and input stream. // Prepare reader and input stream.
//Reader reader; //Reader reader;
GenericReader<AutoUTF<unsigned>, UTF8<> > reader; // CHANGED GenericReader<AutoUTF<unsigned>, UTF8<> > reader; // CHANGED
char readBuffer[65536]; char readBuffer[65536];
FileReadStream is(stdin, readBuffer, sizeof(readBuffer)); FileReadStream is(stdin, readBuffer, sizeof(readBuffer));
AutoUTFInputStream<unsigned, FileReadStream> eis(is); // NEW AutoUTFInputStream<unsigned, FileReadStream> eis(is); // NEW
// Prepare writer and output stream. // Prepare writer and output stream.
char writeBuffer[65536]; char writeBuffer[65536];
FileWriteStream os(stdout, writeBuffer, sizeof(writeBuffer)); FileWriteStream os(stdout, writeBuffer, sizeof(writeBuffer));
#if 1 #if 1
// Use the same Encoding of the input. Also use BOM according to input. // Use the same Encoding of the input. Also use BOM according to input.
typedef AutoUTFOutputStream<unsigned, FileWriteStream> OutputStream; // NEW typedef AutoUTFOutputStream<unsigned, FileWriteStream> OutputStream; // NEW
OutputStream eos(os, eis.GetType(), eis.HasBOM()); // NEW OutputStream eos(os, eis.GetType(), eis.HasBOM()); // NEW
PrettyWriter<OutputStream, UTF8<>, AutoUTF<unsigned> > writer(eos); // CHANGED PrettyWriter<OutputStream, UTF8<>, AutoUTF<unsigned> > writer(eos); // CHANGED
#else #else
// You may also use static bound encoding type, such as output to UTF-16LE with BOM // You may also use static bound encoding type, such as output to UTF-16LE with BOM
typedef EncodedOutputStream<UTF16LE<>,FileWriteStream> OutputStream; // NEW typedef EncodedOutputStream<UTF16LE<>,FileWriteStream> OutputStream; // NEW
OutputStream eos(os, true); // NEW OutputStream eos(os, true); // NEW
PrettyWriter<OutputStream, UTF8<>, UTF16LE<> > writer(eos); // CHANGED PrettyWriter<OutputStream, UTF8<>, UTF16LE<> > writer(eos); // CHANGED
#endif #endif
// JSON reader parse from the input stream and let writer generate the output. // JSON reader parse from the input stream and let writer generate the output.
//if (!reader.Parse<kParseValidateEncodingFlag>(is, writer)) { //if (!reader.Parse<kParseValidateEncodingFlag>(is, writer)) {
if (!reader.Parse<kParseValidateEncodingFlag>(eis, writer)) { // CHANGED if (!reader.Parse<kParseValidateEncodingFlag>(eis, writer)) { // CHANGED
fprintf(stderr, "\nError(%u): %s\n", (unsigned)reader.GetErrorOffset(), GetParseError_En(reader.GetParseErrorCode())); fprintf(stderr, "\nError(%u): %s\n", (unsigned)reader.GetErrorOffset(), GetParseError_En(reader.GetParseErrorCode()));
return 1; return 1;
} }
return 0; return 0;
} }

View File

@ -1,8 +1,8 @@
// Serialize example // Serialize example
// This example shows writing JSON string with writer directly. // This example shows writing JSON string with writer directly.
#include "rapidjson/prettywriter.h" // for stringify JSON #include "rapidjson/prettywriter.h" // for stringify JSON
#include "rapidjson/filestream.h" // wrapper of C stream for prettywriter as output #include "rapidjson/filestream.h" // wrapper of C stream for prettywriter as output
#include <cstdio> #include <cstdio>
#include <string> #include <string>
#include <vector> #include <vector>
@ -11,23 +11,23 @@ using namespace rapidjson;
class Person { class Person {
public: public:
Person(const std::string& name, unsigned age) : name_(name), age_(age) {} Person(const std::string& name, unsigned age) : name_(name), age_(age) {}
virtual ~Person(); virtual ~Person();
protected: protected:
template <typename Writer> template <typename Writer>
void Serialize(Writer& writer) const { void Serialize(Writer& writer) const {
// This base class just write out name-value pairs, without wrapping within an object. // This base class just write out name-value pairs, without wrapping within an object.
writer.String("name"); writer.String("name");
writer.String(name_.c_str(), (SizeType)name_.length()); // Suppling length of string is faster. writer.String(name_.c_str(), (SizeType)name_.length()); // Suppling length of string is faster.
writer.String("age"); writer.String("age");
writer.Uint(age_); writer.Uint(age_);
} }
private: private:
std::string name_; std::string name_;
unsigned age_; unsigned age_;
}; };
Person::~Person() { Person::~Person() {
@ -35,115 +35,115 @@ Person::~Person() {
class Education { class Education {
public: public:
Education(const std::string& school, double GPA) : school_(school), GPA_(GPA) {} Education(const std::string& school, double GPA) : school_(school), GPA_(GPA) {}
template <typename Writer> template <typename Writer>
void Serialize(Writer& writer) const { void Serialize(Writer& writer) const {
writer.StartObject(); writer.StartObject();
writer.String("school"); writer.String("school");
writer.String(school_.c_str(), (SizeType)school_.length()); writer.String(school_.c_str(), (SizeType)school_.length());
writer.String("GPA"); writer.String("GPA");
writer.Double(GPA_); writer.Double(GPA_);
writer.EndObject(); writer.EndObject();
} }
private: private:
std::string school_; std::string school_;
double GPA_; double GPA_;
}; };
class Dependent : public Person { class Dependent : public Person {
public: public:
Dependent(const std::string& name, unsigned age, Education* education = 0) : Person(name, age), education_(education) {} Dependent(const std::string& name, unsigned age, Education* education = 0) : Person(name, age), education_(education) {}
Dependent(const Dependent& rhs) : Person(rhs), education_(0) { education_ = (rhs.education_ == 0) ? 0 : new Education(*rhs.education_); } Dependent(const Dependent& rhs) : Person(rhs), education_(0) { education_ = (rhs.education_ == 0) ? 0 : new Education(*rhs.education_); }
virtual ~Dependent(); virtual ~Dependent();
Dependent& operator=(const Dependent& rhs) { Dependent& operator=(const Dependent& rhs) {
if (this == &rhs) if (this == &rhs)
return *this; return *this;
delete education_; delete education_;
education_ = (rhs.education_ == 0) ? 0 : new Education(*rhs.education_); education_ = (rhs.education_ == 0) ? 0 : new Education(*rhs.education_);
return *this; return *this;
} }
template <typename Writer> template <typename Writer>
void Serialize(Writer& writer) const { void Serialize(Writer& writer) const {
writer.StartObject(); writer.StartObject();
Person::Serialize(writer); Person::Serialize(writer);
writer.String("education"); writer.String("education");
if (education_) if (education_)
education_->Serialize(writer); education_->Serialize(writer);
else else
writer.Null(); writer.Null();
writer.EndObject(); writer.EndObject();
} }
private: private:
Education *education_; Education *education_;
}; };
Dependent::~Dependent() { Dependent::~Dependent() {
delete education_; delete education_;
} }
class Employee : public Person { class Employee : public Person {
public: public:
Employee(const std::string& name, unsigned age, bool married) : Person(name, age), dependents_(), married_(married) {} Employee(const std::string& name, unsigned age, bool married) : Person(name, age), dependents_(), married_(married) {}
virtual ~Employee(); virtual ~Employee();
void AddDependent(const Dependent& dependent) { void AddDependent(const Dependent& dependent) {
dependents_.push_back(dependent); dependents_.push_back(dependent);
} }
template <typename Writer> template <typename Writer>
void Serialize(Writer& writer) const { void Serialize(Writer& writer) const {
writer.StartObject(); writer.StartObject();
Person::Serialize(writer); Person::Serialize(writer);
writer.String("married"); writer.String("married");
writer.Bool(married_); writer.Bool(married_);
writer.String(("dependents")); writer.String(("dependents"));
writer.StartArray(); writer.StartArray();
for (std::vector<Dependent>::const_iterator dependentItr = dependents_.begin(); dependentItr != dependents_.end(); ++dependentItr) for (std::vector<Dependent>::const_iterator dependentItr = dependents_.begin(); dependentItr != dependents_.end(); ++dependentItr)
dependentItr->Serialize(writer); dependentItr->Serialize(writer);
writer.EndArray(); writer.EndArray();
writer.EndObject(); writer.EndObject();
} }
private: private:
std::vector<Dependent> dependents_; std::vector<Dependent> dependents_;
bool married_; bool married_;
}; };
Employee::~Employee() { Employee::~Employee() {
} }
int main(int, char*[]) { int main(int, char*[]) {
std::vector<Employee> employees; std::vector<Employee> employees;
employees.push_back(Employee("Milo YIP", 34, true)); employees.push_back(Employee("Milo YIP", 34, true));
employees.back().AddDependent(Dependent("Lua YIP", 3, new Education("Happy Kindergarten", 3.5))); employees.back().AddDependent(Dependent("Lua YIP", 3, new Education("Happy Kindergarten", 3.5)));
employees.back().AddDependent(Dependent("Mio YIP", 1)); employees.back().AddDependent(Dependent("Mio YIP", 1));
employees.push_back(Employee("Percy TSE", 30, false)); employees.push_back(Employee("Percy TSE", 30, false));
FileStream s(stdout); FileStream s(stdout);
PrettyWriter<FileStream> writer(s); // Can also use Writer for condensed formatting PrettyWriter<FileStream> writer(s); // Can also use Writer for condensed formatting
writer.StartArray(); writer.StartArray();
for (std::vector<Employee>::const_iterator employeeItr = employees.begin(); employeeItr != employees.end(); ++employeeItr) for (std::vector<Employee>::const_iterator employeeItr = employees.begin(); employeeItr != employees.end(); ++employeeItr)
employeeItr->Serialize(writer); employeeItr->Serialize(writer);
writer.EndArray(); writer.EndArray();
return 0; return 0;
} }

View File

@ -9,21 +9,21 @@
using namespace rapidjson; using namespace rapidjson;
int main() { int main() {
// 1. Parse a JSON string into DOM. // 1. Parse a JSON string into DOM.
const char* json = "{\"project\":\"rapidjson\",\"stars\":10}"; const char* json = "{\"project\":\"rapidjson\",\"stars\":10}";
Document d; Document d;
d.Parse(json); d.Parse(json);
// 2. Modify it by DOM. // 2. Modify it by DOM.
Value& s = d["stars"]; Value& s = d["stars"];
s.SetInt(s.GetInt() + 1); s.SetInt(s.GetInt() + 1);
// 3. Stringify the DOM // 3. Stringify the DOM
StringBuffer buffer; StringBuffer buffer;
Writer<StringBuffer> writer(buffer); Writer<StringBuffer> writer(buffer);
d.Accept(writer); d.Accept(writer);
// Output {"project":"rapidjson","stars":11} // Output {"project":"rapidjson","stars":11}
std::cout << buffer.GetString() << std::endl; std::cout << buffer.GetString() << std::endl;
return 0; return 0;
} }

View File

@ -5,21 +5,21 @@ using namespace rapidjson;
using namespace std; using namespace std;
struct MyHandler { struct MyHandler {
bool Null() { cout << "Null()" << endl; return true; } bool Null() { cout << "Null()" << endl; return true; }
bool Bool(bool b) { cout << "Bool(" << boolalpha << b << ")" << 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 Int(int i) { cout << "Int(" << i << ")" << endl; return true; }
bool Uint(unsigned u) { cout << "Uint(" << u << ")" << 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 Int64(int64_t i) { cout << "Int64(" << i << ")" << endl; return true; }
bool Uint64(uint64_t u) { cout << "Uint64(" << u << ")" << 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 Double(double d) { cout << "Double(" << d << ")" << endl; return true; }
bool String(const char* str, SizeType length, bool copy) { bool String(const char* str, SizeType length, bool copy) {
cout << "String(" << str << ", " << length << ", " << boolalpha << copy << ")" << endl; cout << "String(" << str << ", " << length << ", " << boolalpha << copy << ")" << endl;
return true; return true;
} }
bool StartObject() { cout << "StartObject()" << endl; return true; } bool StartObject() { cout << "StartObject()" << endl; return true; }
bool EndObject(SizeType memberCount) { cout << "EndObject(" << memberCount << ")" << endl; return true; } bool EndObject(SizeType memberCount) { cout << "EndObject(" << memberCount << ")" << endl; return true; }
bool StartArray() { cout << "StartArray()" << endl; return true; } bool StartArray() { cout << "StartArray()" << endl; return true; }
bool EndArray(SizeType elementCount) { cout << "EndArray(" << elementCount << ")" << endl; return true; } bool EndArray(SizeType elementCount) { cout << "EndArray(" << elementCount << ")" << endl; return true; }
}; };
int main() { int main() {
@ -28,7 +28,7 @@ int main() {
MyHandler handler; MyHandler handler;
Reader reader; Reader reader;
StringStream ss(json); StringStream ss(json);
reader.Parse(ss, handler); reader.Parse(ss, handler);
return 0; return 0;
} }

View File

@ -31,5 +31,5 @@ int main() {
cout << s.GetString() << endl; cout << s.GetString() << endl;
return 0; return 0;
} }

View File

@ -1,156 +1,156 @@
// Hello World example // Hello World example
// This example shows basic usage of DOM-style API. // This example shows basic usage of DOM-style API.
#include "rapidjson/document.h" // rapidjson's DOM-style API #include "rapidjson/document.h" // rapidjson's DOM-style API
#include "rapidjson/prettywriter.h" // for stringify JSON #include "rapidjson/prettywriter.h" // for stringify JSON
#include "rapidjson/filestream.h" // wrapper of C stream for prettywriter as output #include "rapidjson/filestream.h" // wrapper of C stream for prettywriter as output
#include <cstdio> #include <cstdio>
using namespace rapidjson; using namespace rapidjson;
int main(int, char*[]) { int main(int, char*[]) {
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// 1. Parse a JSON text string to a document. // 1. Parse a JSON text string to a document.
const char json[] = " { \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3, 4] } "; const char json[] = " { \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3, 4] } ";
printf("Original JSON:\n %s\n", json); printf("Original JSON:\n %s\n", json);
Document document; // Default template parameter uses UTF8 and MemoryPoolAllocator. Document document; // Default template parameter uses UTF8 and MemoryPoolAllocator.
#if 0 #if 0
// "normal" parsing, decode strings to new buffers. Can use other input stream via ParseStream(). // "normal" parsing, decode strings to new buffers. Can use other input stream via ParseStream().
if (document.Parse(json).HasParseError()) if (document.Parse(json).HasParseError())
return 1; return 1;
#else #else
// In-situ parsing, decode strings directly in the source string. Source must be string. // In-situ parsing, decode strings directly in the source string. Source must be string.
{ {
char buffer[sizeof(json)]; char buffer[sizeof(json)];
memcpy(buffer, json, sizeof(json)); memcpy(buffer, json, sizeof(json));
if (document.ParseInsitu(buffer).HasParseError()) if (document.ParseInsitu(buffer).HasParseError())
return 1; return 1;
} }
#endif #endif
printf("\nParsing to document succeeded.\n"); printf("\nParsing to document succeeded.\n");
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// 2. Access values in document. // 2. Access values in document.
printf("\nAccess values in document:\n"); printf("\nAccess values in document:\n");
assert(document.IsObject()); // Document is a JSON value represents the root of DOM. Root can be either an object or array. assert(document.IsObject()); // Document is a JSON value represents the root of DOM. Root can be either an object or array.
assert(document.HasMember("hello")); assert(document.HasMember("hello"));
assert(document["hello"].IsString()); assert(document["hello"].IsString());
printf("hello = %s\n", document["hello"].GetString()); printf("hello = %s\n", document["hello"].GetString());
// Since version 0.2, you can use single lookup to check the existing of member and its value: // Since version 0.2, you can use single lookup to check the existing of member and its value:
Value::MemberIterator hello = document.FindMember("hello"); Value::MemberIterator hello = document.FindMember("hello");
assert(hello != document.MemberEnd()); assert(hello != document.MemberEnd());
assert(hello->value.IsString()); assert(hello->value.IsString());
assert(strcmp("world", hello->value.GetString()) == 0); assert(strcmp("world", hello->value.GetString()) == 0);
(void)hello; (void)hello;
assert(document["t"].IsBool()); // JSON true/false are bool. Can also uses more specific function IsTrue(). assert(document["t"].IsBool()); // JSON true/false are bool. Can also uses more specific function IsTrue().
printf("t = %s\n", document["t"].GetBool() ? "true" : "false"); printf("t = %s\n", document["t"].GetBool() ? "true" : "false");
assert(document["f"].IsBool()); assert(document["f"].IsBool());
printf("f = %s\n", document["f"].GetBool() ? "true" : "false"); printf("f = %s\n", document["f"].GetBool() ? "true" : "false");
printf("n = %s\n", document["n"].IsNull() ? "null" : "?"); printf("n = %s\n", document["n"].IsNull() ? "null" : "?");
assert(document["i"].IsNumber()); // Number is a JSON type, but C++ needs more specific type. assert(document["i"].IsNumber()); // Number is a JSON type, but C++ needs more specific type.
assert(document["i"].IsInt()); // In this case, IsUint()/IsInt64()/IsUInt64() also return true. assert(document["i"].IsInt()); // In this case, IsUint()/IsInt64()/IsUInt64() also return true.
printf("i = %d\n", document["i"].GetInt()); // Alternative (int)document["i"] printf("i = %d\n", document["i"].GetInt()); // Alternative (int)document["i"]
assert(document["pi"].IsNumber()); assert(document["pi"].IsNumber());
assert(document["pi"].IsDouble()); assert(document["pi"].IsDouble());
printf("pi = %g\n", document["pi"].GetDouble()); printf("pi = %g\n", document["pi"].GetDouble());
{ {
const Value& a = document["a"]; // Using a reference for consecutive access is handy and faster. const Value& a = document["a"]; // Using a reference for consecutive access is handy and faster.
assert(a.IsArray()); assert(a.IsArray());
for (SizeType i = 0; i < a.Size(); i++) // rapidjson uses SizeType instead of size_t. for (SizeType i = 0; i < a.Size(); i++) // rapidjson uses SizeType instead of size_t.
printf("a[%d] = %d\n", i, a[i].GetInt()); printf("a[%d] = %d\n", i, a[i].GetInt());
// Note: // Note:
//int x = a[0].GetInt(); // Error: operator[ is ambiguous, as 0 also mean a null pointer of const char* type. //int x = a[0].GetInt(); // Error: operator[ is ambiguous, as 0 also mean a null pointer of const char* type.
int y = a[SizeType(0)].GetInt(); // Cast to SizeType will work. int y = a[SizeType(0)].GetInt(); // Cast to SizeType will work.
int z = a[0u].GetInt(); // This works too. int z = a[0u].GetInt(); // This works too.
(void)y; (void)y;
(void)z; (void)z;
// Iterating array with iterators // Iterating array with iterators
printf("a = "); printf("a = ");
for (Value::ConstValueIterator itr = a.Begin(); itr != a.End(); ++itr) for (Value::ConstValueIterator itr = a.Begin(); itr != a.End(); ++itr)
printf("%d ", itr->GetInt()); printf("%d ", itr->GetInt());
printf("\n"); printf("\n");
} }
// Iterating object members // Iterating object members
static const char* kTypeNames[] = { "Null", "False", "True", "Object", "Array", "String", "Number" }; static const char* kTypeNames[] = { "Null", "False", "True", "Object", "Array", "String", "Number" };
for (Value::ConstMemberIterator itr = document.MemberBegin(); itr != document.MemberEnd(); ++itr) for (Value::ConstMemberIterator itr = document.MemberBegin(); itr != document.MemberEnd(); ++itr)
printf("Type of member %s is %s\n", itr->name.GetString(), kTypeNames[itr->value.GetType()]); printf("Type of member %s is %s\n", itr->name.GetString(), kTypeNames[itr->value.GetType()]);
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// 3. Modify values in document. // 3. Modify values in document.
// Change i to a bigger number // Change i to a bigger number
{ {
uint64_t f20 = 1; // compute factorial of 20 uint64_t f20 = 1; // compute factorial of 20
for (uint64_t j = 1; j <= 20; j++) for (uint64_t j = 1; j <= 20; j++)
f20 *= j; f20 *= j;
document["i"] = f20; // Alternate form: document["i"].SetUint64(f20) document["i"] = f20; // Alternate form: document["i"].SetUint64(f20)
assert(!document["i"].IsInt()); // No longer can be cast as int or uint. assert(!document["i"].IsInt()); // No longer can be cast as int or uint.
} }
// Adding values to array. // Adding values to array.
{ {
Value& a = document["a"]; // This time we uses non-const reference. Value& a = document["a"]; // This time we uses non-const reference.
Document::AllocatorType& allocator = document.GetAllocator(); Document::AllocatorType& allocator = document.GetAllocator();
for (int i = 5; i <= 10; i++) for (int i = 5; i <= 10; i++)
a.PushBack(i, allocator); // May look a bit strange, allocator is needed for potentially realloc. We normally uses the document's. a.PushBack(i, allocator); // May look a bit strange, allocator is needed for potentially realloc. We normally uses the document's.
// Fluent API // Fluent API
a.PushBack("Lua", allocator).PushBack("Mio", allocator); a.PushBack("Lua", allocator).PushBack("Mio", allocator);
} }
// Making string values. // Making string values.
// This version of SetString() just store the pointer to the string. // This version of SetString() just store the pointer to the string.
// So it is for literal and string that exists within value's life-cycle. // So it is for literal and string that exists within value's life-cycle.
{ {
document["hello"] = "rapidjson"; // This will invoke strlen() document["hello"] = "rapidjson"; // This will invoke strlen()
// Faster version: // Faster version:
// document["hello"].SetString("rapidjson", 9); // document["hello"].SetString("rapidjson", 9);
} }
// This version of SetString() needs an allocator, which means it will allocate a new buffer and copy the the string into the buffer. // This version of SetString() needs an allocator, which means it will allocate a new buffer and copy the the string into the buffer.
Value author; Value author;
{ {
char buffer[10]; char buffer[10];
int len = sprintf(buffer, "%s %s", "Milo", "Yip"); // synthetic example of dynamically created string. int len = sprintf(buffer, "%s %s", "Milo", "Yip"); // synthetic example of dynamically created string.
author.SetString(buffer, static_cast<size_t>(len), document.GetAllocator()); author.SetString(buffer, static_cast<size_t>(len), document.GetAllocator());
// Shorter but slower version: // Shorter but slower version:
// document["hello"].SetString(buffer, document.GetAllocator()); // document["hello"].SetString(buffer, document.GetAllocator());
// Constructor version: // Constructor version:
// Value author(buffer, len, document.GetAllocator()); // Value author(buffer, len, document.GetAllocator());
// Value author(buffer, document.GetAllocator()); // Value author(buffer, document.GetAllocator());
memset(buffer, 0, sizeof(buffer)); // For demonstration purpose. memset(buffer, 0, sizeof(buffer)); // For demonstration purpose.
} }
// Variable 'buffer' is unusable now but 'author' has already made a copy. // Variable 'buffer' is unusable now but 'author' has already made a copy.
document.AddMember("author", author, document.GetAllocator()); document.AddMember("author", author, document.GetAllocator());
assert(author.IsNull()); // Move semantic for assignment. After this variable is assigned as a member, the variable becomes null. assert(author.IsNull()); // Move semantic for assignment. After this variable is assigned as a member, the variable becomes null.
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// 4. Stringify JSON // 4. Stringify JSON
printf("\nModified JSON with reformatting:\n"); printf("\nModified JSON with reformatting:\n");
FileStream f(stdout); FileStream f(stdout);
PrettyWriter<FileStream> writer(f); PrettyWriter<FileStream> writer(f);
document.Accept(writer); // Accept() traverses the DOM and generates Handler events. document.Accept(writer); // Accept() traverses the DOM and generates Handler events.
return 0; return 0;
} }

View File

@ -1,3 +1,23 @@
// Copyright (C) 2011 Milo Yip
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef RAPIDJSON_ALLOCATORS_H_ #ifndef RAPIDJSON_ALLOCATORS_H_
#define RAPIDJSON_ALLOCATORS_H_ #define RAPIDJSON_ALLOCATORS_H_
@ -9,31 +29,31 @@ namespace rapidjson {
// Allocator // Allocator
/*! \class rapidjson::Allocator /*! \class rapidjson::Allocator
\brief Concept for allocating, resizing and freeing memory block. \brief Concept for allocating, resizing and freeing memory block.
Note that Malloc() and Realloc() are non-static but Free() is static. Note that Malloc() and Realloc() are non-static but Free() is static.
So if an allocator need to support Free(), it needs to put its pointer in So if an allocator need to support Free(), it needs to put its pointer in
the header of memory block. the header of memory block.
\code \code
concept Allocator { concept Allocator {
static const bool kNeedFree; //!< Whether this allocator needs to call Free(). static const bool kNeedFree; //!< Whether this allocator needs to call Free().
// Allocate a memory block. // Allocate a memory block.
// \param size of the memory block in bytes. // \param size of the memory block in bytes.
// \returns pointer to the memory block. // \returns pointer to the memory block.
void* Malloc(size_t size); void* Malloc(size_t size);
// Resize a memory block. // Resize a memory block.
// \param originalPtr The pointer to current memory block. Null pointer is permitted. // \param originalPtr The pointer to current memory block. Null pointer is permitted.
// \param originalSize The current size in bytes. (Design issue: since some allocator may not book-keep this, explicitly pass to it can save memory.) // \param originalSize The current size in bytes. (Design issue: since some allocator may not book-keep this, explicitly pass to it can save memory.)
// \param newSize the new size in bytes. // \param newSize the new size in bytes.
void* Realloc(void* originalPtr, size_t originalSize, size_t newSize); void* Realloc(void* originalPtr, size_t originalSize, size_t newSize);
// Free a memory block. // Free a memory block.
// \param pointer to the memory block. Null pointer is permitted. // \param pointer to the memory block. Null pointer is permitted.
static void Free(void *ptr); static void Free(void *ptr);
}; };
\endcode \endcode
*/ */
@ -43,14 +63,14 @@ concept Allocator {
//! C-runtime library allocator. //! C-runtime library allocator.
/*! This class is just wrapper for standard C library memory routines. /*! This class is just wrapper for standard C library memory routines.
\note implements Allocator concept \note implements Allocator concept
*/ */
class CrtAllocator { class CrtAllocator {
public: public:
static const bool kNeedFree = true; static const bool kNeedFree = true;
void* Malloc(size_t size) { return malloc(size); } void* Malloc(size_t size) { return malloc(size); }
void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) { (void)originalSize; return realloc(originalPtr, newSize); } void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) { (void)originalSize; return realloc(originalPtr, newSize); }
static void Free(void *ptr) { free(ptr); } static void Free(void *ptr) { free(ptr); }
}; };
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@ -70,155 +90,155 @@ public:
The user-buffer is not deallocated by this allocator. The user-buffer is not deallocated by this allocator.
\tparam BaseAllocator the allocator type for allocating memory chunks. Default is CrtAllocator. \tparam BaseAllocator the allocator type for allocating memory chunks. Default is CrtAllocator.
\note implements Allocator concept \note implements Allocator concept
*/ */
template <typename BaseAllocator = CrtAllocator> template <typename BaseAllocator = CrtAllocator>
class MemoryPoolAllocator { class MemoryPoolAllocator {
public: public:
static const bool kNeedFree = false; //!< Tell users that no need to call Free() with this allocator. (concept Allocator) static const bool kNeedFree = false; //!< Tell users that no need to call Free() with this allocator. (concept Allocator)
//! Constructor with chunkSize. //! Constructor with chunkSize.
/*! \param chunkSize The size of memory chunk. The default is kDefaultChunkSize. /*! \param chunkSize The size of memory chunk. The default is kDefaultChunkSize.
\param baseAllocator The allocator for allocating memory chunks. \param baseAllocator The allocator for allocating memory chunks.
*/ */
MemoryPoolAllocator(size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) : MemoryPoolAllocator(size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) :
chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(0), baseAllocator_(baseAllocator), ownBaseAllocator_(0) chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(0), baseAllocator_(baseAllocator), ownBaseAllocator_(0)
{ {
if (!baseAllocator_) if (!baseAllocator_)
ownBaseAllocator_ = baseAllocator_ = new BaseAllocator(); ownBaseAllocator_ = baseAllocator_ = new BaseAllocator();
AddChunk(chunk_capacity_); AddChunk(chunk_capacity_);
} }
//! Constructor with user-supplied buffer. //! Constructor with user-supplied buffer.
/*! The user buffer will be used firstly. When it is full, memory pool allocates new chunk with chunk size. /*! The user buffer will be used firstly. When it is full, memory pool allocates new chunk with chunk size.
The user buffer will not be deallocated when this allocator is destructed. The user buffer will not be deallocated when this allocator is destructed.
\param buffer User supplied buffer. \param buffer User supplied buffer.
\param size Size of the buffer in bytes. It must at least larger than sizeof(ChunkHeader). \param size Size of the buffer in bytes. It must at least larger than sizeof(ChunkHeader).
\param chunkSize The size of memory chunk. The default is kDefaultChunkSize. \param chunkSize The size of memory chunk. The default is kDefaultChunkSize.
\param baseAllocator The allocator for allocating memory chunks. \param baseAllocator The allocator for allocating memory chunks.
*/ */
MemoryPoolAllocator(void *buffer, size_t size, size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) : MemoryPoolAllocator(void *buffer, size_t size, size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) :
chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(buffer), baseAllocator_(baseAllocator), ownBaseAllocator_(0) chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(buffer), baseAllocator_(baseAllocator), ownBaseAllocator_(0)
{ {
RAPIDJSON_ASSERT(buffer != 0); RAPIDJSON_ASSERT(buffer != 0);
RAPIDJSON_ASSERT(size > sizeof(ChunkHeader)); RAPIDJSON_ASSERT(size > sizeof(ChunkHeader));
chunkHead_ = reinterpret_cast<ChunkHeader*>(buffer); chunkHead_ = reinterpret_cast<ChunkHeader*>(buffer);
chunkHead_->capacity = size - sizeof(ChunkHeader); chunkHead_->capacity = size - sizeof(ChunkHeader);
chunkHead_->size = 0; chunkHead_->size = 0;
chunkHead_->next = 0; chunkHead_->next = 0;
} }
//! Destructor. //! Destructor.
/*! This deallocates all memory chunks, excluding the user-supplied buffer. /*! This deallocates all memory chunks, excluding the user-supplied buffer.
*/ */
~MemoryPoolAllocator() { ~MemoryPoolAllocator() {
Clear(); Clear();
delete ownBaseAllocator_; delete ownBaseAllocator_;
} }
//! Deallocates all memory chunks, excluding the user-supplied buffer. //! Deallocates all memory chunks, excluding the user-supplied buffer.
void Clear() { void Clear() {
while(chunkHead_ != 0 && chunkHead_ != userBuffer_) { while(chunkHead_ != 0 && chunkHead_ != userBuffer_) {
ChunkHeader* next = chunkHead_->next; ChunkHeader* next = chunkHead_->next;
baseAllocator_->Free(chunkHead_); baseAllocator_->Free(chunkHead_);
chunkHead_ = next; chunkHead_ = next;
} }
} }
//! Computes the total capacity of allocated memory chunks. //! Computes the total capacity of allocated memory chunks.
/*! \return total capacity in bytes. /*! \return total capacity in bytes.
*/ */
size_t Capacity() const { size_t Capacity() const {
size_t capacity = 0; size_t capacity = 0;
for (ChunkHeader* c = chunkHead_; c != 0; c = c->next) for (ChunkHeader* c = chunkHead_; c != 0; c = c->next)
capacity += c->capacity; capacity += c->capacity;
return capacity; return capacity;
} }
//! Computes the memory blocks allocated. //! Computes the memory blocks allocated.
/*! \return total used bytes. /*! \return total used bytes.
*/ */
size_t Size() const { size_t Size() const {
size_t size = 0; size_t size = 0;
for (ChunkHeader* c = chunkHead_; c != 0; c = c->next) for (ChunkHeader* c = chunkHead_; c != 0; c = c->next)
size += c->size; size += c->size;
return size; return size;
} }
//! Allocates a memory block. (concept Allocator) //! Allocates a memory block. (concept Allocator)
void* Malloc(size_t size) { void* Malloc(size_t size) {
size = RAPIDJSON_ALIGN(size); size = RAPIDJSON_ALIGN(size);
if (chunkHead_->size + size > chunkHead_->capacity) if (chunkHead_->size + size > chunkHead_->capacity)
AddChunk(chunk_capacity_ > size ? chunk_capacity_ : size); AddChunk(chunk_capacity_ > size ? chunk_capacity_ : size);
void *buffer = reinterpret_cast<char *>(chunkHead_ + 1) + chunkHead_->size; void *buffer = reinterpret_cast<char *>(chunkHead_ + 1) + chunkHead_->size;
chunkHead_->size += size; chunkHead_->size += size;
return buffer; return buffer;
} }
//! Resizes a memory block (concept Allocator) //! Resizes a memory block (concept Allocator)
void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) { void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) {
if (originalPtr == 0) if (originalPtr == 0)
return Malloc(newSize); return Malloc(newSize);
// Do not shrink if new size is smaller than original // Do not shrink if new size is smaller than original
if (originalSize >= newSize) if (originalSize >= newSize)
return originalPtr; return originalPtr;
// Simply expand it if it is the last allocation and there is sufficient space // Simply expand it if it is the last allocation and there is sufficient space
if (originalPtr == (char *)(chunkHead_ + 1) + chunkHead_->size - originalSize) { if (originalPtr == (char *)(chunkHead_ + 1) + chunkHead_->size - originalSize) {
size_t increment = static_cast<size_t>(newSize - originalSize); size_t increment = static_cast<size_t>(newSize - originalSize);
increment = RAPIDJSON_ALIGN(increment); increment = RAPIDJSON_ALIGN(increment);
if (chunkHead_->size + increment <= chunkHead_->capacity) { if (chunkHead_->size + increment <= chunkHead_->capacity) {
chunkHead_->size += increment; chunkHead_->size += increment;
return originalPtr; return originalPtr;
} }
} }
// Realloc process: allocate and copy memory, do not free original buffer. // Realloc process: allocate and copy memory, do not free original buffer.
void* newBuffer = Malloc(newSize); void* newBuffer = Malloc(newSize);
RAPIDJSON_ASSERT(newBuffer != 0); // Do not handle out-of-memory explicitly. RAPIDJSON_ASSERT(newBuffer != 0); // Do not handle out-of-memory explicitly.
return memcpy(newBuffer, originalPtr, originalSize); return memcpy(newBuffer, originalPtr, originalSize);
} }
//! Frees a memory block (concept Allocator) //! Frees a memory block (concept Allocator)
static void Free(void *ptr) { (void)ptr; } // Do nothing static void Free(void *ptr) { (void)ptr; } // Do nothing
private: private:
//! Copy constructor is not permitted. //! Copy constructor is not permitted.
MemoryPoolAllocator(const MemoryPoolAllocator& rhs) /* = delete */; MemoryPoolAllocator(const MemoryPoolAllocator& rhs) /* = delete */;
//! Copy assignment operator is not permitted. //! Copy assignment operator is not permitted.
MemoryPoolAllocator& operator=(const MemoryPoolAllocator& rhs) /* = delete */; MemoryPoolAllocator& operator=(const MemoryPoolAllocator& rhs) /* = delete */;
//! Creates a new chunk. //! Creates a new chunk.
/*! \param capacity Capacity of the chunk in bytes. /*! \param capacity Capacity of the chunk in bytes.
*/ */
void AddChunk(size_t capacity) { void AddChunk(size_t capacity) {
ChunkHeader* chunk = reinterpret_cast<ChunkHeader*>(baseAllocator_->Malloc(sizeof(ChunkHeader) + capacity)); ChunkHeader* chunk = reinterpret_cast<ChunkHeader*>(baseAllocator_->Malloc(sizeof(ChunkHeader) + capacity));
chunk->capacity = capacity; chunk->capacity = capacity;
chunk->size = 0; chunk->size = 0;
chunk->next = chunkHead_; chunk->next = chunkHead_;
chunkHead_ = chunk; chunkHead_ = chunk;
} }
static const int kDefaultChunkCapacity = 64 * 1024; //!< Default chunk capacity. static const int kDefaultChunkCapacity = 64 * 1024; //!< Default chunk capacity.
//! Chunk header for perpending to each chunk. //! Chunk header for perpending to each chunk.
/*! Chunks are stored as a singly linked list. /*! Chunks are stored as a singly linked list.
*/ */
struct ChunkHeader { struct ChunkHeader {
size_t capacity; //!< Capacity of the chunk in bytes (excluding the header itself). size_t capacity; //!< Capacity of the chunk in bytes (excluding the header itself).
size_t size; //!< Current size of allocated memory in bytes. size_t size; //!< Current size of allocated memory in bytes.
ChunkHeader *next; //!< Next chunk in the linked list. ChunkHeader *next; //!< Next chunk in the linked list.
}; };
ChunkHeader *chunkHead_; //!< Head of the chunk linked-list. Only the head chunk serves allocation. ChunkHeader *chunkHead_; //!< Head of the chunk linked-list. Only the head chunk serves allocation.
size_t chunk_capacity_; //!< The minimum capacity of chunk when they are allocated. size_t chunk_capacity_; //!< The minimum capacity of chunk when they are allocated.
void *userBuffer_; //!< User supplied buffer. void *userBuffer_; //!< User supplied buffer.
BaseAllocator* baseAllocator_; //!< base allocator for allocating memory chunks. BaseAllocator* baseAllocator_; //!< base allocator for allocating memory chunks.
BaseAllocator* ownBaseAllocator_; //!< base allocator created by this object. BaseAllocator* ownBaseAllocator_; //!< base allocator created by this object.
}; };
} // namespace rapidjson } // namespace rapidjson

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,23 @@
// Copyright (C) 2011 Milo Yip
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef RAPIDJSON_ENCODEDSTREAM_H_ #ifndef RAPIDJSON_ENCODEDSTREAM_H_
#define RAPIDJSON_ENCODEDSTREAM_H_ #define RAPIDJSON_ENCODEDSTREAM_H_
@ -12,251 +32,251 @@ namespace rapidjson {
//! Input byte stream wrapper with a statically bound encoding. //! Input byte stream wrapper with a statically bound encoding.
/*! /*!
\tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE. \tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE.
\tparam InputByteStream Type of input byte stream. For example, FileReadStream. \tparam InputByteStream Type of input byte stream. For example, FileReadStream.
*/ */
template <typename Encoding, typename InputByteStream> template <typename Encoding, typename InputByteStream>
class EncodedInputStream { class EncodedInputStream {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
public: public:
typedef typename Encoding::Ch Ch; typedef typename Encoding::Ch Ch;
EncodedInputStream(InputByteStream& is) : is_(is) { EncodedInputStream(InputByteStream& is) : is_(is) {
current_ = Encoding::TakeBOM(is_); current_ = Encoding::TakeBOM(is_);
} }
Ch Peek() const { return current_; } Ch Peek() const { return current_; }
Ch Take() { Ch c = current_; current_ = Encoding::Take(is_); return c; } Ch Take() { Ch c = current_; current_ = Encoding::Take(is_); return c; }
size_t Tell() const { return is_.Tell(); } size_t Tell() const { return is_.Tell(); }
// Not implemented // Not implemented
void Put(Ch) { RAPIDJSON_ASSERT(false); } void Put(Ch) { RAPIDJSON_ASSERT(false); }
void Flush() { RAPIDJSON_ASSERT(false); } void Flush() { RAPIDJSON_ASSERT(false); }
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
private: private:
EncodedInputStream(const EncodedInputStream&); EncodedInputStream(const EncodedInputStream&);
EncodedInputStream& operator=(const EncodedInputStream&); EncodedInputStream& operator=(const EncodedInputStream&);
InputByteStream& is_; InputByteStream& is_;
Ch current_; Ch current_;
}; };
//! Output byte stream wrapper with statically bound encoding. //! Output byte stream wrapper with statically bound encoding.
/*! /*!
\tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE. \tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE.
\tparam InputByteStream Type of input byte stream. For example, FileWriteStream. \tparam InputByteStream Type of input byte stream. For example, FileWriteStream.
*/ */
template <typename Encoding, typename OutputByteStream> template <typename Encoding, typename OutputByteStream>
class EncodedOutputStream { class EncodedOutputStream {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
public: public:
typedef typename Encoding::Ch Ch; typedef typename Encoding::Ch Ch;
EncodedOutputStream(OutputByteStream& os, bool putBOM = true) : os_(os) { EncodedOutputStream(OutputByteStream& os, bool putBOM = true) : os_(os) {
if (putBOM) if (putBOM)
Encoding::PutBOM(os_); Encoding::PutBOM(os_);
} }
void Put(Ch c) { Encoding::Put(os_, c); } void Put(Ch c) { Encoding::Put(os_, c); }
void Flush() { os_.Flush(); } void Flush() { os_.Flush(); }
// Not implemented // Not implemented
Ch Peek() const { RAPIDJSON_ASSERT(false); } Ch Peek() const { RAPIDJSON_ASSERT(false); }
Ch Take() { RAPIDJSON_ASSERT(false); } Ch Take() { RAPIDJSON_ASSERT(false); }
size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; }
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
private: private:
EncodedOutputStream(const EncodedOutputStream&); EncodedOutputStream(const EncodedOutputStream&);
EncodedOutputStream& operator=(const EncodedOutputStream&); EncodedOutputStream& operator=(const EncodedOutputStream&);
OutputByteStream& os_; OutputByteStream& os_;
}; };
#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8<Ch>::x, UTF16LE<Ch>::x, UTF16BE<Ch>::x, UTF32LE<Ch>::x, UTF32BE<Ch>::x #define RAPIDJSON_ENCODINGS_FUNC(x) UTF8<Ch>::x, UTF16LE<Ch>::x, UTF16BE<Ch>::x, UTF32LE<Ch>::x, UTF32BE<Ch>::x
//! Input stream wrapper with dynamically bound encoding and automatic encoding detection. //! Input stream wrapper with dynamically bound encoding and automatic encoding detection.
/*! /*!
\tparam CharType Type of character for reading. \tparam CharType Type of character for reading.
\tparam InputByteStream type of input byte stream to be wrapped. \tparam InputByteStream type of input byte stream to be wrapped.
*/ */
template <typename CharType, typename InputByteStream> template <typename CharType, typename InputByteStream>
class AutoUTFInputStream { class AutoUTFInputStream {
RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1);
public: public:
typedef CharType Ch; typedef CharType Ch;
//! Constructor. //! Constructor.
/*! /*!
\param is input stream to be wrapped. \param is input stream to be wrapped.
\param type UTF encoding type if it is not detected from the stream. \param type UTF encoding type if it is not detected from the stream.
*/ */
AutoUTFInputStream(InputByteStream& is, UTFType type = kUTF8) : is_(&is), type_(type), hasBOM_(false) { AutoUTFInputStream(InputByteStream& is, UTFType type = kUTF8) : is_(&is), type_(type), hasBOM_(false) {
DetectType(); DetectType();
static const TakeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Take) }; static const TakeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Take) };
takeFunc_ = f[type_]; takeFunc_ = f[type_];
current_ = takeFunc_(*is_); current_ = takeFunc_(*is_);
} }
UTFType GetType() const { return type_; } UTFType GetType() const { return type_; }
bool HasBOM() const { return hasBOM_; } bool HasBOM() const { return hasBOM_; }
Ch Peek() const { return current_; } Ch Peek() const { return current_; }
Ch Take() { Ch c = current_; current_ = takeFunc_(*is_); return c; } Ch Take() { Ch c = current_; current_ = takeFunc_(*is_); return c; }
size_t Tell() const { return is_->Tell(); } size_t Tell() const { return is_->Tell(); }
// Not implemented // Not implemented
void Put(Ch) { RAPIDJSON_ASSERT(false); } void Put(Ch) { RAPIDJSON_ASSERT(false); }
void Flush() { RAPIDJSON_ASSERT(false); } void Flush() { RAPIDJSON_ASSERT(false); }
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
private: private:
AutoUTFInputStream(const AutoUTFInputStream&); AutoUTFInputStream(const AutoUTFInputStream&);
AutoUTFInputStream& operator=(const AutoUTFInputStream&); AutoUTFInputStream& operator=(const AutoUTFInputStream&);
// Detect encoding type with BOM or RFC 4627 // Detect encoding type with BOM or RFC 4627
void DetectType() { void DetectType() {
// BOM (Byte Order Mark): // BOM (Byte Order Mark):
// 00 00 FE FF UTF-32BE // 00 00 FE FF UTF-32BE
// FF FE 00 00 UTF-32LE // FF FE 00 00 UTF-32LE
// FE FF UTF-16BE // FE FF UTF-16BE
// FF FE UTF-16LE // FF FE UTF-16LE
// EF BB BF UTF-8 // EF BB BF UTF-8
const unsigned char* c = (const unsigned char *)is_->Peek4(); const unsigned char* c = (const unsigned char *)is_->Peek4();
if (!c) if (!c)
return; return;
unsigned bom = c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24); unsigned bom = c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24);
hasBOM_ = false; hasBOM_ = false;
if (bom == 0xFFFE0000) { type_ = kUTF32BE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); } if (bom == 0xFFFE0000) { type_ = kUTF32BE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); }
else if (bom == 0x0000FEFF) { type_ = kUTF32LE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); } else if (bom == 0x0000FEFF) { type_ = kUTF32LE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); }
else if ((bom & 0xFFFF) == 0xFFFE) { type_ = kUTF16BE; hasBOM_ = true; is_->Take(); is_->Take(); } else if ((bom & 0xFFFF) == 0xFFFE) { type_ = kUTF16BE; hasBOM_ = true; is_->Take(); is_->Take(); }
else if ((bom & 0xFFFF) == 0xFEFF) { type_ = kUTF16LE; hasBOM_ = true; is_->Take(); is_->Take(); } else if ((bom & 0xFFFF) == 0xFEFF) { type_ = kUTF16LE; hasBOM_ = true; is_->Take(); is_->Take(); }
else if ((bom & 0xFFFFFF) == 0xBFBBEF) { type_ = kUTF8; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); } else if ((bom & 0xFFFFFF) == 0xBFBBEF) { type_ = kUTF8; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); }
// RFC 4627: Section 3 // RFC 4627: Section 3
// "Since the first two characters of a JSON text will always be ASCII // "Since the first two characters of a JSON text will always be ASCII
// characters [RFC0020], it is possible to determine whether an octet // characters [RFC0020], it is possible to determine whether an octet
// stream is UTF-8, UTF-16 (BE or LE), or UTF-32 (BE or LE) by looking // stream is UTF-8, UTF-16 (BE or LE), or UTF-32 (BE or LE) by looking
// at the pattern of nulls in the first four octets." // at the pattern of nulls in the first four octets."
// 00 00 00 xx UTF-32BE // 00 00 00 xx UTF-32BE
// 00 xx 00 xx UTF-16BE // 00 xx 00 xx UTF-16BE
// xx 00 00 00 UTF-32LE // xx 00 00 00 UTF-32LE
// xx 00 xx 00 UTF-16LE // xx 00 xx 00 UTF-16LE
// xx xx xx xx UTF-8 // xx xx xx xx UTF-8
if (!hasBOM_) { if (!hasBOM_) {
unsigned pattern = (c[0] ? 1 : 0) | (c[1] ? 2 : 0) | (c[2] ? 4 : 0) | (c[3] ? 8 : 0); unsigned pattern = (c[0] ? 1 : 0) | (c[1] ? 2 : 0) | (c[2] ? 4 : 0) | (c[3] ? 8 : 0);
switch (pattern) { switch (pattern) {
case 0x08: type_ = kUTF32BE; break; case 0x08: type_ = kUTF32BE; break;
case 0x0A: type_ = kUTF16BE; break; case 0x0A: type_ = kUTF16BE; break;
case 0x01: type_ = kUTF32LE; break; case 0x01: type_ = kUTF32LE; break;
case 0x05: type_ = kUTF16LE; break; case 0x05: type_ = kUTF16LE; break;
case 0x0F: type_ = kUTF8; break; case 0x0F: type_ = kUTF8; break;
default: break; // Use type defined by user. default: break; // Use type defined by user.
} }
} }
// Runtime check whether the size of character type is sufficient. It only perform checks with assertion. // Runtime check whether the size of character type is sufficient. It only perform checks with assertion.
switch (type_) { switch (type_) {
case kUTF8: case kUTF8:
// Do nothing // Do nothing
break; break;
case kUTF16LE: case kUTF16LE:
case kUTF16BE: case kUTF16BE:
RAPIDJSON_ASSERT(sizeof(Ch) >= 2); RAPIDJSON_ASSERT(sizeof(Ch) >= 2);
break; break;
case kUTF32LE: case kUTF32LE:
case kUTF32BE: case kUTF32BE:
RAPIDJSON_ASSERT(sizeof(Ch) >= 4); RAPIDJSON_ASSERT(sizeof(Ch) >= 4);
break; break;
default: default:
RAPIDJSON_ASSERT(false); // Invalid type RAPIDJSON_ASSERT(false); // Invalid type
} }
} }
typedef Ch (*TakeFunc)(InputByteStream& is); typedef Ch (*TakeFunc)(InputByteStream& is);
InputByteStream* is_; InputByteStream* is_;
UTFType type_; UTFType type_;
Ch current_; Ch current_;
TakeFunc takeFunc_; TakeFunc takeFunc_;
bool hasBOM_; bool hasBOM_;
}; };
//! Output stream wrapper with dynamically bound encoding and automatic encoding detection. //! Output stream wrapper with dynamically bound encoding and automatic encoding detection.
/*! /*!
\tparam CharType Type of character for writing. \tparam CharType Type of character for writing.
\tparam InputByteStream type of output byte stream to be wrapped. \tparam InputByteStream type of output byte stream to be wrapped.
*/ */
template <typename CharType, typename OutputByteStream> template <typename CharType, typename OutputByteStream>
class AutoUTFOutputStream { class AutoUTFOutputStream {
RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1);
public: public:
typedef CharType Ch; typedef CharType Ch;
//! Constructor. //! Constructor.
/*! /*!
\param os output stream to be wrapped. \param os output stream to be wrapped.
\param type UTF encoding type. \param type UTF encoding type.
\param putBOM Whether to write BOM at the beginning of the stream. \param putBOM Whether to write BOM at the beginning of the stream.
*/ */
AutoUTFOutputStream(OutputByteStream& os, UTFType type, bool putBOM) : os_(&os), type_(type) { AutoUTFOutputStream(OutputByteStream& os, UTFType type, bool putBOM) : os_(&os), type_(type) {
// RUntime check whether the size of character type is sufficient. It only perform checks with assertion. // RUntime check whether the size of character type is sufficient. It only perform checks with assertion.
switch (type_) { switch (type_) {
case kUTF16LE: case kUTF16LE:
case kUTF16BE: case kUTF16BE:
RAPIDJSON_ASSERT(sizeof(Ch) >= 2); RAPIDJSON_ASSERT(sizeof(Ch) >= 2);
break; break;
case kUTF32LE: case kUTF32LE:
case kUTF32BE: case kUTF32BE:
RAPIDJSON_ASSERT(sizeof(Ch) >= 4); RAPIDJSON_ASSERT(sizeof(Ch) >= 4);
break; break;
case kUTF8: case kUTF8:
// Do nothing // Do nothing
break; break;
default: default:
RAPIDJSON_ASSERT(false); // Invalid UTFType RAPIDJSON_ASSERT(false); // Invalid UTFType
} }
static const PutFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Put) }; static const PutFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Put) };
putFunc_ = f[type_]; putFunc_ = f[type_];
if (putBOM) if (putBOM)
PutBOM(); PutBOM();
} }
UTFType GetType() const { return type_; } UTFType GetType() const { return type_; }
void Put(Ch c) { putFunc_(*os_, c); } void Put(Ch c) { putFunc_(*os_, c); }
void Flush() { os_->Flush(); } void Flush() { os_->Flush(); }
// Not implemented // Not implemented
Ch Peek() const { RAPIDJSON_ASSERT(false); } Ch Peek() const { RAPIDJSON_ASSERT(false); }
Ch Take() { RAPIDJSON_ASSERT(false); } Ch Take() { RAPIDJSON_ASSERT(false); }
size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; }
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
private: private:
AutoUTFOutputStream(const AutoUTFOutputStream&); AutoUTFOutputStream(const AutoUTFOutputStream&);
AutoUTFOutputStream& operator=(const AutoUTFOutputStream&); AutoUTFOutputStream& operator=(const AutoUTFOutputStream&);
void PutBOM() { void PutBOM() {
typedef void (*PutBOMFunc)(OutputByteStream&); typedef void (*PutBOMFunc)(OutputByteStream&);
static const PutBOMFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(PutBOM) }; static const PutBOMFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(PutBOM) };
f[type_](*os_); f[type_](*os_);
} }
typedef void (*PutFunc)(OutputByteStream&, Ch); typedef void (*PutFunc)(OutputByteStream&, Ch);
OutputByteStream* os_; OutputByteStream* os_;
UTFType type_; UTFType type_;
PutFunc putFunc_; PutFunc putFunc_;
}; };
#undef RAPIDJSON_ENCODINGS_FUNC #undef RAPIDJSON_ENCODINGS_FUNC

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,23 @@
// Copyright (C) 2011 Milo Yip
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef RAPIDJSON_ERROR_EN_H__ #ifndef RAPIDJSON_ERROR_EN_H__
#define RAPIDJSON_ERROR_EN_H__ #define RAPIDJSON_ERROR_EN_H__
@ -7,43 +27,43 @@ namespace rapidjson {
//! Maps error code of parsing into error message. //! Maps error code of parsing into error message.
/*! /*!
\param parseErrorCode Error code obtained in parsing. \param parseErrorCode Error code obtained in parsing.
\return the error message. \return the error message.
\note User can make a copy of this function for localization. \note User can make a copy of this function for localization.
Using switch-case is safer for future modification of error codes. Using switch-case is safer for future modification of error codes.
*/ */
inline const RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErrorCode) { inline const RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErrorCode) {
switch (parseErrorCode) { switch (parseErrorCode) {
case kParseErrorNone: return RAPIDJSON_ERROR_STRING("No error."); case kParseErrorNone: return RAPIDJSON_ERROR_STRING("No error.");
case kParseErrorDocumentEmpty: return RAPIDJSON_ERROR_STRING("The document is empty."); case kParseErrorDocumentEmpty: return RAPIDJSON_ERROR_STRING("The document is empty.");
case kParseErrorDocumentRootNotObjectOrArray: return RAPIDJSON_ERROR_STRING("The document root must be either object or array."); case kParseErrorDocumentRootNotObjectOrArray: return RAPIDJSON_ERROR_STRING("The document root must be either object or array.");
case kParseErrorDocumentRootNotSingular: return RAPIDJSON_ERROR_STRING("The document root must not follow by other values."); case kParseErrorDocumentRootNotSingular: return RAPIDJSON_ERROR_STRING("The document root must not follow by other values.");
case kParseErrorValueInvalid: return RAPIDJSON_ERROR_STRING("Invalid value."); case kParseErrorValueInvalid: return RAPIDJSON_ERROR_STRING("Invalid value.");
case kParseErrorObjectMissName: return RAPIDJSON_ERROR_STRING("Missing a name for object member."); case kParseErrorObjectMissName: return RAPIDJSON_ERROR_STRING("Missing a name for object member.");
case kParseErrorObjectMissColon: return RAPIDJSON_ERROR_STRING("Missing a colon after a name of object member."); case kParseErrorObjectMissColon: return RAPIDJSON_ERROR_STRING("Missing a colon after a name of object member.");
case kParseErrorObjectMissCommaOrCurlyBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or '}' after an object member."); case kParseErrorObjectMissCommaOrCurlyBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or '}' after an object member.");
case kParseErrorArrayMissCommaOrSquareBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or ']' after an array element."); case kParseErrorArrayMissCommaOrSquareBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or ']' after an array element.");
case kParseErrorStringUnicodeEscapeInvalidHex: return RAPIDJSON_ERROR_STRING("Incorrect hex digit after \\u escape in string."); case kParseErrorStringUnicodeEscapeInvalidHex: return RAPIDJSON_ERROR_STRING("Incorrect hex digit after \\u escape in string.");
case kParseErrorStringUnicodeSurrogateInvalid: return RAPIDJSON_ERROR_STRING("The surrogate pair in string is invalid."); case kParseErrorStringUnicodeSurrogateInvalid: return RAPIDJSON_ERROR_STRING("The surrogate pair in string is invalid.");
case kParseErrorStringEscapeInvalid: return RAPIDJSON_ERROR_STRING("Invalid escape character in string."); case kParseErrorStringEscapeInvalid: return RAPIDJSON_ERROR_STRING("Invalid escape character in string.");
case kParseErrorStringMissQuotationMark: return RAPIDJSON_ERROR_STRING("Missing a closing quotation mark in string."); case kParseErrorStringMissQuotationMark: return RAPIDJSON_ERROR_STRING("Missing a closing quotation mark in string.");
case kParseErrorStringInvalidEncoding: return RAPIDJSON_ERROR_STRING("Invalid encoding in string."); case kParseErrorStringInvalidEncoding: return RAPIDJSON_ERROR_STRING("Invalid encoding in string.");
case kParseErrorNumberTooBig: return RAPIDJSON_ERROR_STRING("Number too big to be stored in double."); case kParseErrorNumberTooBig: return RAPIDJSON_ERROR_STRING("Number too big to be stored in double.");
case kParseErrorNumberMissFraction: return RAPIDJSON_ERROR_STRING("Miss fraction part in number."); case kParseErrorNumberMissFraction: return RAPIDJSON_ERROR_STRING("Miss fraction part in number.");
case kParseErrorNumberMissExponent: return RAPIDJSON_ERROR_STRING("Miss exponent in number."); case kParseErrorNumberMissExponent: return RAPIDJSON_ERROR_STRING("Miss exponent in number.");
case kParseErrorTermination: return RAPIDJSON_ERROR_STRING("Terminate parsing due to Handler error."); case kParseErrorTermination: return RAPIDJSON_ERROR_STRING("Terminate parsing due to Handler error.");
case kParseErrorUnspecificSyntaxError: return RAPIDJSON_ERROR_STRING("Unspecific syntax error."); case kParseErrorUnspecificSyntaxError: return RAPIDJSON_ERROR_STRING("Unspecific syntax error.");
default: default:
return RAPIDJSON_ERROR_STRING("Unknown error."); return RAPIDJSON_ERROR_STRING("Unknown error.");
} }
} }
} // namespace rapidjson } // namespace rapidjson

View File

@ -1,3 +1,23 @@
// Copyright (C) 2011 Milo Yip
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef RAPIDJSON_ERROR_ERROR_H__ #ifndef RAPIDJSON_ERROR_ERROR_H__
#define RAPIDJSON_ERROR_ERROR_H__ #define RAPIDJSON_ERROR_ERROR_H__
@ -6,8 +26,8 @@
//! Character type of error messages. //! Character type of error messages.
/*! The default charater type is char. /*! The default charater type is char.
On Windows, user can define this macro as TCHAR for supporting both On Windows, user can define this macro as TCHAR for supporting both
unicode/non-unicode settings. unicode/non-unicode settings.
*/ */
#ifndef RAPIDJSON_ERROR_CHARTYPE #ifndef RAPIDJSON_ERROR_CHARTYPE
#define RAPIDJSON_ERROR_CHARTYPE char #define RAPIDJSON_ERROR_CHARTYPE char
@ -18,8 +38,8 @@
//! Macro for converting string literial to RAPIDJSON_ERROR_CHARTYPE[]. //! Macro for converting string literial to RAPIDJSON_ERROR_CHARTYPE[].
/*! By default this conversion macro does nothing. /*! By default this conversion macro does nothing.
On Windows, user can define this macro as _T(x) for supporting both On Windows, user can define this macro as _T(x) for supporting both
unicode/non-unicode settings. unicode/non-unicode settings.
*/ */
#ifndef RAPIDJSON_ERROR_STRING #ifndef RAPIDJSON_ERROR_STRING
#define RAPIDJSON_ERROR_STRING(x) x #define RAPIDJSON_ERROR_STRING(x) x
@ -34,85 +54,85 @@ namespace rapidjson {
/*! \see GenericReader::Parse, GenericReader::GetParseErrorCode /*! \see GenericReader::Parse, GenericReader::GetParseErrorCode
*/ */
enum ParseErrorCode { enum ParseErrorCode {
kParseErrorNone = 0, //!< No error. kParseErrorNone = 0, //!< No error.
kParseErrorDocumentEmpty, //!< The document is empty. kParseErrorDocumentEmpty, //!< The document is empty.
kParseErrorDocumentRootNotObjectOrArray, //!< The document root must be either object or array. kParseErrorDocumentRootNotObjectOrArray, //!< The document root must be either object or array.
kParseErrorDocumentRootNotSingular, //!< The document root must not follow by other values. kParseErrorDocumentRootNotSingular, //!< The document root must not follow by other values.
kParseErrorValueInvalid, //!< Invalid value. kParseErrorValueInvalid, //!< Invalid value.
kParseErrorObjectMissName, //!< Missing a name for object member. kParseErrorObjectMissName, //!< Missing a name for object member.
kParseErrorObjectMissColon, //!< Missing a colon after a name of object member. kParseErrorObjectMissColon, //!< Missing a colon after a name of object member.
kParseErrorObjectMissCommaOrCurlyBracket, //!< Missing a comma or '}' after an object member. kParseErrorObjectMissCommaOrCurlyBracket, //!< Missing a comma or '}' after an object member.
kParseErrorArrayMissCommaOrSquareBracket, //!< Missing a comma or ']' after an array element. kParseErrorArrayMissCommaOrSquareBracket, //!< Missing a comma or ']' after an array element.
kParseErrorStringUnicodeEscapeInvalidHex, //!< Incorrect hex digit after \\u escape in string. kParseErrorStringUnicodeEscapeInvalidHex, //!< Incorrect hex digit after \\u escape in string.
kParseErrorStringUnicodeSurrogateInvalid, //!< The surrogate pair in string is invalid. kParseErrorStringUnicodeSurrogateInvalid, //!< The surrogate pair in string is invalid.
kParseErrorStringEscapeInvalid, //!< Invalid escape character in string. kParseErrorStringEscapeInvalid, //!< Invalid escape character in string.
kParseErrorStringMissQuotationMark, //!< Missing a closing quotation mark in string. kParseErrorStringMissQuotationMark, //!< Missing a closing quotation mark in string.
kParseErrorStringInvalidEncoding, //!< Invalid encoding in string. kParseErrorStringInvalidEncoding, //!< Invalid encoding in string.
kParseErrorNumberTooBig, //!< Number too big to be stored in double. kParseErrorNumberTooBig, //!< Number too big to be stored in double.
kParseErrorNumberMissFraction, //!< Miss fraction part in number. kParseErrorNumberMissFraction, //!< Miss fraction part in number.
kParseErrorNumberMissExponent, //!< Miss exponent in number. kParseErrorNumberMissExponent, //!< Miss exponent in number.
kParseErrorTermination, //!< Parsing was terminated. kParseErrorTermination, //!< Parsing was terminated.
kParseErrorUnspecificSyntaxError, //!< Unspecific syntax error. kParseErrorUnspecificSyntaxError, //!< Unspecific syntax error.
}; };
//! Result of parsing (wraps ParseErrorCode) //! Result of parsing (wraps ParseErrorCode)
/*! /*!
\code \code
Document doc; Document doc;
ParseResult ok = doc.Parse("[42]"); ParseResult ok = doc.Parse("[42]");
if (!ok) { if (!ok) {
fprintf(stderr, "JSON parse error: %s (%u)", fprintf(stderr, "JSON parse error: %s (%u)",
GetParseError_En(ok.Code()), ok.Offset()); GetParseError_En(ok.Code()), ok.Offset());
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
\endcode \endcode
\see GenericReader::Parse, GenericDocument::Parse \see GenericReader::Parse, GenericDocument::Parse
*/ */
struct ParseResult { struct ParseResult {
//! Default constructor, no error. //! Default constructor, no error.
ParseResult() : code_(kParseErrorNone), offset_(0) {} ParseResult() : code_(kParseErrorNone), offset_(0) {}
//! Constructor to set an error. //! Constructor to set an error.
ParseResult(ParseErrorCode code, size_t offset) : code_(code), offset_(offset) {} ParseResult(ParseErrorCode code, size_t offset) : code_(code), offset_(offset) {}
//! Get the error code. //! Get the error code.
ParseErrorCode Code() const { return code_; } ParseErrorCode Code() const { return code_; }
//! Get the error offset, if \ref IsError(), 0 otherwise. //! Get the error offset, if \ref IsError(), 0 otherwise.
size_t Offset() const { return offset_; } size_t Offset() const { return offset_; }
//! Conversion to \c bool, returns \c true, iff !\ref IsError(). //! Conversion to \c bool, returns \c true, iff !\ref IsError().
operator bool() const { return !IsError(); } operator bool() const { return !IsError(); }
//! Whether the result is an error. //! Whether the result is an error.
bool IsError() const { return code_ != kParseErrorNone; } bool IsError() const { return code_ != kParseErrorNone; }
bool operator==(const ParseResult& that) const { return code_ == that.code_; } bool operator==(const ParseResult& that) const { return code_ == that.code_; }
bool operator==(ParseErrorCode code) const { return code_ == code; } bool operator==(ParseErrorCode code) const { return code_ == code; }
friend bool operator==(ParseErrorCode code, const ParseResult & err) { return code == err.code_; } friend bool operator==(ParseErrorCode code, const ParseResult & err) { return code == err.code_; }
//! Reset error code. //! Reset error code.
void Clear() { Set(kParseErrorNone); } void Clear() { Set(kParseErrorNone); }
//! Update error code and offset. //! Update error code and offset.
void Set(ParseErrorCode code, size_t offset = 0) { code_ = code; offset_ = offset; } void Set(ParseErrorCode code, size_t offset = 0) { code_ = code; offset_ = offset; }
private: private:
ParseErrorCode code_; ParseErrorCode code_;
size_t offset_; size_t offset_;
}; };
//! Function pointer type of GetParseError(). //! Function pointer type of GetParseError().
/*! This is the prototype for GetParseError_X(), where X is a locale. /*! This is the prototype for GetParseError_X(), where X is a locale.
User can dynamically change locale in runtime, e.g.: User can dynamically change locale in runtime, e.g.:
\code \code
GetParseErrorFunc GetParseError = GetParseError_En; // or whatever GetParseErrorFunc GetParseError = GetParseError_En; // or whatever
const RAPIDJSON_ERROR_CHARTYPE* s = GetParseError(document.GetParseErrorCode()); const RAPIDJSON_ERROR_CHARTYPE* s = GetParseError(document.GetParseErrorCode());
\endcode \endcode
*/ */

View File

@ -1,3 +1,23 @@
// Copyright (C) 2011 Milo Yip
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef RAPIDJSON_FILEREADSTREAM_H_ #ifndef RAPIDJSON_FILEREADSTREAM_H_
#define RAPIDJSON_FILEREADSTREAM_H_ #define RAPIDJSON_FILEREADSTREAM_H_
@ -8,65 +28,65 @@ namespace rapidjson {
//! File byte stream for input using fread(). //! File byte stream for input using fread().
/*! /*!
\note implements Stream concept \note implements Stream concept
*/ */
class FileReadStream { class FileReadStream {
public: public:
typedef char Ch; //!< Character type (byte). typedef char Ch; //!< Character type (byte).
//! Constructor. //! Constructor.
/*! /*!
\param fp File pointer opened for read. \param fp File pointer opened for read.
\param buffer user-supplied buffer. \param buffer user-supplied buffer.
\param bufferSize size of buffer in bytes. Must >=4 bytes. \param bufferSize size of buffer in bytes. Must >=4 bytes.
*/ */
FileReadStream(FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferSize_(bufferSize), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) { FileReadStream(FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferSize_(bufferSize), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) {
RAPIDJSON_ASSERT(fp_ != 0); RAPIDJSON_ASSERT(fp_ != 0);
RAPIDJSON_ASSERT(bufferSize >= 4); RAPIDJSON_ASSERT(bufferSize >= 4);
Read(); Read();
} }
Ch Peek() const { return *current_; } Ch Peek() const { return *current_; }
Ch Take() { Ch c = *current_; Read(); return c; } Ch Take() { Ch c = *current_; Read(); return c; }
size_t Tell() const { return count_ + static_cast<size_t>(current_ - buffer_); } size_t Tell() const { return count_ + static_cast<size_t>(current_ - buffer_); }
// Not implemented // Not implemented
void Put(Ch) { RAPIDJSON_ASSERT(false); } void Put(Ch) { RAPIDJSON_ASSERT(false); }
void Flush() { RAPIDJSON_ASSERT(false); } void Flush() { RAPIDJSON_ASSERT(false); }
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
// For encoding detection only. // For encoding detection only.
const Ch* Peek4() const { const Ch* Peek4() const {
return (current_ + 4 <= bufferLast_) ? current_ : 0; return (current_ + 4 <= bufferLast_) ? current_ : 0;
} }
private: private:
void Read() { void Read() {
if (current_ < bufferLast_) if (current_ < bufferLast_)
++current_; ++current_;
else if (!eof_) { else if (!eof_) {
count_ += readCount_; count_ += readCount_;
readCount_ = fread(buffer_, 1, bufferSize_, fp_); readCount_ = fread(buffer_, 1, bufferSize_, fp_);
bufferLast_ = buffer_ + readCount_ - 1; bufferLast_ = buffer_ + readCount_ - 1;
current_ = buffer_; current_ = buffer_;
if (readCount_ < bufferSize_) { if (readCount_ < bufferSize_) {
buffer_[readCount_] = '\0'; buffer_[readCount_] = '\0';
++bufferLast_; ++bufferLast_;
eof_ = true; eof_ = true;
} }
} }
} }
FILE* fp_; FILE* fp_;
Ch *buffer_; Ch *buffer_;
size_t bufferSize_; size_t bufferSize_;
Ch *bufferLast_; Ch *bufferLast_;
Ch *current_; Ch *current_;
size_t readCount_; size_t readCount_;
size_t count_; //!< Number of characters read size_t count_; //!< Number of characters read
bool eof_; bool eof_;
}; };
} // namespace rapidjson } // namespace rapidjson

View File

@ -1,3 +1,23 @@
// Copyright (C) 2011 Milo Yip
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef RAPIDJSON_FILESTREAM_H_ #ifndef RAPIDJSON_FILESTREAM_H_
#define RAPIDJSON_FILESTREAM_H_ #define RAPIDJSON_FILESTREAM_H_
@ -8,44 +28,44 @@ namespace rapidjson {
//! (Depreciated) Wrapper of C file stream for input or output. //! (Depreciated) Wrapper of C file stream for input or output.
/*! /*!
This simple wrapper does not check the validity of the stream. This simple wrapper does not check the validity of the stream.
\note implements Stream concept \note implements Stream concept
\note deprecated: This was only for basic testing in version 0.1, it is found that the performance is very low by using fgetc(). Use FileReadStream instead. \note deprecated: This was only for basic testing in version 0.1, it is found that the performance is very low by using fgetc(). Use FileReadStream instead.
*/ */
class FileStream { class FileStream {
public: public:
typedef char Ch; //!< Character type. Only support char. typedef char Ch; //!< Character type. Only support char.
FileStream(FILE* fp) : fp_(fp), current_('\0'), count_(0) { Read(); } FileStream(FILE* fp) : fp_(fp), current_('\0'), count_(0) { Read(); }
char Peek() const { return current_; } char Peek() const { return current_; }
char Take() { char c = current_; Read(); return c; } char Take() { char c = current_; Read(); return c; }
size_t Tell() const { return count_; } size_t Tell() const { return count_; }
void Put(char c) { fputc(c, fp_); } void Put(char c) { fputc(c, fp_); }
void Flush() { fflush(fp_); } void Flush() { fflush(fp_); }
// Not implemented // Not implemented
char* PutBegin() { return 0; } char* PutBegin() { return 0; }
size_t PutEnd(char*) { return 0; } size_t PutEnd(char*) { return 0; }
private: private:
// Prohibit copy constructor & assignment operator. // Prohibit copy constructor & assignment operator.
FileStream(const FileStream&); FileStream(const FileStream&);
FileStream& operator=(const FileStream&); FileStream& operator=(const FileStream&);
void Read() { void Read() {
RAPIDJSON_ASSERT(fp_ != 0); RAPIDJSON_ASSERT(fp_ != 0);
int c = fgetc(fp_); int c = fgetc(fp_);
if (c != EOF) { if (c != EOF) {
current_ = (char)c; current_ = (char)c;
count_++; count_++;
} }
else if (current_ != '\0') else if (current_ != '\0')
current_ = '\0'; current_ = '\0';
} }
FILE* fp_; FILE* fp_;
char current_; char current_;
size_t count_; size_t count_;
}; };
} // namespace rapidjson } // namespace rapidjson

View File

@ -1,3 +1,23 @@
// Copyright (C) 2011 Milo Yip
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef RAPIDJSON_FILEWRITESTREAM_H_ #ifndef RAPIDJSON_FILEWRITESTREAM_H_
#define RAPIDJSON_FILEWRITESTREAM_H_ #define RAPIDJSON_FILEWRITESTREAM_H_
@ -8,68 +28,68 @@ namespace rapidjson {
//! Wrapper of C file stream for input using fread(). //! Wrapper of C file stream for input using fread().
/*! /*!
\note implements Stream concept \note implements Stream concept
*/ */
class FileWriteStream { class FileWriteStream {
public: public:
typedef char Ch; //!< Character type. Only support char. typedef char Ch; //!< Character type. Only support char.
FileWriteStream(FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferEnd_(buffer + bufferSize), current_(buffer_) { FileWriteStream(FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferEnd_(buffer + bufferSize), current_(buffer_) {
RAPIDJSON_ASSERT(fp_ != 0); RAPIDJSON_ASSERT(fp_ != 0);
} }
void Put(char c) { void Put(char c) {
if (current_ >= bufferEnd_) if (current_ >= bufferEnd_)
Flush(); Flush();
*current_++ = c; *current_++ = c;
} }
void PutN(char c, size_t n) { void PutN(char c, size_t n) {
size_t avail = static_cast<size_t>(bufferEnd_ - current_); size_t avail = static_cast<size_t>(bufferEnd_ - current_);
while (n > avail) { while (n > avail) {
memset(current_, c, avail); memset(current_, c, avail);
current_ += avail; current_ += avail;
Flush(); Flush();
n -= avail; n -= avail;
avail = static_cast<size_t>(bufferEnd_ - current_); avail = static_cast<size_t>(bufferEnd_ - current_);
} }
if (n > 0) { if (n > 0) {
memset(current_, c, n); memset(current_, c, n);
current_ += n; current_ += n;
} }
} }
void Flush() { void Flush() {
if (current_ != buffer_) { if (current_ != buffer_) {
fwrite(buffer_, 1, static_cast<size_t>(current_ - buffer_), fp_); fwrite(buffer_, 1, static_cast<size_t>(current_ - buffer_), fp_);
current_ = buffer_; current_ = buffer_;
} }
} }
// Not implemented // Not implemented
char Peek() const { RAPIDJSON_ASSERT(false); return 0; } char Peek() const { RAPIDJSON_ASSERT(false); return 0; }
char Take() { RAPIDJSON_ASSERT(false); return 0; } char Take() { RAPIDJSON_ASSERT(false); return 0; }
size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; }
char* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } char* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
size_t PutEnd(char*) { RAPIDJSON_ASSERT(false); return 0; } size_t PutEnd(char*) { RAPIDJSON_ASSERT(false); return 0; }
private: private:
// Prohibit copy constructor & assignment operator. // Prohibit copy constructor & assignment operator.
FileWriteStream(const FileWriteStream&); FileWriteStream(const FileWriteStream&);
FileWriteStream& operator=(const FileWriteStream&); FileWriteStream& operator=(const FileWriteStream&);
FILE* fp_; FILE* fp_;
char *buffer_; char *buffer_;
char *bufferEnd_; char *bufferEnd_;
char *current_; char *current_;
}; };
//! Implement specialized version of PutN() with memset() for better performance. //! Implement specialized version of PutN() with memset() for better performance.
template<> template<>
inline void PutN(FileWriteStream& stream, char c, size_t n) { inline void PutN(FileWriteStream& stream, char c, size_t n) {
stream.PutN(c, n); stream.PutN(c, n);
} }
} // namespace rapidjson } // namespace rapidjson

View File

@ -1,5 +1,26 @@
// Modified from https://github.com/miloyip/dtoa-benchmark/blob/master/src/milo/dtoa_milo.h // Copyright (C) 2011 Milo Yip
// API is changed to return the character passed the end of string, without writing '\0' //
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
// This is a C++ header-only implementation of Grisu2 algorithm from the publication:
// Loitsch, Florian. "Printing floating-point numbers quickly and accurately with
// integers." ACM Sigplan Notices 45.6 (2010): 233-243.
#ifndef RAPIDJSON_DTOA_ #ifndef RAPIDJSON_DTOA_
#define RAPIDJSON_DTOA_ #define RAPIDJSON_DTOA_
@ -22,385 +43,369 @@ RAPIDJSON_DIAG_OFF(effc++)
#endif #endif
struct DiyFp { struct DiyFp {
DiyFp() {} DiyFp() {}
DiyFp(uint64_t f, int e) : f(f), e(e) {} DiyFp(uint64_t f, int e) : f(f), e(e) {}
DiyFp(double d) { DiyFp(double d) {
union { union {
double d; double d;
uint64_t u64; uint64_t u64;
} u = { d }; } u = { d };
int biased_e = (u.u64 & kDpExponentMask) >> kDpSignificandSize; int biased_e = (u.u64 & kDpExponentMask) >> kDpSignificandSize;
uint64_t significand = (u.u64 & kDpSignificandMask); uint64_t significand = (u.u64 & kDpSignificandMask);
if (biased_e != 0) { if (biased_e != 0) {
f = significand + kDpHiddenBit; f = significand + kDpHiddenBit;
e = biased_e - kDpExponentBias; e = biased_e - kDpExponentBias;
} }
else { else {
f = significand; f = significand;
e = kDpMinExponent + 1; e = kDpMinExponent + 1;
} }
} }
DiyFp operator-(const DiyFp& rhs) const { DiyFp operator-(const DiyFp& rhs) const {
return DiyFp(f - rhs.f, e); return DiyFp(f - rhs.f, e);
} }
DiyFp operator*(const DiyFp& rhs) const { DiyFp operator*(const DiyFp& rhs) const {
#if defined(_MSC_VER) && defined(_M_AMD64) #if defined(_MSC_VER) && defined(_M_AMD64)
uint64_t h; uint64_t h;
uint64_t l = _umul128(f, rhs.f, &h); uint64_t l = _umul128(f, rhs.f, &h);
if (l & (uint64_t(1) << 63)) // rounding if (l & (uint64_t(1) << 63)) // rounding
h++; h++;
return DiyFp(h, e + rhs.e + 64); return DiyFp(h, e + rhs.e + 64);
#elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__) #elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__)
unsigned __int128 p = static_cast<unsigned __int128>(f) * static_cast<unsigned __int128>(rhs.f); unsigned __int128 p = static_cast<unsigned __int128>(f) * static_cast<unsigned __int128>(rhs.f);
uint64_t h = p >> 64; uint64_t h = p >> 64;
uint64_t l = static_cast<uint64_t>(p); uint64_t l = static_cast<uint64_t>(p);
if (l & (uint64_t(1) << 63)) // rounding if (l & (uint64_t(1) << 63)) // rounding
h++; h++;
return DiyFp(h, e + rhs.e + 64); return DiyFp(h, e + rhs.e + 64);
#else #else
const uint64_t M32 = 0xFFFFFFFF; const uint64_t M32 = 0xFFFFFFFF;
const uint64_t a = f >> 32; const uint64_t a = f >> 32;
const uint64_t b = f & M32; const uint64_t b = f & M32;
const uint64_t c = rhs.f >> 32; const uint64_t c = rhs.f >> 32;
const uint64_t d = rhs.f & M32; const uint64_t d = rhs.f & M32;
const uint64_t ac = a * c; const uint64_t ac = a * c;
const uint64_t bc = b * c; const uint64_t bc = b * c;
const uint64_t ad = a * d; const uint64_t ad = a * d;
const uint64_t bd = b * d; const uint64_t bd = b * d;
uint64_t tmp = (bd >> 32) + (ad & M32) + (bc & M32); uint64_t tmp = (bd >> 32) + (ad & M32) + (bc & M32);
tmp += 1U << 31; /// mult_round tmp += 1U << 31; /// mult_round
return DiyFp(ac + (ad >> 32) + (bc >> 32) + (tmp >> 32), e + rhs.e + 64); return DiyFp(ac + (ad >> 32) + (bc >> 32) + (tmp >> 32), e + rhs.e + 64);
#endif #endif
} }
DiyFp Normalize() const { DiyFp Normalize() const {
#if defined(_MSC_VER) && defined(_M_AMD64) #if defined(_MSC_VER) && defined(_M_AMD64)
unsigned long index; unsigned long index;
_BitScanReverse64(&index, f); _BitScanReverse64(&index, f);
return DiyFp(f << (63 - index), e - (63 - index)); return DiyFp(f << (63 - index), e - (63 - index));
#elif defined(__GNUC__) #elif defined(__GNUC__)
int s = __builtin_clzll(f) + 1; int s = __builtin_clzll(f) + 1;
return DiyFp(f << s, e - s); return DiyFp(f << s, e - s);
#else #else
DiyFp res = *this; DiyFp res = *this;
while (!(res.f & kDpHiddenBit)) { while (!(res.f & kDpHiddenBit)) {
res.f <<= 1; res.f <<= 1;
res.e--; res.e--;
} }
res.f <<= (kDiySignificandSize - kDpSignificandSize - 1); res.f <<= (kDiySignificandSize - kDpSignificandSize - 1);
res.e = res.e - (kDiySignificandSize - kDpSignificandSize - 1); res.e = res.e - (kDiySignificandSize - kDpSignificandSize - 1);
return res; return res;
#endif #endif
} }
DiyFp NormalizeBoundary() const { DiyFp NormalizeBoundary() const {
#if defined(_MSC_VER) && defined(_M_AMD64) #if defined(_MSC_VER) && defined(_M_AMD64)
unsigned long index; unsigned long index;
_BitScanReverse64(&index, f); _BitScanReverse64(&index, f);
return DiyFp (f << (63 - index), e - (63 - index)); return DiyFp (f << (63 - index), e - (63 - index));
#else #else
DiyFp res = *this; DiyFp res = *this;
while (!(res.f & (kDpHiddenBit << 1))) { while (!(res.f & (kDpHiddenBit << 1))) {
res.f <<= 1; res.f <<= 1;
res.e--; res.e--;
} }
res.f <<= (kDiySignificandSize - kDpSignificandSize - 2); res.f <<= (kDiySignificandSize - kDpSignificandSize - 2);
res.e = res.e - (kDiySignificandSize - kDpSignificandSize - 2); res.e = res.e - (kDiySignificandSize - kDpSignificandSize - 2);
return res; return res;
#endif #endif
} }
void NormalizedBoundaries(DiyFp* minus, DiyFp* plus) const { void NormalizedBoundaries(DiyFp* minus, DiyFp* plus) const {
DiyFp pl = DiyFp((f << 1) + 1, e - 1).NormalizeBoundary(); DiyFp pl = DiyFp((f << 1) + 1, e - 1).NormalizeBoundary();
DiyFp mi = (f == kDpHiddenBit) ? DiyFp((f << 2) - 1, e - 2) : DiyFp((f << 1) - 1, e - 1); DiyFp mi = (f == kDpHiddenBit) ? DiyFp((f << 2) - 1, e - 2) : DiyFp((f << 1) - 1, e - 1);
mi.f <<= mi.e - pl.e; mi.f <<= mi.e - pl.e;
mi.e = pl.e; mi.e = pl.e;
*plus = pl; *plus = pl;
*minus = mi; *minus = mi;
} }
static const int kDiySignificandSize = 64; static const int kDiySignificandSize = 64;
static const int kDpSignificandSize = 52; static const int kDpSignificandSize = 52;
static const int kDpExponentBias = 0x3FF + kDpSignificandSize; static const int kDpExponentBias = 0x3FF + kDpSignificandSize;
static const int kDpMinExponent = -kDpExponentBias; static const int kDpMinExponent = -kDpExponentBias;
static const uint64_t kDpExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000); static const uint64_t kDpExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000);
static const uint64_t kDpSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF); static const uint64_t kDpSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF);
static const uint64_t kDpHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000); static const uint64_t kDpHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000);
uint64_t f; uint64_t f;
int e; int e;
}; };
inline DiyFp GetCachedPower(int e, int* K) { inline DiyFp GetCachedPower(int e, int* K) {
// 10^-348, 10^-340, ..., 10^340 // 10^-348, 10^-340, ..., 10^340
static const uint64_t kCachedPowers_F[] = { static const uint64_t kCachedPowers_F[] = {
RAPIDJSON_UINT64_C2(0xfa8fd5a0, 0x081c0288), RAPIDJSON_UINT64_C2(0xbaaee17f, 0xa23ebf76), RAPIDJSON_UINT64_C2(0xfa8fd5a0, 0x081c0288), RAPIDJSON_UINT64_C2(0xbaaee17f, 0xa23ebf76),
RAPIDJSON_UINT64_C2(0x8b16fb20, 0x3055ac76), RAPIDJSON_UINT64_C2(0xcf42894a, 0x5dce35ea), RAPIDJSON_UINT64_C2(0x8b16fb20, 0x3055ac76), RAPIDJSON_UINT64_C2(0xcf42894a, 0x5dce35ea),
RAPIDJSON_UINT64_C2(0x9a6bb0aa, 0x55653b2d), RAPIDJSON_UINT64_C2(0xe61acf03, 0x3d1a45df), RAPIDJSON_UINT64_C2(0x9a6bb0aa, 0x55653b2d), RAPIDJSON_UINT64_C2(0xe61acf03, 0x3d1a45df),
RAPIDJSON_UINT64_C2(0xab70fe17, 0xc79ac6ca), RAPIDJSON_UINT64_C2(0xff77b1fc, 0xbebcdc4f), RAPIDJSON_UINT64_C2(0xab70fe17, 0xc79ac6ca), RAPIDJSON_UINT64_C2(0xff77b1fc, 0xbebcdc4f),
RAPIDJSON_UINT64_C2(0xbe5691ef, 0x416bd60c), RAPIDJSON_UINT64_C2(0x8dd01fad, 0x907ffc3c), RAPIDJSON_UINT64_C2(0xbe5691ef, 0x416bd60c), RAPIDJSON_UINT64_C2(0x8dd01fad, 0x907ffc3c),
RAPIDJSON_UINT64_C2(0xd3515c28, 0x31559a83), RAPIDJSON_UINT64_C2(0x9d71ac8f, 0xada6c9b5), RAPIDJSON_UINT64_C2(0xd3515c28, 0x31559a83), RAPIDJSON_UINT64_C2(0x9d71ac8f, 0xada6c9b5),
RAPIDJSON_UINT64_C2(0xea9c2277, 0x23ee8bcb), RAPIDJSON_UINT64_C2(0xaecc4991, 0x4078536d), RAPIDJSON_UINT64_C2(0xea9c2277, 0x23ee8bcb), RAPIDJSON_UINT64_C2(0xaecc4991, 0x4078536d),
RAPIDJSON_UINT64_C2(0x823c1279, 0x5db6ce57), RAPIDJSON_UINT64_C2(0xc2109436, 0x4dfb5637), RAPIDJSON_UINT64_C2(0x823c1279, 0x5db6ce57), RAPIDJSON_UINT64_C2(0xc2109436, 0x4dfb5637),
RAPIDJSON_UINT64_C2(0x9096ea6f, 0x3848984f), RAPIDJSON_UINT64_C2(0xd77485cb, 0x25823ac7), RAPIDJSON_UINT64_C2(0x9096ea6f, 0x3848984f), RAPIDJSON_UINT64_C2(0xd77485cb, 0x25823ac7),
RAPIDJSON_UINT64_C2(0xa086cfcd, 0x97bf97f4), RAPIDJSON_UINT64_C2(0xef340a98, 0x172aace5), RAPIDJSON_UINT64_C2(0xa086cfcd, 0x97bf97f4), RAPIDJSON_UINT64_C2(0xef340a98, 0x172aace5),
RAPIDJSON_UINT64_C2(0xb23867fb, 0x2a35b28e), RAPIDJSON_UINT64_C2(0x84c8d4df, 0xd2c63f3b), RAPIDJSON_UINT64_C2(0xb23867fb, 0x2a35b28e), RAPIDJSON_UINT64_C2(0x84c8d4df, 0xd2c63f3b),
RAPIDJSON_UINT64_C2(0xc5dd4427, 0x1ad3cdba), RAPIDJSON_UINT64_C2(0x936b9fce, 0xbb25c996), RAPIDJSON_UINT64_C2(0xc5dd4427, 0x1ad3cdba), RAPIDJSON_UINT64_C2(0x936b9fce, 0xbb25c996),
RAPIDJSON_UINT64_C2(0xdbac6c24, 0x7d62a584), RAPIDJSON_UINT64_C2(0xa3ab6658, 0x0d5fdaf6), RAPIDJSON_UINT64_C2(0xdbac6c24, 0x7d62a584), RAPIDJSON_UINT64_C2(0xa3ab6658, 0x0d5fdaf6),
RAPIDJSON_UINT64_C2(0xf3e2f893, 0xdec3f126), RAPIDJSON_UINT64_C2(0xb5b5ada8, 0xaaff80b8), RAPIDJSON_UINT64_C2(0xf3e2f893, 0xdec3f126), RAPIDJSON_UINT64_C2(0xb5b5ada8, 0xaaff80b8),
RAPIDJSON_UINT64_C2(0x87625f05, 0x6c7c4a8b), RAPIDJSON_UINT64_C2(0xc9bcff60, 0x34c13053), RAPIDJSON_UINT64_C2(0x87625f05, 0x6c7c4a8b), RAPIDJSON_UINT64_C2(0xc9bcff60, 0x34c13053),
RAPIDJSON_UINT64_C2(0x964e858c, 0x91ba2655), RAPIDJSON_UINT64_C2(0xdff97724, 0x70297ebd), RAPIDJSON_UINT64_C2(0x964e858c, 0x91ba2655), RAPIDJSON_UINT64_C2(0xdff97724, 0x70297ebd),
RAPIDJSON_UINT64_C2(0xa6dfbd9f, 0xb8e5b88f), RAPIDJSON_UINT64_C2(0xf8a95fcf, 0x88747d94), RAPIDJSON_UINT64_C2(0xa6dfbd9f, 0xb8e5b88f), RAPIDJSON_UINT64_C2(0xf8a95fcf, 0x88747d94),
RAPIDJSON_UINT64_C2(0xb9447093, 0x8fa89bcf), RAPIDJSON_UINT64_C2(0x8a08f0f8, 0xbf0f156b), RAPIDJSON_UINT64_C2(0xb9447093, 0x8fa89bcf), RAPIDJSON_UINT64_C2(0x8a08f0f8, 0xbf0f156b),
RAPIDJSON_UINT64_C2(0xcdb02555, 0x653131b6), RAPIDJSON_UINT64_C2(0x993fe2c6, 0xd07b7fac), RAPIDJSON_UINT64_C2(0xcdb02555, 0x653131b6), RAPIDJSON_UINT64_C2(0x993fe2c6, 0xd07b7fac),
RAPIDJSON_UINT64_C2(0xe45c10c4, 0x2a2b3b06), RAPIDJSON_UINT64_C2(0xaa242499, 0x697392d3), RAPIDJSON_UINT64_C2(0xe45c10c4, 0x2a2b3b06), RAPIDJSON_UINT64_C2(0xaa242499, 0x697392d3),
RAPIDJSON_UINT64_C2(0xfd87b5f2, 0x8300ca0e), RAPIDJSON_UINT64_C2(0xbce50864, 0x92111aeb), RAPIDJSON_UINT64_C2(0xfd87b5f2, 0x8300ca0e), RAPIDJSON_UINT64_C2(0xbce50864, 0x92111aeb),
RAPIDJSON_UINT64_C2(0x8cbccc09, 0x6f5088cc), RAPIDJSON_UINT64_C2(0xd1b71758, 0xe219652c), RAPIDJSON_UINT64_C2(0x8cbccc09, 0x6f5088cc), RAPIDJSON_UINT64_C2(0xd1b71758, 0xe219652c),
RAPIDJSON_UINT64_C2(0x9c400000, 0x00000000), RAPIDJSON_UINT64_C2(0xe8d4a510, 0x00000000), RAPIDJSON_UINT64_C2(0x9c400000, 0x00000000), RAPIDJSON_UINT64_C2(0xe8d4a510, 0x00000000),
RAPIDJSON_UINT64_C2(0xad78ebc5, 0xac620000), RAPIDJSON_UINT64_C2(0x813f3978, 0xf8940984), RAPIDJSON_UINT64_C2(0xad78ebc5, 0xac620000), RAPIDJSON_UINT64_C2(0x813f3978, 0xf8940984),
RAPIDJSON_UINT64_C2(0xc097ce7b, 0xc90715b3), RAPIDJSON_UINT64_C2(0x8f7e32ce, 0x7bea5c70), RAPIDJSON_UINT64_C2(0xc097ce7b, 0xc90715b3), RAPIDJSON_UINT64_C2(0x8f7e32ce, 0x7bea5c70),
RAPIDJSON_UINT64_C2(0xd5d238a4, 0xabe98068), RAPIDJSON_UINT64_C2(0x9f4f2726, 0x179a2245), RAPIDJSON_UINT64_C2(0xd5d238a4, 0xabe98068), RAPIDJSON_UINT64_C2(0x9f4f2726, 0x179a2245),
RAPIDJSON_UINT64_C2(0xed63a231, 0xd4c4fb27), RAPIDJSON_UINT64_C2(0xb0de6538, 0x8cc8ada8), RAPIDJSON_UINT64_C2(0xed63a231, 0xd4c4fb27), RAPIDJSON_UINT64_C2(0xb0de6538, 0x8cc8ada8),
RAPIDJSON_UINT64_C2(0x83c7088e, 0x1aab65db), RAPIDJSON_UINT64_C2(0xc45d1df9, 0x42711d9a), RAPIDJSON_UINT64_C2(0x83c7088e, 0x1aab65db), RAPIDJSON_UINT64_C2(0xc45d1df9, 0x42711d9a),
RAPIDJSON_UINT64_C2(0x924d692c, 0xa61be758), RAPIDJSON_UINT64_C2(0xda01ee64, 0x1a708dea), RAPIDJSON_UINT64_C2(0x924d692c, 0xa61be758), RAPIDJSON_UINT64_C2(0xda01ee64, 0x1a708dea),
RAPIDJSON_UINT64_C2(0xa26da399, 0x9aef774a), RAPIDJSON_UINT64_C2(0xf209787b, 0xb47d6b85), RAPIDJSON_UINT64_C2(0xa26da399, 0x9aef774a), RAPIDJSON_UINT64_C2(0xf209787b, 0xb47d6b85),
RAPIDJSON_UINT64_C2(0xb454e4a1, 0x79dd1877), RAPIDJSON_UINT64_C2(0x865b8692, 0x5b9bc5c2), RAPIDJSON_UINT64_C2(0xb454e4a1, 0x79dd1877), RAPIDJSON_UINT64_C2(0x865b8692, 0x5b9bc5c2),
RAPIDJSON_UINT64_C2(0xc83553c5, 0xc8965d3d), RAPIDJSON_UINT64_C2(0x952ab45c, 0xfa97a0b3), RAPIDJSON_UINT64_C2(0xc83553c5, 0xc8965d3d), RAPIDJSON_UINT64_C2(0x952ab45c, 0xfa97a0b3),
RAPIDJSON_UINT64_C2(0xde469fbd, 0x99a05fe3), RAPIDJSON_UINT64_C2(0xa59bc234, 0xdb398c25), RAPIDJSON_UINT64_C2(0xde469fbd, 0x99a05fe3), RAPIDJSON_UINT64_C2(0xa59bc234, 0xdb398c25),
RAPIDJSON_UINT64_C2(0xf6c69a72, 0xa3989f5c), RAPIDJSON_UINT64_C2(0xb7dcbf53, 0x54e9bece), RAPIDJSON_UINT64_C2(0xf6c69a72, 0xa3989f5c), RAPIDJSON_UINT64_C2(0xb7dcbf53, 0x54e9bece),
RAPIDJSON_UINT64_C2(0x88fcf317, 0xf22241e2), RAPIDJSON_UINT64_C2(0xcc20ce9b, 0xd35c78a5), RAPIDJSON_UINT64_C2(0x88fcf317, 0xf22241e2), RAPIDJSON_UINT64_C2(0xcc20ce9b, 0xd35c78a5),
RAPIDJSON_UINT64_C2(0x98165af3, 0x7b2153df), RAPIDJSON_UINT64_C2(0xe2a0b5dc, 0x971f303a), RAPIDJSON_UINT64_C2(0x98165af3, 0x7b2153df), RAPIDJSON_UINT64_C2(0xe2a0b5dc, 0x971f303a),
RAPIDJSON_UINT64_C2(0xa8d9d153, 0x5ce3b396), RAPIDJSON_UINT64_C2(0xfb9b7cd9, 0xa4a7443c), RAPIDJSON_UINT64_C2(0xa8d9d153, 0x5ce3b396), RAPIDJSON_UINT64_C2(0xfb9b7cd9, 0xa4a7443c),
RAPIDJSON_UINT64_C2(0xbb764c4c, 0xa7a44410), RAPIDJSON_UINT64_C2(0x8bab8eef, 0xb6409c1a), RAPIDJSON_UINT64_C2(0xbb764c4c, 0xa7a44410), RAPIDJSON_UINT64_C2(0x8bab8eef, 0xb6409c1a),
RAPIDJSON_UINT64_C2(0xd01fef10, 0xa657842c), RAPIDJSON_UINT64_C2(0x9b10a4e5, 0xe9913129), RAPIDJSON_UINT64_C2(0xd01fef10, 0xa657842c), RAPIDJSON_UINT64_C2(0x9b10a4e5, 0xe9913129),
RAPIDJSON_UINT64_C2(0xe7109bfb, 0xa19c0c9d), RAPIDJSON_UINT64_C2(0xac2820d9, 0x623bf429), RAPIDJSON_UINT64_C2(0xe7109bfb, 0xa19c0c9d), RAPIDJSON_UINT64_C2(0xac2820d9, 0x623bf429),
RAPIDJSON_UINT64_C2(0x80444b5e, 0x7aa7cf85), RAPIDJSON_UINT64_C2(0xbf21e440, 0x03acdd2d), RAPIDJSON_UINT64_C2(0x80444b5e, 0x7aa7cf85), RAPIDJSON_UINT64_C2(0xbf21e440, 0x03acdd2d),
RAPIDJSON_UINT64_C2(0x8e679c2f, 0x5e44ff8f), RAPIDJSON_UINT64_C2(0xd433179d, 0x9c8cb841), RAPIDJSON_UINT64_C2(0x8e679c2f, 0x5e44ff8f), RAPIDJSON_UINT64_C2(0xd433179d, 0x9c8cb841),
RAPIDJSON_UINT64_C2(0x9e19db92, 0xb4e31ba9), RAPIDJSON_UINT64_C2(0xeb96bf6e, 0xbadf77d9), RAPIDJSON_UINT64_C2(0x9e19db92, 0xb4e31ba9), RAPIDJSON_UINT64_C2(0xeb96bf6e, 0xbadf77d9),
RAPIDJSON_UINT64_C2(0xaf87023b, 0x9bf0ee6b) RAPIDJSON_UINT64_C2(0xaf87023b, 0x9bf0ee6b)
}; };
static const int16_t kCachedPowers_E[] = { static const int16_t kCachedPowers_E[] = {
-1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, -1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980,
-954, -927, -901, -874, -847, -821, -794, -768, -741, -715, -954, -927, -901, -874, -847, -821, -794, -768, -741, -715,
-688, -661, -635, -608, -582, -555, -529, -502, -475, -449, -688, -661, -635, -608, -582, -555, -529, -502, -475, -449,
-422, -396, -369, -343, -316, -289, -263, -236, -210, -183, -422, -396, -369, -343, -316, -289, -263, -236, -210, -183,
-157, -130, -103, -77, -50, -24, 3, 30, 56, 83, -157, -130, -103, -77, -50, -24, 3, 30, 56, 83,
109, 136, 162, 189, 216, 242, 269, 295, 322, 348, 109, 136, 162, 189, 216, 242, 269, 295, 322, 348,
375, 402, 428, 455, 481, 508, 534, 561, 588, 614, 375, 402, 428, 455, 481, 508, 534, 561, 588, 614,
641, 667, 694, 720, 747, 774, 800, 827, 853, 880, 641, 667, 694, 720, 747, 774, 800, 827, 853, 880,
907, 933, 960, 986, 1013, 1039, 1066 907, 933, 960, 986, 1013, 1039, 1066
}; };
//int k = static_cast<int>(ceil((-61 - e) * 0.30102999566398114)) + 374; //int k = static_cast<int>(ceil((-61 - e) * 0.30102999566398114)) + 374;
double dk = (-61 - e) * 0.30102999566398114 + 347; // dk must be positive, so can do ceiling in positive double dk = (-61 - e) * 0.30102999566398114 + 347; // dk must be positive, so can do ceiling in positive
int k = static_cast<int>(dk); int k = static_cast<int>(dk);
if (k != dk) if (k != dk)
k++; k++;
unsigned index = static_cast<unsigned>((k >> 3) + 1); unsigned index = static_cast<unsigned>((k >> 3) + 1);
*K = -(-348 + static_cast<int>(index << 3)); // decimal exponent no need lookup table *K = -(-348 + static_cast<int>(index << 3)); // decimal exponent no need lookup table
return DiyFp(kCachedPowers_F[index], kCachedPowers_E[index]); return DiyFp(kCachedPowers_F[index], kCachedPowers_E[index]);
} }
inline void GrisuRound(char* buffer, int len, uint64_t delta, uint64_t rest, uint64_t ten_kappa, uint64_t wp_w) { inline void GrisuRound(char* buffer, int len, uint64_t delta, uint64_t rest, uint64_t ten_kappa, uint64_t wp_w) {
while (rest < wp_w && delta - rest >= ten_kappa && while (rest < wp_w && delta - rest >= ten_kappa &&
(rest + ten_kappa < wp_w || /// closer (rest + ten_kappa < wp_w || /// closer
wp_w - rest > rest + ten_kappa - wp_w)) { wp_w - rest > rest + ten_kappa - wp_w)) {
buffer[len - 1]--; buffer[len - 1]--;
rest += ten_kappa; rest += ten_kappa;
} }
} }
inline unsigned CountDecimalDigit32(uint32_t n) { inline unsigned CountDecimalDigit32(uint32_t n) {
// Simple pure C++ implementation was faster than __builtin_clz version in this situation. // Simple pure C++ implementation was faster than __builtin_clz version in this situation.
if (n < 10) return 1; if (n < 10) return 1;
if (n < 100) return 2; if (n < 100) return 2;
if (n < 1000) return 3; if (n < 1000) return 3;
if (n < 10000) return 4; if (n < 10000) return 4;
if (n < 100000) return 5; if (n < 100000) return 5;
if (n < 1000000) return 6; if (n < 1000000) return 6;
if (n < 10000000) return 7; if (n < 10000000) return 7;
if (n < 100000000) return 8; if (n < 100000000) return 8;
if (n < 1000000000) return 9; if (n < 1000000000) return 9;
return 10; return 10;
} }
inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buffer, int* len, int* K) { inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buffer, int* len, int* K) {
static const uint32_t kPow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; static const uint32_t kPow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 };
const DiyFp one(uint64_t(1) << -Mp.e, Mp.e); const DiyFp one(uint64_t(1) << -Mp.e, Mp.e);
const DiyFp wp_w = Mp - W; const DiyFp wp_w = Mp - W;
uint32_t p1 = static_cast<uint32_t>(Mp.f >> -one.e); uint32_t p1 = static_cast<uint32_t>(Mp.f >> -one.e);
uint64_t p2 = Mp.f & (one.f - 1); uint64_t p2 = Mp.f & (one.f - 1);
int kappa = CountDecimalDigit32(p1); int kappa = CountDecimalDigit32(p1);
*len = 0; *len = 0;
while (kappa > 0) { while (kappa > 0) {
uint32_t d; uint32_t d;
switch (kappa) { switch (kappa) {
case 10: d = p1 / 1000000000; p1 %= 1000000000; break; case 10: d = p1 / 1000000000; p1 %= 1000000000; break;
case 9: d = p1 / 100000000; p1 %= 100000000; break; case 9: d = p1 / 100000000; p1 %= 100000000; break;
case 8: d = p1 / 10000000; p1 %= 10000000; break; case 8: d = p1 / 10000000; p1 %= 10000000; break;
case 7: d = p1 / 1000000; p1 %= 1000000; break; case 7: d = p1 / 1000000; p1 %= 1000000; break;
case 6: d = p1 / 100000; p1 %= 100000; break; case 6: d = p1 / 100000; p1 %= 100000; break;
case 5: d = p1 / 10000; p1 %= 10000; break; case 5: d = p1 / 10000; p1 %= 10000; break;
case 4: d = p1 / 1000; p1 %= 1000; break; case 4: d = p1 / 1000; p1 %= 1000; break;
case 3: d = p1 / 100; p1 %= 100; break; case 3: d = p1 / 100; p1 %= 100; break;
case 2: d = p1 / 10; p1 %= 10; break; case 2: d = p1 / 10; p1 %= 10; break;
case 1: d = p1; p1 = 0; break; case 1: d = p1; p1 = 0; break;
default: default:
#if defined(_MSC_VER) #if defined(_MSC_VER)
__assume(0); __assume(0);
#elif __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) #elif __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
__builtin_unreachable(); __builtin_unreachable();
#else #else
d = 0; d = 0;
#endif #endif
} }
if (d || *len) if (d || *len)
buffer[(*len)++] = '0' + static_cast<char>(d); buffer[(*len)++] = '0' + static_cast<char>(d);
kappa--; kappa--;
uint64_t tmp = (static_cast<uint64_t>(p1) << -one.e) + p2; uint64_t tmp = (static_cast<uint64_t>(p1) << -one.e) + p2;
if (tmp <= delta) { if (tmp <= delta) {
*K += kappa; *K += kappa;
GrisuRound(buffer, *len, delta, tmp, static_cast<uint64_t>(kPow10[kappa]) << -one.e, wp_w.f); GrisuRound(buffer, *len, delta, tmp, static_cast<uint64_t>(kPow10[kappa]) << -one.e, wp_w.f);
return; return;
} }
} }
// kappa = 0 // kappa = 0
for (;;) { for (;;) {
p2 *= 10; p2 *= 10;
delta *= 10; delta *= 10;
char d = static_cast<char>(p2 >> -one.e); char d = static_cast<char>(p2 >> -one.e);
if (d || *len) if (d || *len)
buffer[(*len)++] = '0' + d; buffer[(*len)++] = '0' + d;
p2 &= one.f - 1; p2 &= one.f - 1;
kappa--; kappa--;
if (p2 < delta) { if (p2 < delta) {
*K += kappa; *K += kappa;
GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * kPow10[-kappa]); GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * kPow10[-kappa]);
return; return;
} }
} }
} }
inline void Grisu2(double value, char* buffer, int* length, int* K) { inline void Grisu2(double value, char* buffer, int* length, int* K) {
const DiyFp v(value); const DiyFp v(value);
DiyFp w_m, w_p; DiyFp w_m, w_p;
v.NormalizedBoundaries(&w_m, &w_p); v.NormalizedBoundaries(&w_m, &w_p);
const DiyFp c_mk = GetCachedPower(w_p.e, K); const DiyFp c_mk = GetCachedPower(w_p.e, K);
const DiyFp W = v.Normalize() * c_mk; const DiyFp W = v.Normalize() * c_mk;
DiyFp Wp = w_p * c_mk; DiyFp Wp = w_p * c_mk;
DiyFp Wm = w_m * c_mk; DiyFp Wm = w_m * c_mk;
Wm.f++; Wm.f++;
Wp.f--; Wp.f--;
DigitGen(W, Wp, Wp.f - Wm.f, buffer, length, K); DigitGen(W, Wp, Wp.f - Wm.f, buffer, length, K);
} }
//inline const char* GetDigitsLut() {
// static const char cDigitsLut[200] = {
// '0', '0', '0', '1', '0', '2', '0', '3', '0', '4', '0', '5', '0', '6', '0', '7', '0', '8', '0', '9',
// '1', '0', '1', '1', '1', '2', '1', '3', '1', '4', '1', '5', '1', '6', '1', '7', '1', '8', '1', '9',
// '2', '0', '2', '1', '2', '2', '2', '3', '2', '4', '2', '5', '2', '6', '2', '7', '2', '8', '2', '9',
// '3', '0', '3', '1', '3', '2', '3', '3', '3', '4', '3', '5', '3', '6', '3', '7', '3', '8', '3', '9',
// '4', '0', '4', '1', '4', '2', '4', '3', '4', '4', '4', '5', '4', '6', '4', '7', '4', '8', '4', '9',
// '5', '0', '5', '1', '5', '2', '5', '3', '5', '4', '5', '5', '5', '6', '5', '7', '5', '8', '5', '9',
// '6', '0', '6', '1', '6', '2', '6', '3', '6', '4', '6', '5', '6', '6', '6', '7', '6', '8', '6', '9',
// '7', '0', '7', '1', '7', '2', '7', '3', '7', '4', '7', '5', '7', '6', '7', '7', '7', '8', '7', '9',
// '8', '0', '8', '1', '8', '2', '8', '3', '8', '4', '8', '5', '8', '6', '8', '7', '8', '8', '8', '9',
// '9', '0', '9', '1', '9', '2', '9', '3', '9', '4', '9', '5', '9', '6', '9', '7', '9', '8', '9', '9'
// };
// return cDigitsLut;
//}
inline char* WriteExponent(int K, char* buffer) { inline char* WriteExponent(int K, char* buffer) {
if (K < 0) { if (K < 0) {
*buffer++ = '-'; *buffer++ = '-';
K = -K; K = -K;
} }
if (K >= 100) { if (K >= 100) {
*buffer++ = '0' + static_cast<char>(K / 100); *buffer++ = '0' + static_cast<char>(K / 100);
K %= 100; K %= 100;
const char* d = GetDigitsLut() + K * 2; const char* d = GetDigitsLut() + K * 2;
*buffer++ = d[0]; *buffer++ = d[0];
*buffer++ = d[1]; *buffer++ = d[1];
} }
else if (K >= 10) { else if (K >= 10) {
const char* d = GetDigitsLut() + K * 2; const char* d = GetDigitsLut() + K * 2;
*buffer++ = d[0]; *buffer++ = d[0];
*buffer++ = d[1]; *buffer++ = d[1];
} }
else else
*buffer++ = '0' + static_cast<char>(K); *buffer++ = '0' + static_cast<char>(K);
return buffer; return buffer;
} }
inline char* Prettify(char* buffer, int length, int k) { inline char* Prettify(char* buffer, int length, int k) {
const int kk = length + k; // 10^(kk-1) <= v < 10^kk const int kk = length + k; // 10^(kk-1) <= v < 10^kk
if (length <= kk && kk <= 21) { if (length <= kk && kk <= 21) {
// 1234e7 -> 12340000000 // 1234e7 -> 12340000000
for (int i = length; i < kk; i++) for (int i = length; i < kk; i++)
buffer[i] = '0'; buffer[i] = '0';
buffer[kk] = '.'; buffer[kk] = '.';
buffer[kk + 1] = '0'; buffer[kk + 1] = '0';
return &buffer[kk + 2]; return &buffer[kk + 2];
} }
else if (0 < kk && kk <= 21) { else if (0 < kk && kk <= 21) {
// 1234e-2 -> 12.34 // 1234e-2 -> 12.34
memmove(&buffer[kk + 1], &buffer[kk], length - kk); memmove(&buffer[kk + 1], &buffer[kk], length - kk);
buffer[kk] = '.'; buffer[kk] = '.';
return &buffer[length + 1]; return &buffer[length + 1];
} }
else if (-6 < kk && kk <= 0) { else if (-6 < kk && kk <= 0) {
// 1234e-6 -> 0.001234 // 1234e-6 -> 0.001234
const int offset = 2 - kk; const int offset = 2 - kk;
memmove(&buffer[offset], &buffer[0], length); memmove(&buffer[offset], &buffer[0], length);
buffer[0] = '0'; buffer[0] = '0';
buffer[1] = '.'; buffer[1] = '.';
for (int i = 2; i < offset; i++) for (int i = 2; i < offset; i++)
buffer[i] = '0'; buffer[i] = '0';
return &buffer[length + offset]; return &buffer[length + offset];
} }
else if (length == 1) { else if (length == 1) {
// 1e30 // 1e30
buffer[1] = 'e'; buffer[1] = 'e';
return WriteExponent(kk - 1, &buffer[2]); return WriteExponent(kk - 1, &buffer[2]);
} }
else { else {
// 1234e30 -> 1.234e33 // 1234e30 -> 1.234e33
memmove(&buffer[2], &buffer[1], length - 1); memmove(&buffer[2], &buffer[1], length - 1);
buffer[1] = '.'; buffer[1] = '.';
buffer[length + 1] = 'e'; buffer[length + 1] = 'e';
return WriteExponent(kk - 1, &buffer[0 + length + 2]); return WriteExponent(kk - 1, &buffer[0 + length + 2]);
} }
} }
inline char* dtoa(double value, char* buffer) { inline char* dtoa(double value, char* buffer) {
if (value == 0) { if (value == 0) {
buffer[0] = '0'; buffer[0] = '0';
buffer[1] = '.'; buffer[1] = '.';
buffer[2] = '0'; buffer[2] = '0';
return &buffer[3]; return &buffer[3];
} }
else { else {
if (value < 0) { if (value < 0) {
*buffer++ = '-'; *buffer++ = '-';
value = -value; value = -value;
} }
int length, K; int length, K;
Grisu2(value, buffer, &length, &K); Grisu2(value, buffer, &length, &K);
return Prettify(buffer, length, K); return Prettify(buffer, length, K);
} }
} }
#ifdef __GNUC__ #ifdef __GNUC__

View File

@ -1,12 +1,29 @@
// Copyright (C) 2011 Milo Yip
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef RAPIDJSON_ITOA_ #ifndef RAPIDJSON_ITOA_
#define RAPIDJSON_ITOA_ #define RAPIDJSON_ITOA_
namespace rapidjson { namespace rapidjson {
namespace internal { namespace internal {
// Modified from https://github.com/miloyip/itoa-benchmark/blob/master/src/branchlut.cpp
// API is changed to return the character passed the end of string, without writing '\0'
inline const char* GetDigitsLut() { inline const char* GetDigitsLut() {
static const char cDigitsLut[200] = { static const char cDigitsLut[200] = {
'0','0','0','1','0','2','0','3','0','4','0','5','0','6','0','7','0','8','0','9', '0','0','0','1','0','2','0','3','0','4','0','5','0','6','0','7','0','8','0','9',
@ -98,12 +115,12 @@ inline char* u32toa(uint32_t value, char* buffer) {
} }
inline char* i32toa(int32_t value, char* buffer) { inline char* i32toa(int32_t value, char* buffer) {
if (value < 0) { if (value < 0) {
*buffer++ = '-'; *buffer++ = '-';
value = -value; value = -value;
} }
return u32toa(static_cast<uint32_t>(value), buffer); return u32toa(static_cast<uint32_t>(value), buffer);
} }
inline char* u64toa(uint64_t value, char* buffer) { inline char* u64toa(uint64_t value, char* buffer) {
@ -271,16 +288,16 @@ inline char* u64toa(uint64_t value, char* buffer) {
*buffer++ = cDigitsLut[d8 + 1]; *buffer++ = cDigitsLut[d8 + 1];
} }
return buffer; return buffer;
} }
inline char* i64toa(int64_t value, char* buffer) { inline char* i64toa(int64_t value, char* buffer) {
if (value < 0) { if (value < 0) {
*buffer++ = '-'; *buffer++ = '-';
value = -value; value = -value;
} }
return u64toa(static_cast<uint64_t>(value), buffer); return u64toa(static_cast<uint64_t>(value), buffer);
} }
} // namespace internal } // namespace internal

View File

@ -1,3 +1,23 @@
// Copyright (C) 2011 Milo Yip
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef RAPIDJSON_INTERNAL_META_H_ #ifndef RAPIDJSON_INTERNAL_META_H_
#define RAPIDJSON_INTERNAL_META_H_ #define RAPIDJSON_INTERNAL_META_H_
@ -35,10 +55,10 @@ template <typename T> struct IsPointer<T*> : TrueType {};
template <typename CT, typename T> template <typename CT, typename T>
struct IsMoreConst { struct IsMoreConst {
enum { Value = enum { Value =
( IsSame< typename RemoveConst<CT>::Type, typename RemoveConst<T>::Type>::Value ( IsSame< typename RemoveConst<CT>::Type, typename RemoveConst<T>::Type>::Value
&& ( IsConst<CT>::Value >= IsConst<T>::Value ) ) && ( IsConst<CT>::Value >= IsConst<T>::Value ) )
}; };
}; };
template <bool Condition, typename T = void> struct EnableIfCond; template <bool Condition, typename T = void> struct EnableIfCond;
@ -60,15 +80,15 @@ template <typename T> struct RemoveSfinaeFptr {};
template <typename T> struct RemoveSfinaeFptr<SfinaeResultTag&(*)(T)> { typedef T Type; }; template <typename T> struct RemoveSfinaeFptr<SfinaeResultTag&(*)(T)> { typedef T Type; };
#define RAPIDJSON_REMOVEFPTR_(type) \ #define RAPIDJSON_REMOVEFPTR_(type) \
typename ::rapidjson::internal::RemoveSfinaeFptr \ typename ::rapidjson::internal::RemoveSfinaeFptr \
< ::rapidjson::internal::SfinaeResultTag&(*) type>::Type < ::rapidjson::internal::SfinaeResultTag&(*) type>::Type
#define RAPIDJSON_ENABLEIF(cond) \ #define RAPIDJSON_ENABLEIF(cond) \
typename ::rapidjson::internal::EnableIf \ typename ::rapidjson::internal::EnableIf \
<RAPIDJSON_REMOVEFPTR_(cond)>::Type * = NULL <RAPIDJSON_REMOVEFPTR_(cond)>::Type * = NULL
#define RAPIDJSON_DISABLEIF_RETURN(cond,returntype) \ #define RAPIDJSON_DISABLEIF_RETURN(cond,returntype) \
typename ::rapidjson::internal::DisableIf<cond,returntype>::Type typename ::rapidjson::internal::DisableIf<cond,returntype>::Type
} // namespace internal } // namespace internal
} // namespace rapidjson } // namespace rapidjson

View File

@ -1,3 +1,23 @@
// Copyright (C) 2011 Milo Yip
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef RAPIDJSON_POW10_ #ifndef RAPIDJSON_POW10_
#define RAPIDJSON_POW10_ #define RAPIDJSON_POW10_
@ -6,31 +26,31 @@ namespace internal {
//! Computes integer powers of 10 in double (10.0^n). //! Computes integer powers of 10 in double (10.0^n).
/*! This function uses lookup table for fast and accurate results. /*! This function uses lookup table for fast and accurate results.
\param n non-negative exponent. Must <= 308. \param n non-negative exponent. Must <= 308.
\return 10.0^n \return 10.0^n
*/ */
inline double Pow10(int n) { inline double Pow10(int n) {
static const double e[] = { // 1e-0...1e308: 309 * 8 bytes = 2472 bytes static const double e[] = { // 1e-0...1e308: 309 * 8 bytes = 2472 bytes
1e+0, 1e+0,
1e+1, 1e+2, 1e+3, 1e+4, 1e+5, 1e+6, 1e+7, 1e+8, 1e+9, 1e+10, 1e+11, 1e+12, 1e+13, 1e+14, 1e+15, 1e+16, 1e+17, 1e+18, 1e+19, 1e+20, 1e+1, 1e+2, 1e+3, 1e+4, 1e+5, 1e+6, 1e+7, 1e+8, 1e+9, 1e+10, 1e+11, 1e+12, 1e+13, 1e+14, 1e+15, 1e+16, 1e+17, 1e+18, 1e+19, 1e+20,
1e+21, 1e+22, 1e+23, 1e+24, 1e+25, 1e+26, 1e+27, 1e+28, 1e+29, 1e+30, 1e+31, 1e+32, 1e+33, 1e+34, 1e+35, 1e+36, 1e+37, 1e+38, 1e+39, 1e+40, 1e+21, 1e+22, 1e+23, 1e+24, 1e+25, 1e+26, 1e+27, 1e+28, 1e+29, 1e+30, 1e+31, 1e+32, 1e+33, 1e+34, 1e+35, 1e+36, 1e+37, 1e+38, 1e+39, 1e+40,
1e+41, 1e+42, 1e+43, 1e+44, 1e+45, 1e+46, 1e+47, 1e+48, 1e+49, 1e+50, 1e+51, 1e+52, 1e+53, 1e+54, 1e+55, 1e+56, 1e+57, 1e+58, 1e+59, 1e+60, 1e+41, 1e+42, 1e+43, 1e+44, 1e+45, 1e+46, 1e+47, 1e+48, 1e+49, 1e+50, 1e+51, 1e+52, 1e+53, 1e+54, 1e+55, 1e+56, 1e+57, 1e+58, 1e+59, 1e+60,
1e+61, 1e+62, 1e+63, 1e+64, 1e+65, 1e+66, 1e+67, 1e+68, 1e+69, 1e+70, 1e+71, 1e+72, 1e+73, 1e+74, 1e+75, 1e+76, 1e+77, 1e+78, 1e+79, 1e+80, 1e+61, 1e+62, 1e+63, 1e+64, 1e+65, 1e+66, 1e+67, 1e+68, 1e+69, 1e+70, 1e+71, 1e+72, 1e+73, 1e+74, 1e+75, 1e+76, 1e+77, 1e+78, 1e+79, 1e+80,
1e+81, 1e+82, 1e+83, 1e+84, 1e+85, 1e+86, 1e+87, 1e+88, 1e+89, 1e+90, 1e+91, 1e+92, 1e+93, 1e+94, 1e+95, 1e+96, 1e+97, 1e+98, 1e+99, 1e+100, 1e+81, 1e+82, 1e+83, 1e+84, 1e+85, 1e+86, 1e+87, 1e+88, 1e+89, 1e+90, 1e+91, 1e+92, 1e+93, 1e+94, 1e+95, 1e+96, 1e+97, 1e+98, 1e+99, 1e+100,
1e+101,1e+102,1e+103,1e+104,1e+105,1e+106,1e+107,1e+108,1e+109,1e+110,1e+111,1e+112,1e+113,1e+114,1e+115,1e+116,1e+117,1e+118,1e+119,1e+120, 1e+101,1e+102,1e+103,1e+104,1e+105,1e+106,1e+107,1e+108,1e+109,1e+110,1e+111,1e+112,1e+113,1e+114,1e+115,1e+116,1e+117,1e+118,1e+119,1e+120,
1e+121,1e+122,1e+123,1e+124,1e+125,1e+126,1e+127,1e+128,1e+129,1e+130,1e+131,1e+132,1e+133,1e+134,1e+135,1e+136,1e+137,1e+138,1e+139,1e+140, 1e+121,1e+122,1e+123,1e+124,1e+125,1e+126,1e+127,1e+128,1e+129,1e+130,1e+131,1e+132,1e+133,1e+134,1e+135,1e+136,1e+137,1e+138,1e+139,1e+140,
1e+141,1e+142,1e+143,1e+144,1e+145,1e+146,1e+147,1e+148,1e+149,1e+150,1e+151,1e+152,1e+153,1e+154,1e+155,1e+156,1e+157,1e+158,1e+159,1e+160, 1e+141,1e+142,1e+143,1e+144,1e+145,1e+146,1e+147,1e+148,1e+149,1e+150,1e+151,1e+152,1e+153,1e+154,1e+155,1e+156,1e+157,1e+158,1e+159,1e+160,
1e+161,1e+162,1e+163,1e+164,1e+165,1e+166,1e+167,1e+168,1e+169,1e+170,1e+171,1e+172,1e+173,1e+174,1e+175,1e+176,1e+177,1e+178,1e+179,1e+180, 1e+161,1e+162,1e+163,1e+164,1e+165,1e+166,1e+167,1e+168,1e+169,1e+170,1e+171,1e+172,1e+173,1e+174,1e+175,1e+176,1e+177,1e+178,1e+179,1e+180,
1e+181,1e+182,1e+183,1e+184,1e+185,1e+186,1e+187,1e+188,1e+189,1e+190,1e+191,1e+192,1e+193,1e+194,1e+195,1e+196,1e+197,1e+198,1e+199,1e+200, 1e+181,1e+182,1e+183,1e+184,1e+185,1e+186,1e+187,1e+188,1e+189,1e+190,1e+191,1e+192,1e+193,1e+194,1e+195,1e+196,1e+197,1e+198,1e+199,1e+200,
1e+201,1e+202,1e+203,1e+204,1e+205,1e+206,1e+207,1e+208,1e+209,1e+210,1e+211,1e+212,1e+213,1e+214,1e+215,1e+216,1e+217,1e+218,1e+219,1e+220, 1e+201,1e+202,1e+203,1e+204,1e+205,1e+206,1e+207,1e+208,1e+209,1e+210,1e+211,1e+212,1e+213,1e+214,1e+215,1e+216,1e+217,1e+218,1e+219,1e+220,
1e+221,1e+222,1e+223,1e+224,1e+225,1e+226,1e+227,1e+228,1e+229,1e+230,1e+231,1e+232,1e+233,1e+234,1e+235,1e+236,1e+237,1e+238,1e+239,1e+240, 1e+221,1e+222,1e+223,1e+224,1e+225,1e+226,1e+227,1e+228,1e+229,1e+230,1e+231,1e+232,1e+233,1e+234,1e+235,1e+236,1e+237,1e+238,1e+239,1e+240,
1e+241,1e+242,1e+243,1e+244,1e+245,1e+246,1e+247,1e+248,1e+249,1e+250,1e+251,1e+252,1e+253,1e+254,1e+255,1e+256,1e+257,1e+258,1e+259,1e+260, 1e+241,1e+242,1e+243,1e+244,1e+245,1e+246,1e+247,1e+248,1e+249,1e+250,1e+251,1e+252,1e+253,1e+254,1e+255,1e+256,1e+257,1e+258,1e+259,1e+260,
1e+261,1e+262,1e+263,1e+264,1e+265,1e+266,1e+267,1e+268,1e+269,1e+270,1e+271,1e+272,1e+273,1e+274,1e+275,1e+276,1e+277,1e+278,1e+279,1e+280, 1e+261,1e+262,1e+263,1e+264,1e+265,1e+266,1e+267,1e+268,1e+269,1e+270,1e+271,1e+272,1e+273,1e+274,1e+275,1e+276,1e+277,1e+278,1e+279,1e+280,
1e+281,1e+282,1e+283,1e+284,1e+285,1e+286,1e+287,1e+288,1e+289,1e+290,1e+291,1e+292,1e+293,1e+294,1e+295,1e+296,1e+297,1e+298,1e+299,1e+300, 1e+281,1e+282,1e+283,1e+284,1e+285,1e+286,1e+287,1e+288,1e+289,1e+290,1e+291,1e+292,1e+293,1e+294,1e+295,1e+296,1e+297,1e+298,1e+299,1e+300,
1e+301,1e+302,1e+303,1e+304,1e+305,1e+306,1e+307,1e+308 1e+301,1e+302,1e+303,1e+304,1e+305,1e+306,1e+307,1e+308
}; };
RAPIDJSON_ASSERT(n >= 0 && n <= 308); RAPIDJSON_ASSERT(n >= 0 && n <= 308);
return e[n]; return e[n];
} }
} // namespace internal } // namespace internal

View File

@ -1,3 +1,23 @@
// Copyright (C) 2011 Milo Yip
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef RAPIDJSON_INTERNAL_STACK_H_ #ifndef RAPIDJSON_INTERNAL_STACK_H_
#define RAPIDJSON_INTERNAL_STACK_H_ #define RAPIDJSON_INTERNAL_STACK_H_
@ -13,79 +33,79 @@ namespace internal {
template <typename Allocator> template <typename Allocator>
class Stack { class Stack {
public: public:
Stack(Allocator* allocator, size_t stack_capacity) : allocator_(allocator), own_allocator_(0), stack_(0), stack_top_(0), stack_end_(0), stack_capacity_(stack_capacity) { Stack(Allocator* allocator, size_t stack_capacity) : allocator_(allocator), own_allocator_(0), stack_(0), stack_top_(0), stack_end_(0), stack_capacity_(stack_capacity) {
RAPIDJSON_ASSERT(stack_capacity_ > 0); RAPIDJSON_ASSERT(stack_capacity_ > 0);
if (!allocator_) if (!allocator_)
own_allocator_ = allocator_ = new Allocator(); own_allocator_ = allocator_ = new Allocator();
stack_top_ = stack_ = (char*)allocator_->Malloc(stack_capacity_); stack_top_ = stack_ = (char*)allocator_->Malloc(stack_capacity_);
stack_end_ = stack_ + stack_capacity_; stack_end_ = stack_ + stack_capacity_;
} }
~Stack() { ~Stack() {
Allocator::Free(stack_); Allocator::Free(stack_);
delete own_allocator_; // Only delete if it is owned by the stack delete own_allocator_; // Only delete if it is owned by the stack
} }
void Clear() { /*stack_top_ = 0;*/ stack_top_ = stack_; } void Clear() { /*stack_top_ = 0;*/ stack_top_ = stack_; }
// Optimization note: try to minimize the size of this function for force inline. // Optimization note: try to minimize the size of this function for force inline.
// Expansion is run very infrequently, so it is moved to another (probably non-inline) function. // Expansion is run very infrequently, so it is moved to another (probably non-inline) function.
template<typename T> template<typename T>
RAPIDJSON_FORCEINLINE T* Push(size_t count = 1) { RAPIDJSON_FORCEINLINE T* Push(size_t count = 1) {
// Expand the stack if needed // Expand the stack if needed
if (stack_top_ + sizeof(T) * count >= stack_end_) if (stack_top_ + sizeof(T) * count >= stack_end_)
Expand<T>(count); Expand<T>(count);
T* ret = reinterpret_cast<T*>(stack_top_); T* ret = reinterpret_cast<T*>(stack_top_);
stack_top_ += sizeof(T) * count; stack_top_ += sizeof(T) * count;
return ret; return ret;
} }
template<typename T> template<typename T>
T* Pop(size_t count) { T* Pop(size_t count) {
RAPIDJSON_ASSERT(GetSize() >= count * sizeof(T)); RAPIDJSON_ASSERT(GetSize() >= count * sizeof(T));
stack_top_ -= count * sizeof(T); stack_top_ -= count * sizeof(T);
return reinterpret_cast<T*>(stack_top_); return reinterpret_cast<T*>(stack_top_);
} }
template<typename T> template<typename T>
T* Top() { T* Top() {
RAPIDJSON_ASSERT(GetSize() >= sizeof(T)); RAPIDJSON_ASSERT(GetSize() >= sizeof(T));
return reinterpret_cast<T*>(stack_top_ - sizeof(T)); return reinterpret_cast<T*>(stack_top_ - sizeof(T));
} }
template<typename T> template<typename T>
T* Bottom() { return (T*)stack_; } T* Bottom() { return (T*)stack_; }
Allocator& GetAllocator() { return *allocator_; } Allocator& GetAllocator() { return *allocator_; }
bool Empty() const { return stack_top_ == stack_; } bool Empty() const { return stack_top_ == stack_; }
size_t GetSize() const { return static_cast<size_t>(stack_top_ - stack_); } size_t GetSize() const { return static_cast<size_t>(stack_top_ - stack_); }
size_t GetCapacity() const { return stack_capacity_; } size_t GetCapacity() const { return stack_capacity_; }
private: private:
template<typename T> template<typename T>
void Expand(size_t count) { void Expand(size_t count) {
size_t new_capacity = stack_capacity_ * 2; size_t new_capacity = stack_capacity_ * 2;
size_t size = GetSize(); size_t size = GetSize();
size_t new_size = GetSize() + sizeof(T) * count; size_t new_size = GetSize() + sizeof(T) * count;
if (new_capacity < new_size) if (new_capacity < new_size)
new_capacity = new_size; new_capacity = new_size;
stack_ = (char*)allocator_->Realloc(stack_, stack_capacity_, new_capacity); stack_ = (char*)allocator_->Realloc(stack_, stack_capacity_, new_capacity);
stack_capacity_ = new_capacity; stack_capacity_ = new_capacity;
stack_top_ = stack_ + size; stack_top_ = stack_ + size;
stack_end_ = stack_ + stack_capacity_; stack_end_ = stack_ + stack_capacity_;
} }
// Prohibit copy constructor & assignment operator. // Prohibit copy constructor & assignment operator.
Stack(const Stack&); Stack(const Stack&);
Stack& operator=(const Stack&); Stack& operator=(const Stack&);
Allocator* allocator_; Allocator* allocator_;
Allocator* own_allocator_; Allocator* own_allocator_;
char *stack_; char *stack_;
char *stack_top_; char *stack_top_;
char *stack_end_; char *stack_end_;
size_t stack_capacity_; size_t stack_capacity_;
}; };
} // namespace internal } // namespace internal

View File

@ -1,3 +1,23 @@
// Copyright (C) 2011 Milo Yip
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef RAPIDJSON_INTERNAL_STRFUNC_H_ #ifndef RAPIDJSON_INTERNAL_STRFUNC_H_
#define RAPIDJSON_INTERNAL_STRFUNC_H_ #define RAPIDJSON_INTERNAL_STRFUNC_H_
@ -5,16 +25,16 @@ namespace rapidjson {
namespace internal { namespace internal {
//! Custom strlen() which works on different character types. //! Custom strlen() which works on different character types.
/*! \tparam Ch Character type (e.g. char, wchar_t, short) /*! \tparam Ch Character type (e.g. char, wchar_t, short)
\param s Null-terminated input string. \param s Null-terminated input string.
\return Number of characters in the string. \return Number of characters in the string.
\note This has the same semantics as strlen(), the return value is not number of Unicode codepoints. \note This has the same semantics as strlen(), the return value is not number of Unicode codepoints.
*/ */
template <typename Ch> template <typename Ch>
inline SizeType StrLen(const Ch* s) { inline SizeType StrLen(const Ch* s) {
const Ch* p = s; const Ch* p = s;
while (*p) ++p; while (*p) ++p;
return SizeType(p - s); return SizeType(p - s);
} }
} // namespace internal } // namespace internal

View File

@ -1,3 +1,23 @@
// Copyright (C) 2011 Milo Yip
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef RAPIDJSON_MEMORYBUFFER_H_ #ifndef RAPIDJSON_MEMORYBUFFER_H_
#define RAPIDJSON_MEMORYBUFFER_H_ #define RAPIDJSON_MEMORYBUFFER_H_
@ -8,38 +28,38 @@ namespace rapidjson {
//! Represents an in-memory output byte stream. //! Represents an in-memory output byte stream.
/*! /*!
This class is mainly for being wrapped by EncodedOutputStream or AutoUTFOutputStream. This class is mainly for being wrapped by EncodedOutputStream or AutoUTFOutputStream.
It is similar to FileWriteBuffer but the destination is an in-memory buffer instead of a file. It is similar to FileWriteBuffer but the destination is an in-memory buffer instead of a file.
Differences between MemoryBuffer and StringBuffer: Differences between MemoryBuffer and StringBuffer:
1. StringBuffer has Encoding but MemoryBuffer is only a byte buffer. 1. StringBuffer has Encoding but MemoryBuffer is only a byte buffer.
2. StringBuffer::GetString() returns a null-terminated string. MemoryBuffer::GetBuffer() returns a buffer without terminator. 2. StringBuffer::GetString() returns a null-terminated string. MemoryBuffer::GetBuffer() returns a buffer without terminator.
\tparam Allocator type for allocating memory buffer. \tparam Allocator type for allocating memory buffer.
\note implements Stream concept \note implements Stream concept
*/ */
template <typename Allocator = CrtAllocator> template <typename Allocator = CrtAllocator>
struct GenericMemoryBuffer { struct GenericMemoryBuffer {
typedef char Ch; // byte typedef char Ch; // byte
GenericMemoryBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {} GenericMemoryBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {}
void Put(Ch c) { *stack_.template Push<Ch>() = c; } void Put(Ch c) { *stack_.template Push<Ch>() = c; }
void Flush() {} void Flush() {}
void Clear() { stack_.Clear(); } void Clear() { stack_.Clear(); }
Ch* Push(size_t count) { return stack_.template Push<Ch>(count); } Ch* Push(size_t count) { return stack_.template Push<Ch>(count); }
void Pop(size_t count) { stack_.template Pop<Ch>(count); } void Pop(size_t count) { stack_.template Pop<Ch>(count); }
const Ch* GetBuffer() const { const Ch* GetBuffer() const {
return stack_.template Bottom<Ch>(); return stack_.template Bottom<Ch>();
} }
size_t GetSize() const { return stack_.GetSize(); } size_t GetSize() const { return stack_.GetSize(); }
static const size_t kDefaultCapacity = 256; static const size_t kDefaultCapacity = 256;
mutable internal::Stack<Allocator> stack_; mutable internal::Stack<Allocator> stack_;
}; };
typedef GenericMemoryBuffer<> MemoryBuffer; typedef GenericMemoryBuffer<> MemoryBuffer;
@ -47,7 +67,7 @@ typedef GenericMemoryBuffer<> MemoryBuffer;
//! Implement specialized version of PutN() with memset() for better performance. //! Implement specialized version of PutN() with memset() for better performance.
template<> template<>
inline void PutN(MemoryBuffer& memoryBuffer, char c, size_t n) { inline void PutN(MemoryBuffer& memoryBuffer, char c, size_t n) {
memset(memoryBuffer.stack_.Push<char>(n), c, n * sizeof(c)); memset(memoryBuffer.stack_.Push<char>(n), c, n * sizeof(c));
} }
} // namespace rapidjson } // namespace rapidjson

View File

@ -1,3 +1,23 @@
// Copyright (C) 2011 Milo Yip
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef RAPIDJSON_MEMORYSTREAM_H_ #ifndef RAPIDJSON_MEMORYSTREAM_H_
#define RAPIDJSON_MEMORYSTREAM_H_ #define RAPIDJSON_MEMORYSTREAM_H_
@ -7,39 +27,39 @@ namespace rapidjson {
//! Represents an in-memory input byte stream. //! Represents an in-memory input byte stream.
/*! /*!
This class is mainly for being wrapped by EncodedInputStream or AutoUTFInputStream. This class is mainly for being wrapped by EncodedInputStream or AutoUTFInputStream.
It is similar to FileReadBuffer but the source is an in-memory buffer instead of a file. It is similar to FileReadBuffer but the source is an in-memory buffer instead of a file.
Differences between MemoryStream and StringStream: Differences between MemoryStream and StringStream:
1. StringStream has encoding but MemoryStream is a byte stream. 1. StringStream has encoding but MemoryStream is a byte stream.
2. MemoryStream needs size of the source buffer and the buffer don't need to be null terminated. StringStream assume null-terminated string as source. 2. MemoryStream needs size of the source buffer and the buffer don't need to be null terminated. StringStream assume null-terminated string as source.
3. MemoryStream supports Peek4() for encoding detection. StringStream is specified with an encoding so it should not have Peek4(). 3. MemoryStream supports Peek4() for encoding detection. StringStream is specified with an encoding so it should not have Peek4().
\note implements Stream concept \note implements Stream concept
*/ */
struct MemoryStream { struct MemoryStream {
typedef char Ch; // byte typedef char Ch; // byte
MemoryStream(const Ch *src, size_t size) : src_(src), begin_(src), end_(src + size), size_(size) {} MemoryStream(const Ch *src, size_t size) : src_(src), begin_(src), end_(src + size), size_(size) {}
Ch Peek() const { return *src_; } Ch Peek() const { return *src_; }
Ch Take() { return (src_ == end_) ? '\0' : *src_++; } Ch Take() { return (src_ == end_) ? '\0' : *src_++; }
size_t Tell() const { return static_cast<size_t>(src_ - begin_); } size_t Tell() const { return static_cast<size_t>(src_ - begin_); }
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
void Put(Ch) { RAPIDJSON_ASSERT(false); } void Put(Ch) { RAPIDJSON_ASSERT(false); }
void Flush() { RAPIDJSON_ASSERT(false); } void Flush() { RAPIDJSON_ASSERT(false); }
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
// For encoding detection only. // For encoding detection only.
const Ch* Peek4() const { const Ch* Peek4() const {
return Tell() + 4 <= size_ ? src_ : 0; return Tell() + 4 <= size_ ? src_ : 0;
} }
const Ch* src_; //!< Current read position. const Ch* src_; //!< Current read position.
const Ch* begin_; //!< Original head of the string. const Ch* begin_; //!< Original head of the string.
const Ch* end_; //!< End of stream. const Ch* end_; //!< End of stream.
size_t size_; //!< Size of the stream. size_t size_; //!< Size of the stream.
}; };
} // namespace rapidjson } // namespace rapidjson

View File

@ -1,3 +1,23 @@
// Copyright (C) 2011 Milo Yip
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef RAPIDJSON_PRETTYWRITER_H_ #ifndef RAPIDJSON_PRETTYWRITER_H_
#define RAPIDJSON_PRETTYWRITER_H_ #define RAPIDJSON_PRETTYWRITER_H_
@ -12,166 +32,166 @@ namespace rapidjson {
//! Writer with indentation and spacing. //! Writer with indentation and spacing.
/*! /*!
\tparam OutputStream Type of ouptut os. \tparam OutputStream Type of ouptut os.
\tparam SourceEncoding Encoding of source string. \tparam SourceEncoding Encoding of source string.
\tparam TargetEncoding Encoding of output stream. \tparam TargetEncoding Encoding of output stream.
\tparam Allocator Type of allocator for allocating memory of stack. \tparam Allocator Type of allocator for allocating memory of stack.
*/ */
template<typename OutputStream, typename SourceEncoding = UTF8<>, typename TargetEncoding = UTF8<>, typename Allocator = MemoryPoolAllocator<> > template<typename OutputStream, typename SourceEncoding = UTF8<>, typename TargetEncoding = UTF8<>, typename Allocator = MemoryPoolAllocator<> >
class PrettyWriter : public Writer<OutputStream, SourceEncoding, TargetEncoding, Allocator> { class PrettyWriter : public Writer<OutputStream, SourceEncoding, TargetEncoding, Allocator> {
public: public:
typedef Writer<OutputStream, SourceEncoding, TargetEncoding, Allocator> Base; typedef Writer<OutputStream, SourceEncoding, TargetEncoding, Allocator> Base;
typedef typename Base::Ch Ch; typedef typename Base::Ch Ch;
//! Constructor //! Constructor
/*! \param os Output stream. /*! \param os Output stream.
\param allocator User supplied allocator. If it is null, it will create a private one. \param allocator User supplied allocator. If it is null, it will create a private one.
\param levelDepth Initial capacity of stack. \param levelDepth Initial capacity of stack.
*/ */
PrettyWriter(OutputStream& os, Allocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) : PrettyWriter(OutputStream& os, Allocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) :
Base(os, allocator, levelDepth), indentChar_(' '), indentCharCount_(4) {} Base(os, allocator, levelDepth), indentChar_(' '), indentCharCount_(4) {}
//! Set custom indentation. //! Set custom indentation.
/*! \param indentChar Character for indentation. Must be whitespace character (' ', '\\t', '\\n', '\\r'). /*! \param indentChar Character for indentation. Must be whitespace character (' ', '\\t', '\\n', '\\r').
\param indentCharCount Number of indent characters for each indentation level. \param indentCharCount Number of indent characters for each indentation level.
\note The default indentation is 4 spaces. \note The default indentation is 4 spaces.
*/ */
PrettyWriter& SetIndent(Ch indentChar, unsigned indentCharCount) { PrettyWriter& SetIndent(Ch indentChar, unsigned indentCharCount) {
RAPIDJSON_ASSERT(indentChar == ' ' || indentChar == '\t' || indentChar == '\n' || indentChar == '\r'); RAPIDJSON_ASSERT(indentChar == ' ' || indentChar == '\t' || indentChar == '\n' || indentChar == '\r');
indentChar_ = indentChar; indentChar_ = indentChar;
indentCharCount_ = indentCharCount; indentCharCount_ = indentCharCount;
return *this; return *this;
} }
/*! @name Implementation of Handler /*! @name Implementation of Handler
\see Handler \see Handler
*/ */
//@{ //@{
bool Null() { PrettyPrefix(kNullType); return Base::WriteNull(); } bool Null() { PrettyPrefix(kNullType); return Base::WriteNull(); }
bool Bool(bool b) { PrettyPrefix(b ? kTrueType : kFalseType); return Base::WriteBool(b); } bool Bool(bool b) { PrettyPrefix(b ? kTrueType : kFalseType); return Base::WriteBool(b); }
bool Int(int i) { PrettyPrefix(kNumberType); return Base::WriteInt(i); } bool Int(int i) { PrettyPrefix(kNumberType); return Base::WriteInt(i); }
bool Uint(unsigned u) { PrettyPrefix(kNumberType); return Base::WriteUint(u); } bool Uint(unsigned u) { PrettyPrefix(kNumberType); return Base::WriteUint(u); }
bool Int64(int64_t i64) { PrettyPrefix(kNumberType); return Base::WriteInt64(i64); } bool Int64(int64_t i64) { PrettyPrefix(kNumberType); return Base::WriteInt64(i64); }
bool Uint64(uint64_t u64) { PrettyPrefix(kNumberType); return Base::WriteUint64(u64); } bool Uint64(uint64_t u64) { PrettyPrefix(kNumberType); return Base::WriteUint64(u64); }
bool Double(double d) { PrettyPrefix(kNumberType); return Base::WriteDouble(d); } bool Double(double d) { PrettyPrefix(kNumberType); return Base::WriteDouble(d); }
bool String(const Ch* str, SizeType length, bool copy = false) { bool String(const Ch* str, SizeType length, bool copy = false) {
(void)copy; (void)copy;
PrettyPrefix(kStringType); PrettyPrefix(kStringType);
return Base::WriteString(str, length); return Base::WriteString(str, length);
} }
bool StartObject() { bool StartObject() {
PrettyPrefix(kObjectType); PrettyPrefix(kObjectType);
new (Base::level_stack_.template Push<typename Base::Level>()) typename Base::Level(false); new (Base::level_stack_.template Push<typename Base::Level>()) typename Base::Level(false);
return Base::WriteStartObject(); return Base::WriteStartObject();
} }
bool EndObject(SizeType memberCount = 0) { bool EndObject(SizeType memberCount = 0) {
(void)memberCount; (void)memberCount;
RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level));
RAPIDJSON_ASSERT(!Base::level_stack_.template Top<typename Base::Level>()->inArray); RAPIDJSON_ASSERT(!Base::level_stack_.template Top<typename Base::Level>()->inArray);
bool empty = Base::level_stack_.template Pop<typename Base::Level>(1)->valueCount == 0; bool empty = Base::level_stack_.template Pop<typename Base::Level>(1)->valueCount == 0;
if (!empty) { if (!empty) {
Base::os_->Put('\n'); Base::os_->Put('\n');
WriteIndent(); WriteIndent();
} }
if (!Base::WriteEndObject()) if (!Base::WriteEndObject())
return false; return false;
if (Base::level_stack_.Empty()) // end of json text if (Base::level_stack_.Empty()) // end of json text
Base::os_->Flush(); Base::os_->Flush();
return true; return true;
} }
bool StartArray() { bool StartArray() {
PrettyPrefix(kArrayType); PrettyPrefix(kArrayType);
new (Base::level_stack_.template Push<typename Base::Level>()) typename Base::Level(true); new (Base::level_stack_.template Push<typename Base::Level>()) typename Base::Level(true);
return Base::WriteStartArray(); return Base::WriteStartArray();
} }
bool EndArray(SizeType memberCount = 0) { bool EndArray(SizeType memberCount = 0) {
(void)memberCount; (void)memberCount;
RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level));
RAPIDJSON_ASSERT(Base::level_stack_.template Top<typename Base::Level>()->inArray); RAPIDJSON_ASSERT(Base::level_stack_.template Top<typename Base::Level>()->inArray);
bool empty = Base::level_stack_.template Pop<typename Base::Level>(1)->valueCount == 0; bool empty = Base::level_stack_.template Pop<typename Base::Level>(1)->valueCount == 0;
if (!empty) { if (!empty) {
Base::os_->Put('\n'); Base::os_->Put('\n');
WriteIndent(); WriteIndent();
} }
if (!Base::WriteEndArray()) if (!Base::WriteEndArray())
return false; return false;
if (Base::level_stack_.Empty()) // end of json text if (Base::level_stack_.Empty()) // end of json text
Base::os_->Flush(); Base::os_->Flush();
return true; return true;
} }
//@} //@}
/*! @name Convenience extensions */ /*! @name Convenience extensions */
//@{ //@{
//! Simpler but slower overload. //! Simpler but slower overload.
bool String(const Ch* str) { return String(str, internal::StrLen(str)); } bool String(const Ch* str) { return String(str, internal::StrLen(str)); }
//@} //@}
protected: protected:
void PrettyPrefix(Type type) { void PrettyPrefix(Type type) {
(void)type; (void)type;
if (Base::level_stack_.GetSize() != 0) { // this value is not at root if (Base::level_stack_.GetSize() != 0) { // this value is not at root
typename Base::Level* level = Base::level_stack_.template Top<typename Base::Level>(); typename Base::Level* level = Base::level_stack_.template Top<typename Base::Level>();
if (level->inArray) { if (level->inArray) {
if (level->valueCount > 0) { if (level->valueCount > 0) {
Base::os_->Put(','); // add comma if it is not the first element in array Base::os_->Put(','); // add comma if it is not the first element in array
Base::os_->Put('\n'); Base::os_->Put('\n');
} }
else else
Base::os_->Put('\n'); Base::os_->Put('\n');
WriteIndent(); WriteIndent();
} }
else { // in object else { // in object
if (level->valueCount > 0) { if (level->valueCount > 0) {
if (level->valueCount % 2 == 0) { if (level->valueCount % 2 == 0) {
Base::os_->Put(','); Base::os_->Put(',');
Base::os_->Put('\n'); Base::os_->Put('\n');
} }
else { else {
Base::os_->Put(':'); Base::os_->Put(':');
Base::os_->Put(' '); Base::os_->Put(' ');
} }
} }
else else
Base::os_->Put('\n'); Base::os_->Put('\n');
if (level->valueCount % 2 == 0) if (level->valueCount % 2 == 0)
WriteIndent(); WriteIndent();
} }
if (!level->inArray && level->valueCount % 2 == 0) if (!level->inArray && level->valueCount % 2 == 0)
RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name
level->valueCount++; level->valueCount++;
} }
else { else {
RAPIDJSON_ASSERT(type == kObjectType || type == kArrayType); RAPIDJSON_ASSERT(type == kObjectType || type == kArrayType);
RAPIDJSON_ASSERT(!Base::hasRoot_); // Should only has one and only one root. RAPIDJSON_ASSERT(!Base::hasRoot_); // Should only has one and only one root.
Base::hasRoot_ = true; Base::hasRoot_ = true;
} }
} }
void WriteIndent() { void WriteIndent() {
size_t count = (Base::level_stack_.GetSize() / sizeof(typename Base::Level)) * indentCharCount_; size_t count = (Base::level_stack_.GetSize() / sizeof(typename Base::Level)) * indentCharCount_;
PutN(*Base::os_, indentChar_, count); PutN(*Base::os_, indentChar_, count);
} }
Ch indentChar_; Ch indentChar_;
unsigned indentCharCount_; unsigned indentCharCount_;
private: private:
// Prohibit copy constructor & assignment operator. // Prohibit copy constructor & assignment operator.
PrettyWriter(const PrettyWriter&); PrettyWriter(const PrettyWriter&);
PrettyWriter& operator=(const PrettyWriter&); PrettyWriter& operator=(const PrettyWriter&);
}; };
} // namespace rapidjson } // namespace rapidjson

View File

@ -1,3 +1,23 @@
// Copyright (C) 2011 Milo Yip
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef RAPIDJSON_RAPIDJSON_H_ #ifndef RAPIDJSON_RAPIDJSON_H_
#define RAPIDJSON_RAPIDJSON_H_ #define RAPIDJSON_RAPIDJSON_H_
@ -5,13 +25,13 @@
// Version 0.1 // Version 0.1
/*!\file rapidjson.h /*!\file rapidjson.h
\brief common definitions and configuration \brief common definitions and configuration
\todo Complete Doxygen documentation for configure macros. \todo Complete Doxygen documentation for configure macros.
*/ */
#include <cstdlib> // malloc(), realloc(), free() #include <cstdlib> // malloc(), realloc(), free()
#include <cstring> // memcpy() #include <cstring> // memcpy()
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// RAPIDJSON_NO_INT64DEFINE // RAPIDJSON_NO_INT64DEFINE
@ -46,17 +66,17 @@
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// RAPIDJSON_ENDIAN // RAPIDJSON_ENDIAN
#define RAPIDJSON_LITTLEENDIAN 0 //!< Little endian machine #define RAPIDJSON_LITTLEENDIAN 0 //!< Little endian machine
#define RAPIDJSON_BIGENDIAN 1 //!< Big endian machine #define RAPIDJSON_BIGENDIAN 1 //!< Big endian machine
//! Endianness of the machine. //! Endianness of the machine.
/*! GCC 4.6 provided macro for detecting endianness of the target machine. But other /*! GCC 4.6 provided macro for detecting endianness of the target machine. But other
compilers may not have this. User can define RAPIDJSON_ENDIAN to either compilers may not have this. User can define RAPIDJSON_ENDIAN to either
\ref RAPIDJSON_LITTLEENDIAN or \ref RAPIDJSON_BIGENDIAN. \ref RAPIDJSON_LITTLEENDIAN or \ref RAPIDJSON_BIGENDIAN.
Implemented with reference to Implemented with reference to
https://gcc.gnu.org/onlinedocs/gcc-4.6.0/cpp/Common-Predefined-Macros.html https://gcc.gnu.org/onlinedocs/gcc-4.6.0/cpp/Common-Predefined-Macros.html
http://www.boost.org/doc/libs/1_42_0/boost/detail/endian.hpp http://www.boost.org/doc/libs/1_42_0/boost/detail/endian.hpp
*/ */
#ifndef RAPIDJSON_ENDIAN #ifndef RAPIDJSON_ENDIAN
// Detect with GCC 4.6's macro // Detect with GCC 4.6's macro
@ -67,27 +87,27 @@
# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN # define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN
# else # else
# error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN. # error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN.
# endif // __BYTE_ORDER__ # endif // __BYTE_ORDER__
// Detect with GLIBC's endian.h // Detect with GLIBC's endian.h
# elif defined(__GLIBC__) # elif defined(__GLIBC__)
# include <endian.h> # include <endian.h>
# if (__BYTE_ORDER == __LITTLE_ENDIAN) # if (__BYTE_ORDER == __LITTLE_ENDIAN)
# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN # define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN
# elif (__BYTE_ORDER == __BIG_ENDIAN) # elif (__BYTE_ORDER == __BIG_ENDIAN)
# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN # define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN
# else # else
# error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN. # error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN.
# endif // __GLIBC__ # endif // __GLIBC__
// Detect with _LITTLE_ENDIAN and _BIG_ENDIAN macro // Detect with _LITTLE_ENDIAN and _BIG_ENDIAN macro
# elif defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN) # elif defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN)
# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN # define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN
# elif defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN) # elif defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN)
# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN # define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN
// Detect with architecture macros // Detect with architecture macros
# elif defined(__sparc) || defined(__sparc__) || defined(_POWER) || defined(__powerpc__) || defined(__ppc__) || defined(__hpux) || defined(__hppa) || defined(_MIPSEB) || defined(_POWER) || defined(__s390__) # elif defined(__sparc) || defined(__sparc__) || defined(_POWER) || defined(__powerpc__) || defined(__ppc__) || defined(__hpux) || defined(__hppa) || defined(_MIPSEB) || defined(_POWER) || defined(__s390__)
# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN # define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN
# elif defined(__i386__) || defined(__alpha__) || defined(__ia64) || defined(__ia64__) || defined(_M_IX86) || defined(_M_IA64) || defined(_M_ALPHA) || defined(__amd64) || defined(__amd64__) || defined(_M_AMD64) || defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || defined(__bfin__) # elif defined(__i386__) || defined(__alpha__) || defined(__ia64) || defined(__ia64__) || defined(_M_IX86) || defined(_M_IA64) || defined(_M_ALPHA) || defined(__amd64) || defined(__amd64__) || defined(_M_AMD64) || defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || defined(__bfin__)
# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN # define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN
# else # else
# error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN. # error Unknown machine endianess detected. User needs to define RAPIDJSON_ENDIAN.
# endif # endif
@ -98,8 +118,8 @@
//! Data alignment of the machine. //! Data alignment of the machine.
/*! /*!
Some machine requires strict data alignment. Some machine requires strict data alignment.
Currently the default uses 4 bytes alignment. User can customize this. Currently the default uses 4 bytes alignment. User can customize this.
*/ */
#ifndef RAPIDJSON_ALIGN #ifndef RAPIDJSON_ALIGN
#define RAPIDJSON_ALIGN(x) ((x + 3u) & ~3u) #define RAPIDJSON_ALIGN(x) ((x + 3u) & ~3u)
@ -110,9 +130,9 @@
//! Construct a 64-bit literal by a pair of 32-bit integer. //! Construct a 64-bit literal by a pair of 32-bit integer.
/*! /*!
64-bit literal with or without ULL suffix is prone to compiler warnings. 64-bit literal with or without ULL suffix is prone to compiler warnings.
UINT64_C() is C macro which cause compilation problems. UINT64_C() is C macro which cause compilation problems.
Use this macro to define 64-bit constants by a pair of 32-bit integer. Use this macro to define 64-bit constants by a pair of 32-bit integer.
*/ */
#ifndef RAPIDJSON_UINT64_C2 #ifndef RAPIDJSON_UINT64_C2
#define RAPIDJSON_UINT64_C2(high32, low32) ((static_cast<uint64_t>(high32) << 32) | static_cast<uint64_t>(low32)) #define RAPIDJSON_UINT64_C2(high32, low32) ((static_cast<uint64_t>(high32) << 32) | static_cast<uint64_t>(low32))
@ -148,7 +168,7 @@ typedef unsigned SizeType;
//! Assertion. //! Assertion.
/*! By default, rapidjson uses C assert() for assertion. /*! By default, rapidjson uses C assert() for assertion.
User can override it by defining RAPIDJSON_ASSERT(x) macro. User can override it by defining RAPIDJSON_ASSERT(x) macro.
*/ */
#ifndef RAPIDJSON_ASSERT #ifndef RAPIDJSON_ASSERT
#include <cassert> #include <cassert>
@ -180,13 +200,13 @@ template<int x> struct StaticAssertTest {};
//!@endcond //!@endcond
/*! \def RAPIDJSON_STATIC_ASSERT /*! \def RAPIDJSON_STATIC_ASSERT
\brief (internal) macro to check for conditions at compile-time \brief (internal) macro to check for conditions at compile-time
\param x compile-time condition \param x compile-time condition
\hideinitializer \hideinitializer
*/ */
#define RAPIDJSON_STATIC_ASSERT(x) typedef ::rapidjson::StaticAssertTest<\ #define RAPIDJSON_STATIC_ASSERT(x) typedef ::rapidjson::StaticAssertTest<\
sizeof(::rapidjson::STATIC_ASSERTION_FAILURE<bool(x) >)>\ sizeof(::rapidjson::STATIC_ASSERTION_FAILURE<bool(x) >)>\
RAPIDJSON_JOIN(StaticAssertTypedef, __LINE__) RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE RAPIDJSON_JOIN(StaticAssertTypedef, __LINE__) RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE
#endif #endif
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@ -214,7 +234,7 @@ template<int x> struct StaticAssertTest {};
#define RAPIDJSON_PRAGMA(x) _Pragma(RAPIDJSON_STRINGIFY(x)) #define RAPIDJSON_PRAGMA(x) _Pragma(RAPIDJSON_STRINGIFY(x))
#define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(GCC diagnostic x) #define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(GCC diagnostic x)
#define RAPIDJSON_DIAG_OFF(x) \ #define RAPIDJSON_DIAG_OFF(x) \
RAPIDJSON_DIAG_PRAGMA(ignored RAPIDJSON_STRINGIFY(RAPIDJSON_JOIN(-W,x))) RAPIDJSON_DIAG_PRAGMA(ignored RAPIDJSON_STRINGIFY(RAPIDJSON_JOIN(-W,x)))
// push/pop support in Clang and GCC>=4.6 // push/pop support in Clang and GCC>=4.6
#if defined(__clang__) || (defined(__GNUC__) && RAPIDJSON_VERSION_CODE(__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__) >= RAPIDJSON_VERSION_CODE(4,6,0)) #if defined(__clang__) || (defined(__GNUC__) && RAPIDJSON_VERSION_CODE(__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__) >= RAPIDJSON_VERSION_CODE(4,6,0))
@ -258,65 +278,65 @@ namespace rapidjson {
// Stream // Stream
/*! \class rapidjson::Stream /*! \class rapidjson::Stream
\brief Concept for reading and writing characters. \brief Concept for reading and writing characters.
For read-only stream, no need to implement PutBegin(), Put(), Flush() and PutEnd(). For read-only stream, no need to implement PutBegin(), Put(), Flush() and PutEnd().
For write-only stream, only need to implement Put() and Flush(). For write-only stream, only need to implement Put() and Flush().
\code \code
concept Stream { concept Stream {
typename Ch; //!< Character type of the stream. typename Ch; //!< Character type of the stream.
//! Read the current character from stream without moving the read cursor. //! Read the current character from stream without moving the read cursor.
Ch Peek() const; Ch Peek() const;
//! Read the current character from stream and moving the read cursor to next character. //! Read the current character from stream and moving the read cursor to next character.
Ch Take(); Ch Take();
//! Get the current read cursor. //! Get the current read cursor.
//! \return Number of characters read from start. //! \return Number of characters read from start.
size_t Tell(); size_t Tell();
//! Begin writing operation at the current read pointer. //! Begin writing operation at the current read pointer.
//! \return The begin writer pointer. //! \return The begin writer pointer.
Ch* PutBegin(); Ch* PutBegin();
//! Write a character. //! Write a character.
void Put(Ch c); void Put(Ch c);
//! Flush the buffer. //! Flush the buffer.
void Flush(); void Flush();
//! End the writing operation. //! End the writing operation.
//! \param begin The begin write pointer returned by PutBegin(). //! \param begin The begin write pointer returned by PutBegin().
//! \return Number of characters written. //! \return Number of characters written.
size_t PutEnd(Ch* begin); size_t PutEnd(Ch* begin);
} }
\endcode \endcode
*/ */
//! Provides additional information for stream. //! Provides additional information for stream.
/*! /*!
By using traits pattern, this type provides a default configuration 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. For custom stream, this type can be specialized for other configuration.
See TEST(Reader, CustomStringStream) in readertest.cpp for example. See TEST(Reader, CustomStringStream) in readertest.cpp for example.
*/ */
template<typename Stream> template<typename Stream>
struct StreamTraits { struct StreamTraits {
//! Whether to make local copy of stream for optimization during parsing. //! Whether to make local copy of stream for optimization during parsing.
/*! /*!
By default, for safety, streams do not use local copy optimization. By default, for safety, streams do not use local copy optimization.
Stream that can be copied fast should specialize this, like StreamTraits<StringStream>. Stream that can be copied fast should specialize this, like StreamTraits<StringStream>.
*/ */
enum { copyOptimization = 0 }; enum { copyOptimization = 0 };
}; };
//! Put N copies of a character to a stream. //! Put N copies of a character to a stream.
template<typename Stream, typename Ch> template<typename Stream, typename Ch>
inline void PutN(Stream& stream, Ch c, size_t n) { inline void PutN(Stream& stream, Ch c, size_t n) {
for (size_t i = 0; i < n; i++) for (size_t i = 0; i < n; i++)
stream.Put(c); stream.Put(c);
} }
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@ -327,26 +347,26 @@ inline void PutN(Stream& stream, Ch c, size_t n) {
*/ */
template <typename Encoding> template <typename Encoding>
struct GenericStringStream { struct GenericStringStream {
typedef typename Encoding::Ch Ch; typedef typename Encoding::Ch Ch;
GenericStringStream(const Ch *src) : src_(src), head_(src) {} GenericStringStream(const Ch *src) : src_(src), head_(src) {}
Ch Peek() const { return *src_; } Ch Peek() const { return *src_; }
Ch Take() { return *src_++; } Ch Take() { return *src_++; }
size_t Tell() const { return static_cast<size_t>(src_ - head_); } size_t Tell() const { return static_cast<size_t>(src_ - head_); }
Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; }
void Put(Ch) { RAPIDJSON_ASSERT(false); } void Put(Ch) { RAPIDJSON_ASSERT(false); }
void Flush() { RAPIDJSON_ASSERT(false); } void Flush() { RAPIDJSON_ASSERT(false); }
size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; }
const Ch* src_; //!< Current read position. const Ch* src_; //!< Current read position.
const Ch* head_; //!< Original head of the string. const Ch* head_; //!< Original head of the string.
}; };
template <typename Encoding> template <typename Encoding>
struct StreamTraits<GenericStringStream<Encoding> > { struct StreamTraits<GenericStringStream<Encoding> > {
enum { copyOptimization = 1 }; enum { copyOptimization = 1 };
}; };
//! String stream with UTF8 encoding. //! String stream with UTF8 encoding.
@ -357,37 +377,37 @@ typedef GenericStringStream<UTF8<> > StringStream;
//! A read-write string stream. //! A read-write string stream.
/*! This string stream is particularly designed for in-situ parsing. /*! This string stream is particularly designed for in-situ parsing.
\note implements Stream concept \note implements Stream concept
*/ */
template <typename Encoding> template <typename Encoding>
struct GenericInsituStringStream { struct GenericInsituStringStream {
typedef typename Encoding::Ch Ch; typedef typename Encoding::Ch Ch;
GenericInsituStringStream(Ch *src) : src_(src), dst_(0), head_(src) {} GenericInsituStringStream(Ch *src) : src_(src), dst_(0), head_(src) {}
// Read // Read
Ch Peek() { return *src_; } Ch Peek() { return *src_; }
Ch Take() { return *src_++; } Ch Take() { return *src_++; }
size_t Tell() { return static_cast<size_t>(src_ - head_); } size_t Tell() { return static_cast<size_t>(src_ - head_); }
// Write // Write
void Put(Ch c) { RAPIDJSON_ASSERT(dst_ != 0); *dst_++ = c; } void Put(Ch c) { RAPIDJSON_ASSERT(dst_ != 0); *dst_++ = c; }
Ch* PutBegin() { return dst_ = src_; } Ch* PutBegin() { return dst_ = src_; }
size_t PutEnd(Ch* begin) { return static_cast<size_t>(dst_ - begin); } size_t PutEnd(Ch* begin) { return static_cast<size_t>(dst_ - begin); }
void Flush() {} void Flush() {}
Ch* Push(size_t count) { Ch* begin = dst_; dst_ += count; return begin; } Ch* Push(size_t count) { Ch* begin = dst_; dst_ += count; return begin; }
void Pop(size_t count) { dst_ -= count; } void Pop(size_t count) { dst_ -= count; }
Ch* src_; Ch* src_;
Ch* dst_; Ch* dst_;
Ch* head_; Ch* head_;
}; };
template <typename Encoding> template <typename Encoding>
struct StreamTraits<GenericInsituStringStream<Encoding> > { struct StreamTraits<GenericInsituStringStream<Encoding> > {
enum { copyOptimization = 1 }; enum { copyOptimization = 1 };
}; };
//! Insitu string stream with UTF8 encoding. //! Insitu string stream with UTF8 encoding.
@ -398,13 +418,13 @@ typedef GenericInsituStringStream<UTF8<> > InsituStringStream;
//! Type of JSON value //! Type of JSON value
enum Type { enum Type {
kNullType = 0, //!< null kNullType = 0, //!< null
kFalseType = 1, //!< false kFalseType = 1, //!< false
kTrueType = 2, //!< true kTrueType = 2, //!< true
kObjectType = 3, //!< object kObjectType = 3, //!< object
kArrayType = 4, //!< array kArrayType = 4, //!< array
kStringType = 5, //!< string kStringType = 5, //!< string
kNumberType = 6 //!< number kNumberType = 6 //!< number
}; };
} // namespace rapidjson } // namespace rapidjson

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,23 @@
// Copyright (C) 2011 Milo Yip
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef RAPIDJSON_STRINGBUFFER_H_ #ifndef RAPIDJSON_STRINGBUFFER_H_
#define RAPIDJSON_STRINGBUFFER_H_ #define RAPIDJSON_STRINGBUFFER_H_
@ -8,35 +28,35 @@ namespace rapidjson {
//! Represents an in-memory output stream. //! Represents an in-memory output stream.
/*! /*!
\tparam Encoding Encoding of the stream. \tparam Encoding Encoding of the stream.
\tparam Allocator type for allocating memory buffer. \tparam Allocator type for allocating memory buffer.
\note implements Stream concept \note implements Stream concept
*/ */
template <typename Encoding, typename Allocator = CrtAllocator> template <typename Encoding, typename Allocator = CrtAllocator>
struct GenericStringBuffer { struct GenericStringBuffer {
typedef typename Encoding::Ch Ch; typedef typename Encoding::Ch Ch;
GenericStringBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {} GenericStringBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {}
void Put(Ch c) { *stack_.template Push<Ch>() = c; } void Put(Ch c) { *stack_.template Push<Ch>() = c; }
void Flush() {} void Flush() {}
void Clear() { stack_.Clear(); } void Clear() { stack_.Clear(); }
Ch* Push(size_t count) { return stack_.template Push<Ch>(count); } Ch* Push(size_t count) { return stack_.template Push<Ch>(count); }
void Pop(size_t count) { stack_.template Pop<Ch>(count); } void Pop(size_t count) { stack_.template Pop<Ch>(count); }
const Ch* GetString() const { const Ch* GetString() const {
// Push and pop a null terminator. This is safe. // Push and pop a null terminator. This is safe.
*stack_.template Push<Ch>() = '\0'; *stack_.template Push<Ch>() = '\0';
stack_.template Pop<Ch>(1); stack_.template Pop<Ch>(1);
return stack_.template Bottom<Ch>(); return stack_.template Bottom<Ch>();
} }
size_t GetSize() const { return stack_.GetSize(); } size_t GetSize() const { return stack_.GetSize(); }
static const size_t kDefaultCapacity = 256; static const size_t kDefaultCapacity = 256;
mutable internal::Stack<Allocator> stack_; mutable internal::Stack<Allocator> stack_;
}; };
//! String buffer with UTF8 encoding //! String buffer with UTF8 encoding
@ -45,7 +65,7 @@ typedef GenericStringBuffer<UTF8<> > StringBuffer;
//! Implement specialized version of PutN() with memset() for better performance. //! Implement specialized version of PutN() with memset() for better performance.
template<> template<>
inline void PutN(GenericStringBuffer<UTF8<> >& stream, char c, size_t n) { inline void PutN(GenericStringBuffer<UTF8<> >& stream, char c, size_t n) {
memset(stream.stack_.Push<char>(n), c, n * sizeof(c)); memset(stream.stack_.Push<char>(n), c, n * sizeof(c));
} }
} // namespace rapidjson } // namespace rapidjson

View File

@ -1,3 +1,23 @@
// Copyright (C) 2011 Milo Yip
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef RAPIDJSON_WRITER_H_ #ifndef RAPIDJSON_WRITER_H_
#define RAPIDJSON_WRITER_H_ #define RAPIDJSON_WRITER_H_
@ -7,7 +27,7 @@
#include "internal/dtoa.h" #include "internal/dtoa.h"
#include "internal/itoa.h" #include "internal/itoa.h"
#include "stringbuffer.h" #include "stringbuffer.h"
#include <new> // placement new #include <new> // placement new
#ifdef _MSC_VER #ifdef _MSC_VER
RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_PUSH
@ -18,345 +38,345 @@ namespace rapidjson {
//! JSON writer //! JSON writer
/*! Writer implements the concept Handler. /*! Writer implements the concept Handler.
It generates JSON text by events to an output os. It generates JSON text by events to an output os.
User may programmatically calls the functions of a writer to generate JSON text. User may programmatically calls the functions of a writer to generate JSON text.
On the other side, a writer can also be passed to objects that generates events, On the other side, a writer can also be passed to objects that generates events,
for example Reader::Parse() and Document::Accept(). for example Reader::Parse() and Document::Accept().
\tparam OutputStream Type of output stream. \tparam OutputStream Type of output stream.
\tparam SourceEncoding Encoding of source string. \tparam SourceEncoding Encoding of source string.
\tparam TargetEncoding Encoding of output stream. \tparam TargetEncoding Encoding of output stream.
\tparam Allocator Type of allocator for allocating memory of stack. \tparam Allocator Type of allocator for allocating memory of stack.
\note implements Handler concept \note implements Handler concept
*/ */
template<typename OutputStream, typename SourceEncoding = UTF8<>, typename TargetEncoding = UTF8<>, typename Allocator = MemoryPoolAllocator<> > template<typename OutputStream, typename SourceEncoding = UTF8<>, typename TargetEncoding = UTF8<>, typename Allocator = MemoryPoolAllocator<> >
class Writer { class Writer {
public: public:
typedef typename SourceEncoding::Ch Ch; typedef typename SourceEncoding::Ch Ch;
//! Constructor //! Constructor
/*! \param os Output stream. /*! \param os Output stream.
\param allocator User supplied allocator. If it is null, it will create a private one. \param allocator User supplied allocator. If it is null, it will create a private one.
\param levelDepth Initial capacity of stack. \param levelDepth Initial capacity of stack.
*/ */
Writer(OutputStream& os, Allocator* allocator = 0, size_t levelDepth = kDefaultLevelDepth) : Writer(OutputStream& os, Allocator* allocator = 0, size_t levelDepth = kDefaultLevelDepth) :
os_(&os), level_stack_(allocator, levelDepth * sizeof(Level)), hasRoot_(false) {} os_(&os), level_stack_(allocator, levelDepth * sizeof(Level)), hasRoot_(false) {}
Writer(Allocator* allocator = 0, size_t levelDepth = kDefaultLevelDepth) : Writer(Allocator* allocator = 0, size_t levelDepth = kDefaultLevelDepth) :
os_(0), level_stack_(allocator, levelDepth * sizeof(Level)), hasRoot_(false) {} os_(0), level_stack_(allocator, levelDepth * sizeof(Level)), hasRoot_(false) {}
//! Reset the writer with a new stream. //! Reset the writer with a new stream.
/*! /*!
This function reset the writer with a new stream and default settings, This function reset the writer with a new stream and default settings,
in order to make a Writer object reusable for output multiple JSONs. in order to make a Writer object reusable for output multiple JSONs.
\param os New output stream. \param os New output stream.
\code \code
Writer<OutputStream> writer(os1); Writer<OutputStream> writer(os1);
writer.StartObject(); writer.StartObject();
// ... // ...
writer.EndObject(); writer.EndObject();
writer.Reset(os2); writer.Reset(os2);
writer.StartObject(); writer.StartObject();
// ... // ...
writer.EndObject(); writer.EndObject();
\endcode \endcode
*/ */
void Reset(OutputStream& os) { void Reset(OutputStream& os) {
os_ = &os; os_ = &os;
hasRoot_ = false; hasRoot_ = false;
level_stack_.Clear(); level_stack_.Clear();
} }
//! Checks whether the output is a complete JSON. //! Checks whether the output is a complete JSON.
/*! /*!
A complete JSON has a complete root object or array. A complete JSON has a complete root object or array.
*/ */
bool IsComplete() const { bool IsComplete() const {
return hasRoot_ && level_stack_.Empty(); return hasRoot_ && level_stack_.Empty();
} }
/*!@name Implementation of Handler /*!@name Implementation of Handler
\see Handler \see Handler
*/ */
//@{ //@{
bool Null() { Prefix(kNullType); return WriteNull(); } bool Null() { Prefix(kNullType); return WriteNull(); }
bool Bool(bool b) { Prefix(b ? kTrueType : kFalseType); return WriteBool(b); } bool Bool(bool b) { Prefix(b ? kTrueType : kFalseType); return WriteBool(b); }
bool Int(int i) { Prefix(kNumberType); return WriteInt(i); } bool Int(int i) { Prefix(kNumberType); return WriteInt(i); }
bool Uint(unsigned u) { Prefix(kNumberType); return WriteUint(u); } bool Uint(unsigned u) { Prefix(kNumberType); return WriteUint(u); }
bool Int64(int64_t i64) { Prefix(kNumberType); return WriteInt64(i64); } bool Int64(int64_t i64) { Prefix(kNumberType); return WriteInt64(i64); }
bool Uint64(uint64_t u64) { Prefix(kNumberType); return WriteUint64(u64); } bool Uint64(uint64_t u64) { Prefix(kNumberType); return WriteUint64(u64); }
//! Writes the given \c double value to the stream //! Writes the given \c double value to the stream
/*! /*!
\param d The value to be written. \param d The value to be written.
\return Whether it is succeed. \return Whether it is succeed.
*/ */
bool Double(double d) { Prefix(kNumberType); return WriteDouble(d); } bool Double(double d) { Prefix(kNumberType); return WriteDouble(d); }
bool String(const Ch* str, SizeType length, bool copy = false) { bool String(const Ch* str, SizeType length, bool copy = false) {
(void)copy; (void)copy;
Prefix(kStringType); Prefix(kStringType);
return WriteString(str, length); return WriteString(str, length);
} }
bool StartObject() { bool StartObject() {
Prefix(kObjectType); Prefix(kObjectType);
new (level_stack_.template Push<Level>()) Level(false); new (level_stack_.template Push<Level>()) Level(false);
return WriteStartObject(); return WriteStartObject();
} }
bool EndObject(SizeType memberCount = 0) { bool EndObject(SizeType memberCount = 0) {
(void)memberCount; (void)memberCount;
RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level));
RAPIDJSON_ASSERT(!level_stack_.template Top<Level>()->inArray); RAPIDJSON_ASSERT(!level_stack_.template Top<Level>()->inArray);
level_stack_.template Pop<Level>(1); level_stack_.template Pop<Level>(1);
bool ret = WriteEndObject(); bool ret = WriteEndObject();
if (level_stack_.Empty()) // end of json text if (level_stack_.Empty()) // end of json text
os_->Flush(); os_->Flush();
return ret; return ret;
} }
bool StartArray() { bool StartArray() {
Prefix(kArrayType); Prefix(kArrayType);
new (level_stack_.template Push<Level>()) Level(true); new (level_stack_.template Push<Level>()) Level(true);
return WriteStartArray(); return WriteStartArray();
} }
bool EndArray(SizeType elementCount = 0) { bool EndArray(SizeType elementCount = 0) {
(void)elementCount; (void)elementCount;
RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level));
RAPIDJSON_ASSERT(level_stack_.template Top<Level>()->inArray); RAPIDJSON_ASSERT(level_stack_.template Top<Level>()->inArray);
level_stack_.template Pop<Level>(1); level_stack_.template Pop<Level>(1);
bool ret = WriteEndArray(); bool ret = WriteEndArray();
if (level_stack_.Empty()) // end of json text if (level_stack_.Empty()) // end of json text
os_->Flush(); os_->Flush();
return ret; return ret;
} }
//@} //@}
/*! @name Convenience extensions */ /*! @name Convenience extensions */
//@{ //@{
//! Simpler but slower overload. //! Simpler but slower overload.
bool String(const Ch* str) { return String(str, internal::StrLen(str)); } bool String(const Ch* str) { return String(str, internal::StrLen(str)); }
//@} //@}
protected: protected:
//! Information for each nested level //! Information for each nested level
struct Level { struct Level {
Level(bool inArray_) : valueCount(0), inArray(inArray_) {} Level(bool inArray_) : valueCount(0), inArray(inArray_) {}
size_t valueCount; //!< number of values in this level size_t valueCount; //!< number of values in this level
bool inArray; //!< true if in array, otherwise in object bool inArray; //!< true if in array, otherwise in object
}; };
static const size_t kDefaultLevelDepth = 32; static const size_t kDefaultLevelDepth = 32;
bool WriteNull() { bool WriteNull() {
os_->Put('n'); os_->Put('u'); os_->Put('l'); os_->Put('l'); return true; os_->Put('n'); os_->Put('u'); os_->Put('l'); os_->Put('l'); return true;
} }
bool WriteBool(bool b) { bool WriteBool(bool b) {
if (b) { if (b) {
os_->Put('t'); os_->Put('r'); os_->Put('u'); os_->Put('e'); os_->Put('t'); os_->Put('r'); os_->Put('u'); os_->Put('e');
} }
else { else {
os_->Put('f'); os_->Put('a'); os_->Put('l'); os_->Put('s'); os_->Put('e'); os_->Put('f'); os_->Put('a'); os_->Put('l'); os_->Put('s'); os_->Put('e');
} }
return true; return true;
} }
bool WriteInt(int i) { bool WriteInt(int i) {
char buffer[11]; char buffer[11];
const char* end = internal::i32toa(i, buffer); const char* end = internal::i32toa(i, buffer);
for (const char* p = buffer; p != end; ++p) for (const char* p = buffer; p != end; ++p)
os_->Put(*p); os_->Put(*p);
return true; return true;
} }
bool WriteUint(unsigned u) { bool WriteUint(unsigned u) {
char buffer[10]; char buffer[10];
const char* end = internal::u32toa(u, buffer); const char* end = internal::u32toa(u, buffer);
for (const char* p = buffer; p != end; ++p) for (const char* p = buffer; p != end; ++p)
os_->Put(*p); os_->Put(*p);
return true; return true;
} }
bool WriteInt64(int64_t i64) { bool WriteInt64(int64_t i64) {
char buffer[21]; char buffer[21];
const char* end = internal::i64toa(i64, buffer); const char* end = internal::i64toa(i64, buffer);
for (const char* p = buffer; p != end; ++p) for (const char* p = buffer; p != end; ++p)
os_->Put(*p); os_->Put(*p);
return true; return true;
} }
bool WriteUint64(uint64_t u64) { bool WriteUint64(uint64_t u64) {
char buffer[20]; char buffer[20];
char* end = internal::u64toa(u64, buffer); char* end = internal::u64toa(u64, buffer);
for (char* p = buffer; p != end; ++p) for (char* p = buffer; p != end; ++p)
os_->Put(*p); os_->Put(*p);
return true; return true;
} }
bool WriteDouble(double d) { bool WriteDouble(double d) {
char buffer[25]; char buffer[25];
char* end = internal::dtoa(d, buffer); char* end = internal::dtoa(d, buffer);
for (char* p = buffer; p != end; ++p) for (char* p = buffer; p != end; ++p)
os_->Put(*p); os_->Put(*p);
return true; return true;
} }
#undef RAPIDJSON_SNPRINTF #undef RAPIDJSON_SNPRINTF
bool WriteString(const Ch* str, SizeType length) { bool WriteString(const Ch* str, SizeType length) {
static const char hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; static const char hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
static const char escape[256] = { static const char escape[256] = {
#define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 #define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
//0 1 2 3 4 5 6 7 8 9 A B C D E F //0 1 2 3 4 5 6 7 8 9 A B C D E F
'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'b', 't', 'n', 'u', 'f', 'r', 'u', 'u', // 00 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'b', 't', 'n', 'u', 'f', 'r', 'u', 'u', // 00
'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', // 10 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', // 10
0, 0, '"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20 0, 0, '"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20
Z16, Z16, // 30~4F Z16, Z16, // 30~4F
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, // 50 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, // 50
Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16 // 60~FF Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16 // 60~FF
#undef Z16 #undef Z16
}; };
os_->Put('\"'); os_->Put('\"');
GenericStringStream<SourceEncoding> is(str); GenericStringStream<SourceEncoding> is(str);
while (is.Tell() < length) { while (is.Tell() < length) {
const Ch c = is.Peek(); const Ch c = is.Peek();
if (!TargetEncoding::supportUnicode && (unsigned)c >= 0x80) { if (!TargetEncoding::supportUnicode && (unsigned)c >= 0x80) {
// Unicode escaping // Unicode escaping
unsigned codepoint; unsigned codepoint;
if (!SourceEncoding::Decode(is, &codepoint)) if (!SourceEncoding::Decode(is, &codepoint))
return false; return false;
os_->Put('\\'); os_->Put('\\');
os_->Put('u'); os_->Put('u');
if (codepoint <= 0xD7FF || (codepoint >= 0xE000 && codepoint <= 0xFFFF)) { if (codepoint <= 0xD7FF || (codepoint >= 0xE000 && codepoint <= 0xFFFF)) {
os_->Put(hexDigits[(codepoint >> 12) & 15]); os_->Put(hexDigits[(codepoint >> 12) & 15]);
os_->Put(hexDigits[(codepoint >> 8) & 15]); os_->Put(hexDigits[(codepoint >> 8) & 15]);
os_->Put(hexDigits[(codepoint >> 4) & 15]); os_->Put(hexDigits[(codepoint >> 4) & 15]);
os_->Put(hexDigits[(codepoint ) & 15]); os_->Put(hexDigits[(codepoint ) & 15]);
} }
else if (codepoint >= 0x010000 && codepoint <= 0x10FFFF) { else if (codepoint >= 0x010000 && codepoint <= 0x10FFFF) {
// Surrogate pair // Surrogate pair
unsigned s = codepoint - 0x010000; unsigned s = codepoint - 0x010000;
unsigned lead = (s >> 10) + 0xD800; unsigned lead = (s >> 10) + 0xD800;
unsigned trail = (s & 0x3FF) + 0xDC00; unsigned trail = (s & 0x3FF) + 0xDC00;
os_->Put(hexDigits[(lead >> 12) & 15]); os_->Put(hexDigits[(lead >> 12) & 15]);
os_->Put(hexDigits[(lead >> 8) & 15]); os_->Put(hexDigits[(lead >> 8) & 15]);
os_->Put(hexDigits[(lead >> 4) & 15]); os_->Put(hexDigits[(lead >> 4) & 15]);
os_->Put(hexDigits[(lead ) & 15]); os_->Put(hexDigits[(lead ) & 15]);
os_->Put('\\'); os_->Put('\\');
os_->Put('u'); os_->Put('u');
os_->Put(hexDigits[(trail >> 12) & 15]); os_->Put(hexDigits[(trail >> 12) & 15]);
os_->Put(hexDigits[(trail >> 8) & 15]); os_->Put(hexDigits[(trail >> 8) & 15]);
os_->Put(hexDigits[(trail >> 4) & 15]); os_->Put(hexDigits[(trail >> 4) & 15]);
os_->Put(hexDigits[(trail ) & 15]); os_->Put(hexDigits[(trail ) & 15]);
} }
else else
return false; // invalid code point return false; // invalid code point
} }
else if ((sizeof(Ch) == 1 || (unsigned)c < 256) && escape[(unsigned char)c]) { else if ((sizeof(Ch) == 1 || (unsigned)c < 256) && escape[(unsigned char)c]) {
is.Take(); is.Take();
os_->Put('\\'); os_->Put('\\');
os_->Put(escape[(unsigned char)c]); os_->Put(escape[(unsigned char)c]);
if (escape[(unsigned char)c] == 'u') { if (escape[(unsigned char)c] == 'u') {
os_->Put('0'); os_->Put('0');
os_->Put('0'); os_->Put('0');
os_->Put(hexDigits[(unsigned char)c >> 4]); os_->Put(hexDigits[(unsigned char)c >> 4]);
os_->Put(hexDigits[(unsigned char)c & 0xF]); os_->Put(hexDigits[(unsigned char)c & 0xF]);
} }
} }
else else
Transcoder<SourceEncoding, TargetEncoding>::Transcode(is, *os_); Transcoder<SourceEncoding, TargetEncoding>::Transcode(is, *os_);
} }
os_->Put('\"'); os_->Put('\"');
return true; return true;
} }
bool WriteStartObject() { os_->Put('{'); return true; } bool WriteStartObject() { os_->Put('{'); return true; }
bool WriteEndObject() { os_->Put('}'); return true; } bool WriteEndObject() { os_->Put('}'); return true; }
bool WriteStartArray() { os_->Put('['); return true; } bool WriteStartArray() { os_->Put('['); return true; }
bool WriteEndArray() { os_->Put(']'); return true; } bool WriteEndArray() { os_->Put(']'); return true; }
void Prefix(Type type) { void Prefix(Type type) {
(void)type; (void)type;
if (level_stack_.GetSize() != 0) { // this value is not at root if (level_stack_.GetSize() != 0) { // this value is not at root
Level* level = level_stack_.template Top<Level>(); Level* level = level_stack_.template Top<Level>();
if (level->valueCount > 0) { if (level->valueCount > 0) {
if (level->inArray) if (level->inArray)
os_->Put(','); // add comma if it is not the first element in array os_->Put(','); // add comma if it is not the first element in array
else // in object else // in object
os_->Put((level->valueCount % 2 == 0) ? ',' : ':'); os_->Put((level->valueCount % 2 == 0) ? ',' : ':');
} }
if (!level->inArray && level->valueCount % 2 == 0) if (!level->inArray && level->valueCount % 2 == 0)
RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name
level->valueCount++; level->valueCount++;
} }
else { else {
RAPIDJSON_ASSERT(type == kObjectType || type == kArrayType); RAPIDJSON_ASSERT(type == kObjectType || type == kArrayType);
RAPIDJSON_ASSERT(!hasRoot_); // Should only has one and only one root. RAPIDJSON_ASSERT(!hasRoot_); // Should only has one and only one root.
hasRoot_ = true; hasRoot_ = true;
} }
} }
OutputStream* os_; OutputStream* os_;
internal::Stack<Allocator> level_stack_; internal::Stack<Allocator> level_stack_;
bool hasRoot_; bool hasRoot_;
private: private:
// Prohibit copy constructor & assignment operator. // Prohibit copy constructor & assignment operator.
Writer(const Writer&); Writer(const Writer&);
Writer& operator=(const Writer&); Writer& operator=(const Writer&);
}; };
// Full specialization for StringStream to prevent memory copying // Full specialization for StringStream to prevent memory copying
template<> template<>
inline bool Writer<StringBuffer>::WriteInt(int i) { inline bool Writer<StringBuffer>::WriteInt(int i) {
char *buffer = os_->Push(11); char *buffer = os_->Push(11);
const char* end = internal::i32toa(i, buffer); const char* end = internal::i32toa(i, buffer);
os_->Pop(11 - (end - buffer)); os_->Pop(11 - (end - buffer));
return true; return true;
} }
template<> template<>
inline bool Writer<StringBuffer>::WriteUint(unsigned u) { inline bool Writer<StringBuffer>::WriteUint(unsigned u) {
char *buffer = os_->Push(10); char *buffer = os_->Push(10);
const char* end = internal::u32toa(u, buffer); const char* end = internal::u32toa(u, buffer);
os_->Pop(10 - (end - buffer)); os_->Pop(10 - (end - buffer));
return true; return true;
} }
template<> template<>
inline bool Writer<StringBuffer>::WriteInt64(int64_t i64) { inline bool Writer<StringBuffer>::WriteInt64(int64_t i64) {
char *buffer = os_->Push(21); char *buffer = os_->Push(21);
const char* end = internal::i64toa(i64, buffer); const char* end = internal::i64toa(i64, buffer);
os_->Pop(21 - (end - buffer)); os_->Pop(21 - (end - buffer));
return true; return true;
} }
template<> template<>
inline bool Writer<StringBuffer>::WriteUint64(uint64_t u) { inline bool Writer<StringBuffer>::WriteUint64(uint64_t u) {
char *buffer = os_->Push(20); char *buffer = os_->Push(20);
const char* end = internal::u64toa(u, buffer); const char* end = internal::u64toa(u, buffer);
os_->Pop(20 - (end - buffer)); os_->Pop(20 - (end - buffer));
return true; return true;
} }
template<> template<>
inline bool Writer<StringBuffer>::WriteDouble(double d) { inline bool Writer<StringBuffer>::WriteDouble(double d) {
char *buffer = os_->Push(25); char *buffer = os_->Push(25);
char* end = internal::dtoa(d, buffer); char* end = internal::dtoa(d, buffer);
os_->Pop(25 - (end - buffer)); os_->Pop(25 - (end - buffer));
return true; return true;
} }
} // namespace rapidjson } // namespace rapidjson

View File

@ -1,3 +1,23 @@
// Copyright (C) 2011 Milo Yip
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "perftest.h" #include "perftest.h"
#if TEST_JSONCPP #if TEST_JSONCPP
@ -10,48 +30,48 @@ using namespace Json;
class JsonCpp : public PerfTest { class JsonCpp : public PerfTest {
public: public:
virtual void SetUp() { virtual void SetUp() {
PerfTest::SetUp(); PerfTest::SetUp();
Reader reader; Reader reader;
ASSERT_TRUE(reader.parse(json_, root_)); ASSERT_TRUE(reader.parse(json_, root_));
} }
protected: protected:
Value root_; Value root_;
}; };
TEST_F(JsonCpp, ReaderParse) { TEST_F(JsonCpp, ReaderParse) {
for (int i = 0; i < kTrialCount; i++) { for (int i = 0; i < kTrialCount; i++) {
Value root; Value root;
Reader reader; Reader reader;
ASSERT_TRUE(reader.parse(json_, root)); ASSERT_TRUE(reader.parse(json_, root));
} }
} }
TEST_F(JsonCpp, FastWriter) { TEST_F(JsonCpp, FastWriter) {
for (int i = 0; i < kTrialCount; i++) { for (int i = 0; i < kTrialCount; i++) {
FastWriter writer; FastWriter writer;
std::string str = writer.write(root_); std::string str = writer.write(root_);
//if (i == 0) //if (i == 0)
// std::cout << str.length() << std::endl; // std::cout << str.length() << std::endl;
} }
} }
TEST_F(JsonCpp, StyledWriter) { TEST_F(JsonCpp, StyledWriter) {
for (int i = 0; i < kTrialCount; i++) { for (int i = 0; i < kTrialCount; i++) {
StyledWriter writer; StyledWriter writer;
std::string str = writer.write(root_); std::string str = writer.write(root_);
//if (i == 0) //if (i == 0)
// std::cout << str.length() << std::endl; // std::cout << str.length() << std::endl;
} }
} }
TEST_F(JsonCpp, Whitespace) { TEST_F(JsonCpp, Whitespace) {
for (int i = 0; i < kTrialCount; i++) { for (int i = 0; i < kTrialCount; i++) {
Value root; Value root;
Reader reader; Reader reader;
ASSERT_TRUE(reader.parse(whitespace_, root)); ASSERT_TRUE(reader.parse(whitespace_, root));
} }
} }
#endif // TEST_JSONCPP #endif // TEST_JSONCPP

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +1,30 @@
// Copyright (C) 2011 Milo Yip
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "perftest.h" #include "perftest.h"
int main(int argc, char **argv) { int main(int argc, char **argv) {
#if _MSC_VER #if _MSC_VER
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
//void *testWhetherMemoryLeakDetectionWorks = malloc(1); //void *testWhetherMemoryLeakDetectionWorks = malloc(1);
#endif #endif
::testing::InitGoogleTest(&argc, argv); ::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS(); return RUN_ALL_TESTS();
} }

View File

@ -1,12 +1,32 @@
// Copyright (C) 2011 Milo Yip
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef PERFTEST_H_ #ifndef PERFTEST_H_
#define PERFTEST_H_ #define PERFTEST_H_
#define TEST_RAPIDJSON 1 #define TEST_RAPIDJSON 1
#define TEST_JSONCPP 0 #define TEST_JSONCPP 0
#define TEST_YAJL 0 #define TEST_YAJL 0
#define TEST_ULTRAJSON 0 #define TEST_ULTRAJSON 0
#define TEST_PLATFORM 0 #define TEST_PLATFORM 0
#define TEST_MISC 0 #define TEST_MISC 0
#define TEST_VERSION_CODE(x,y,z) \ #define TEST_VERSION_CODE(x,y,z) \
(((x)*100000) + ((y)*100) + (z)) (((x)*100000) + ((y)*100) + (z))
@ -55,57 +75,57 @@
//! Base class for all performance tests //! Base class for all performance tests
class PerfTest : public ::testing::Test { class PerfTest : public ::testing::Test {
public: public:
PerfTest() : filename_(), json_(), length_(), whitespace_(), whitespace_length_() {} PerfTest() : filename_(), json_(), length_(), whitespace_(), whitespace_length_() {}
virtual void SetUp() { virtual void SetUp() {
FILE *fp = fopen(filename_ = "data/sample.json", "rb"); FILE *fp = fopen(filename_ = "data/sample.json", "rb");
if (!fp) if (!fp)
fp = fopen(filename_ = "../../bin/data/sample.json", "rb"); fp = fopen(filename_ = "../../bin/data/sample.json", "rb");
ASSERT_TRUE(fp != 0); ASSERT_TRUE(fp != 0);
fseek(fp, 0, SEEK_END); fseek(fp, 0, SEEK_END);
length_ = (size_t)ftell(fp); length_ = (size_t)ftell(fp);
fseek(fp, 0, SEEK_SET); fseek(fp, 0, SEEK_SET);
json_ = (char*)malloc(length_ + 1); json_ = (char*)malloc(length_ + 1);
ASSERT_EQ(length_, fread(json_, 1, length_, fp)); ASSERT_EQ(length_, fread(json_, 1, length_, fp));
json_[length_] = '\0'; json_[length_] = '\0';
fclose(fp); fclose(fp);
// whitespace test // whitespace test
whitespace_length_ = 1024 * 1024; whitespace_length_ = 1024 * 1024;
whitespace_ = (char *)malloc(whitespace_length_ + 4); whitespace_ = (char *)malloc(whitespace_length_ + 4);
char *p = whitespace_; char *p = whitespace_;
for (size_t i = 0; i < whitespace_length_; i += 4) { for (size_t i = 0; i < whitespace_length_; i += 4) {
*p++ = ' '; *p++ = ' ';
*p++ = '\n'; *p++ = '\n';
*p++ = '\r'; *p++ = '\r';
*p++ = '\t'; *p++ = '\t';
} }
*p++ = '['; *p++ = '[';
*p++ = '0'; *p++ = '0';
*p++ = ']'; *p++ = ']';
*p++ = '\0'; *p++ = '\0';
} }
virtual void TearDown() { virtual void TearDown() {
free(json_); free(json_);
free(whitespace_); free(whitespace_);
json_ = 0; json_ = 0;
whitespace_ = 0; whitespace_ = 0;
} }
private: private:
PerfTest(const PerfTest&); PerfTest(const PerfTest&);
PerfTest& operator=(const PerfTest&); PerfTest& operator=(const PerfTest&);
protected: protected:
const char* filename_; const char* filename_;
char *json_; char *json_;
size_t length_; size_t length_;
char *whitespace_; char *whitespace_;
size_t whitespace_length_; size_t whitespace_length_;
static const size_t kTrialCount = 1000; static const size_t kTrialCount = 1000;
}; };
#endif // __cplusplus #endif // __cplusplus

View File

@ -1,3 +1,23 @@
// Copyright (C) 2011 Milo Yip
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "perftest.h" #include "perftest.h"
// This file is for giving the performance characteristics of the platform (compiler/OS/CPU). // This file is for giving the performance characteristics of the platform (compiler/OS/CPU).
@ -22,130 +42,130 @@
class Platform : public PerfTest { class Platform : public PerfTest {
public: public:
virtual void SetUp() { virtual void SetUp() {
PerfTest::SetUp(); PerfTest::SetUp();
// temp buffer for testing // temp buffer for testing
temp_ = (char *)malloc(length_ + 1); temp_ = (char *)malloc(length_ + 1);
memcpy(temp_, json_, length_); memcpy(temp_, json_, length_);
checkSum_ = CheckSum(); checkSum_ = CheckSum();
} }
char CheckSum() { char CheckSum() {
char c = 0; char c = 0;
for (size_t i = 0; i < length_; ++i) for (size_t i = 0; i < length_; ++i)
c += temp_[i]; c += temp_[i];
return c; return c;
} }
virtual void TearDown() { virtual void TearDown() {
PerfTest::TearDown(); PerfTest::TearDown();
free(temp_); free(temp_);
} }
protected: protected:
char *temp_; char *temp_;
char checkSum_; char checkSum_;
}; };
TEST_F(Platform, CheckSum) { TEST_F(Platform, CheckSum) {
for (int i = 0; i < kTrialCount; i++) for (int i = 0; i < kTrialCount; i++)
EXPECT_EQ(checkSum_, CheckSum()); EXPECT_EQ(checkSum_, CheckSum());
} }
TEST_F(Platform, strlen) { TEST_F(Platform, strlen) {
for (int i = 0; i < kTrialCount; i++) { for (int i = 0; i < kTrialCount; i++) {
size_t l = strlen(json_); size_t l = strlen(json_);
EXPECT_EQ(length_, l); EXPECT_EQ(length_, l);
} }
} }
TEST_F(Platform, memcmp) { TEST_F(Platform, memcmp) {
for (int i = 0; i < kTrialCount; i++) { for (int i = 0; i < kTrialCount; i++) {
EXPECT_EQ(0, memcmp(temp_, json_, length_)); EXPECT_EQ(0, memcmp(temp_, json_, length_));
} }
} }
TEST_F(Platform, pow) { TEST_F(Platform, pow) {
double sum = 0; double sum = 0;
for (int i = 0; i < kTrialCount * kTrialCount; i++) for (int i = 0; i < kTrialCount * kTrialCount; i++)
sum += pow(10.0, i & 255); sum += pow(10.0, i & 255);
EXPECT_GT(sum, 0.0); EXPECT_GT(sum, 0.0);
} }
TEST_F(Platform, Whitespace_strlen) { TEST_F(Platform, Whitespace_strlen) {
for (int i = 0; i < kTrialCount; i++) { for (int i = 0; i < kTrialCount; i++) {
size_t l = strlen(whitespace_); size_t l = strlen(whitespace_);
EXPECT_GT(l, whitespace_length_); EXPECT_GT(l, whitespace_length_);
} }
} }
TEST_F(Platform, Whitespace_strspn) { TEST_F(Platform, Whitespace_strspn) {
for (int i = 0; i < kTrialCount; i++) { for (int i = 0; i < kTrialCount; i++) {
size_t l = strspn(whitespace_, " \n\r\t"); size_t l = strspn(whitespace_, " \n\r\t");
EXPECT_EQ(whitespace_length_, l); EXPECT_EQ(whitespace_length_, l);
} }
} }
TEST_F(Platform, fread) { TEST_F(Platform, fread) {
for (int i = 0; i < kTrialCount; i++) { for (int i = 0; i < kTrialCount; i++) {
FILE *fp = fopen(filename_, "rb"); FILE *fp = fopen(filename_, "rb");
ASSERT_EQ(length_, fread(temp_, 1, length_, fp)); ASSERT_EQ(length_, fread(temp_, 1, length_, fp));
EXPECT_EQ(checkSum_, CheckSum()); EXPECT_EQ(checkSum_, CheckSum());
fclose(fp); fclose(fp);
} }
} }
#ifdef _MSC_VER #ifdef _MSC_VER
TEST_F(Platform, read) { TEST_F(Platform, read) {
for (int i = 0; i < kTrialCount; i++) { for (int i = 0; i < kTrialCount; i++) {
int fd = _open(filename_, _O_BINARY | _O_RDONLY); int fd = _open(filename_, _O_BINARY | _O_RDONLY);
ASSERT_NE(-1, fd); ASSERT_NE(-1, fd);
ASSERT_EQ(length_, _read(fd, temp_, length_)); ASSERT_EQ(length_, _read(fd, temp_, length_));
EXPECT_EQ(checkSum_, CheckSum()); EXPECT_EQ(checkSum_, CheckSum());
_close(fd); _close(fd);
} }
} }
#else #else
TEST_F(Platform, read) { TEST_F(Platform, read) {
for (int i = 0; i < kTrialCount; i++) { for (int i = 0; i < kTrialCount; i++) {
int fd = open(filename_, O_RDONLY); int fd = open(filename_, O_RDONLY);
ASSERT_NE(-1, fd); ASSERT_NE(-1, fd);
ASSERT_EQ(length_, read(fd, temp_, length_)); ASSERT_EQ(length_, read(fd, temp_, length_));
EXPECT_EQ(checkSum_, CheckSum()); EXPECT_EQ(checkSum_, CheckSum());
close(fd); close(fd);
} }
} }
#endif #endif
#ifdef _WIN32 #ifdef _WIN32
TEST_F(Platform, MapViewOfFile) { TEST_F(Platform, MapViewOfFile) {
for (int i = 0; i < kTrialCount; i++) { for (int i = 0; i < kTrialCount; i++) {
HANDLE file = CreateFile(filename_, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); HANDLE file = CreateFile(filename_, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
ASSERT_NE(INVALID_HANDLE_VALUE, file); ASSERT_NE(INVALID_HANDLE_VALUE, file);
HANDLE mapObject = CreateFileMapping(file, NULL, PAGE_READONLY, 0, length_, NULL); HANDLE mapObject = CreateFileMapping(file, NULL, PAGE_READONLY, 0, length_, NULL);
ASSERT_NE(INVALID_HANDLE_VALUE, mapObject); ASSERT_NE(INVALID_HANDLE_VALUE, mapObject);
void *p = MapViewOfFile(mapObject, FILE_MAP_READ, 0, 0, length_); void *p = MapViewOfFile(mapObject, FILE_MAP_READ, 0, 0, length_);
ASSERT_TRUE(p != NULL); ASSERT_TRUE(p != NULL);
EXPECT_EQ(checkSum_, CheckSum()); EXPECT_EQ(checkSum_, CheckSum());
ASSERT_TRUE(UnmapViewOfFile(p) == TRUE); ASSERT_TRUE(UnmapViewOfFile(p) == TRUE);
ASSERT_TRUE(CloseHandle(mapObject) == TRUE); ASSERT_TRUE(CloseHandle(mapObject) == TRUE);
ASSERT_TRUE(CloseHandle(file) == TRUE); ASSERT_TRUE(CloseHandle(file) == TRUE);
} }
} }
#endif #endif
#ifdef _POSIX_MAPPED_FILES #ifdef _POSIX_MAPPED_FILES
TEST_F(Platform, mmap) { TEST_F(Platform, mmap) {
for (int i = 0; i < kTrialCount; i++) { for (int i = 0; i < kTrialCount; i++) {
int fd = open(filename_, O_RDONLY); int fd = open(filename_, O_RDONLY);
ASSERT_NE(-1, fd); ASSERT_NE(-1, fd);
void *p = mmap(NULL, length_, PROT_READ, MAP_PRIVATE, fd, 0); void *p = mmap(NULL, length_, PROT_READ, MAP_PRIVATE, fd, 0);
ASSERT_TRUE(p != NULL); ASSERT_TRUE(p != NULL);
EXPECT_EQ(checkSum_, CheckSum()); EXPECT_EQ(checkSum_, CheckSum());
munmap(p, length_); munmap(p, length_);
close(fd); close(fd);
} }
} }
#endif #endif

View File

@ -1,3 +1,23 @@
// Copyright (C) 2011 Milo Yip
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "perftest.h" #include "perftest.h"
#if TEST_RAPIDJSON #if TEST_RAPIDJSON
@ -21,162 +41,162 @@ using namespace rapidjson;
class RapidJson : public PerfTest { class RapidJson : public PerfTest {
public: public:
RapidJson() : temp_(), doc_() {} RapidJson() : temp_(), doc_() {}
virtual void SetUp() { virtual void SetUp() {
PerfTest::SetUp(); PerfTest::SetUp();
// temp buffer for insitu parsing. // temp buffer for insitu parsing.
temp_ = (char *)malloc(length_ + 1); temp_ = (char *)malloc(length_ + 1);
// Parse as a document // Parse as a document
EXPECT_FALSE(doc_.Parse(json_).IsNull()); EXPECT_FALSE(doc_.Parse(json_).IsNull());
} }
virtual void TearDown() { virtual void TearDown() {
PerfTest::TearDown(); PerfTest::TearDown();
free(temp_); free(temp_);
} }
private: private:
RapidJson(const RapidJson&); RapidJson(const RapidJson&);
RapidJson& operator=(const RapidJson&); RapidJson& operator=(const RapidJson&);
protected: protected:
char *temp_; char *temp_;
Document doc_; Document doc_;
}; };
TEST_F(RapidJson, SIMD_SUFFIX(ReaderParseInsitu_DummyHandler)) { TEST_F(RapidJson, SIMD_SUFFIX(ReaderParseInsitu_DummyHandler)) {
for (size_t i = 0; i < kTrialCount; i++) { for (size_t i = 0; i < kTrialCount; i++) {
memcpy(temp_, json_, length_ + 1); memcpy(temp_, json_, length_ + 1);
InsituStringStream s(temp_); InsituStringStream s(temp_);
BaseReaderHandler<> h; BaseReaderHandler<> h;
Reader reader; Reader reader;
EXPECT_TRUE(reader.Parse<kParseInsituFlag>(s, h)); EXPECT_TRUE(reader.Parse<kParseInsituFlag>(s, h));
} }
} }
TEST_F(RapidJson, SIMD_SUFFIX(ReaderParseInsitu_DummyHandler_ValidateEncoding)) { TEST_F(RapidJson, SIMD_SUFFIX(ReaderParseInsitu_DummyHandler_ValidateEncoding)) {
for (size_t i = 0; i < kTrialCount; i++) { for (size_t i = 0; i < kTrialCount; i++) {
memcpy(temp_, json_, length_ + 1); memcpy(temp_, json_, length_ + 1);
InsituStringStream s(temp_); InsituStringStream s(temp_);
BaseReaderHandler<> h; BaseReaderHandler<> h;
Reader reader; Reader reader;
EXPECT_TRUE(reader.Parse<kParseInsituFlag | kParseValidateEncodingFlag>(s, h)); EXPECT_TRUE(reader.Parse<kParseInsituFlag | kParseValidateEncodingFlag>(s, h));
} }
} }
TEST_F(RapidJson, SIMD_SUFFIX(ReaderParse_DummyHandler)) { TEST_F(RapidJson, SIMD_SUFFIX(ReaderParse_DummyHandler)) {
for (size_t i = 0; i < kTrialCount; i++) { for (size_t i = 0; i < kTrialCount; i++) {
StringStream s(json_); StringStream s(json_);
BaseReaderHandler<> h; BaseReaderHandler<> h;
Reader reader; Reader reader;
EXPECT_TRUE(reader.Parse(s, h)); EXPECT_TRUE(reader.Parse(s, h));
} }
} }
TEST_F(RapidJson, SIMD_SUFFIX(ReaderParseIterative_DummyHandler)) { TEST_F(RapidJson, SIMD_SUFFIX(ReaderParseIterative_DummyHandler)) {
for (size_t i = 0; i < kTrialCount; i++) { for (size_t i = 0; i < kTrialCount; i++) {
StringStream s(json_); StringStream s(json_);
BaseReaderHandler<> h; BaseReaderHandler<> h;
Reader reader; Reader reader;
EXPECT_TRUE(reader.Parse<kParseIterativeFlag>(s, h)); EXPECT_TRUE(reader.Parse<kParseIterativeFlag>(s, h));
} }
} }
TEST_F(RapidJson, SIMD_SUFFIX(ReaderParseIterativeInsitu_DummyHandler)) { TEST_F(RapidJson, SIMD_SUFFIX(ReaderParseIterativeInsitu_DummyHandler)) {
for (size_t i = 0; i < kTrialCount; i++) { for (size_t i = 0; i < kTrialCount; i++) {
memcpy(temp_, json_, length_ + 1); memcpy(temp_, json_, length_ + 1);
InsituStringStream s(temp_); InsituStringStream s(temp_);
BaseReaderHandler<> h; BaseReaderHandler<> h;
Reader reader; Reader reader;
EXPECT_TRUE(reader.Parse<kParseIterativeFlag|kParseInsituFlag>(s, h)); EXPECT_TRUE(reader.Parse<kParseIterativeFlag|kParseInsituFlag>(s, h));
} }
} }
TEST_F(RapidJson, SIMD_SUFFIX(ReaderParse_DummyHandler_ValidateEncoding)) { TEST_F(RapidJson, SIMD_SUFFIX(ReaderParse_DummyHandler_ValidateEncoding)) {
for (size_t i = 0; i < kTrialCount; i++) { for (size_t i = 0; i < kTrialCount; i++) {
StringStream s(json_); StringStream s(json_);
BaseReaderHandler<> h; BaseReaderHandler<> h;
Reader reader; Reader reader;
EXPECT_TRUE(reader.Parse<kParseValidateEncodingFlag>(s, h)); EXPECT_TRUE(reader.Parse<kParseValidateEncodingFlag>(s, h));
} }
} }
TEST_F(RapidJson, SIMD_SUFFIX(DocumentParseInsitu_MemoryPoolAllocator)) { TEST_F(RapidJson, SIMD_SUFFIX(DocumentParseInsitu_MemoryPoolAllocator)) {
for (size_t i = 0; i < kTrialCount; i++) { for (size_t i = 0; i < kTrialCount; i++) {
memcpy(temp_, json_, length_ + 1); memcpy(temp_, json_, length_ + 1);
Document doc; Document doc;
doc.ParseInsitu(temp_); doc.ParseInsitu(temp_);
ASSERT_TRUE(doc.IsObject()); ASSERT_TRUE(doc.IsObject());
} }
} }
TEST_F(RapidJson, SIMD_SUFFIX(DocumentParseIterativeInsitu_MemoryPoolAllocator)) { TEST_F(RapidJson, SIMD_SUFFIX(DocumentParseIterativeInsitu_MemoryPoolAllocator)) {
for (size_t i = 0; i < kTrialCount; i++) { for (size_t i = 0; i < kTrialCount; i++) {
memcpy(temp_, json_, length_ + 1); memcpy(temp_, json_, length_ + 1);
Document doc; Document doc;
doc.ParseInsitu<kParseIterativeFlag>(temp_); doc.ParseInsitu<kParseIterativeFlag>(temp_);
ASSERT_TRUE(doc.IsObject()); ASSERT_TRUE(doc.IsObject());
} }
} }
TEST_F(RapidJson, SIMD_SUFFIX(DocumentParse_MemoryPoolAllocator)) { TEST_F(RapidJson, SIMD_SUFFIX(DocumentParse_MemoryPoolAllocator)) {
for (size_t i = 0; i < kTrialCount; i++) { for (size_t i = 0; i < kTrialCount; i++) {
Document doc; Document doc;
doc.Parse(json_); doc.Parse(json_);
ASSERT_TRUE(doc.IsObject()); ASSERT_TRUE(doc.IsObject());
} }
} }
TEST_F(RapidJson, SIMD_SUFFIX(DocumentParseIterative_MemoryPoolAllocator)) { TEST_F(RapidJson, SIMD_SUFFIX(DocumentParseIterative_MemoryPoolAllocator)) {
for (size_t i = 0; i < kTrialCount; i++) { for (size_t i = 0; i < kTrialCount; i++) {
Document doc; Document doc;
doc.Parse<kParseIterativeFlag>(json_); doc.Parse<kParseIterativeFlag>(json_);
ASSERT_TRUE(doc.IsObject()); ASSERT_TRUE(doc.IsObject());
} }
} }
TEST_F(RapidJson, SIMD_SUFFIX(DocumentParse_CrtAllocator)) { TEST_F(RapidJson, SIMD_SUFFIX(DocumentParse_CrtAllocator)) {
for (size_t i = 0; i < kTrialCount; i++) { for (size_t i = 0; i < kTrialCount; i++) {
memcpy(temp_, json_, length_ + 1); memcpy(temp_, json_, length_ + 1);
GenericDocument<UTF8<>, CrtAllocator> doc; GenericDocument<UTF8<>, CrtAllocator> doc;
doc.Parse(temp_); doc.Parse(temp_);
ASSERT_TRUE(doc.IsObject()); ASSERT_TRUE(doc.IsObject());
} }
} }
template<typename T> template<typename T>
size_t Traverse(const T& value) { size_t Traverse(const T& value) {
size_t count = 1; size_t count = 1;
switch(value.GetType()) { switch(value.GetType()) {
case kObjectType: case kObjectType:
for (typename T::ConstMemberIterator itr = value.MemberBegin(); itr != value.MemberEnd(); ++itr) { for (typename T::ConstMemberIterator itr = value.MemberBegin(); itr != value.MemberEnd(); ++itr) {
count++; // name count++; // name
count += Traverse(itr->value); count += Traverse(itr->value);
} }
break; break;
case kArrayType: case kArrayType:
for (typename T::ConstValueIterator itr = value.Begin(); itr != value.End(); ++itr) for (typename T::ConstValueIterator itr = value.Begin(); itr != value.End(); ++itr)
count += Traverse(*itr); count += Traverse(*itr);
break; break;
default: default:
// Do nothing. // Do nothing.
break; break;
} }
return count; return count;
} }
TEST_F(RapidJson, DocumentTraverse) { TEST_F(RapidJson, DocumentTraverse) {
for (size_t i = 0; i < kTrialCount; i++) { for (size_t i = 0; i < kTrialCount; i++) {
size_t count = Traverse(doc_); size_t count = Traverse(doc_);
EXPECT_EQ(4339u, count); EXPECT_EQ(4339u, count);
//if (i == 0) //if (i == 0)
// std::cout << count << std::endl; // std::cout << count << std::endl;
} }
} }
#ifdef __GNUC__ #ifdef __GNUC__
@ -185,12 +205,12 @@ RAPIDJSON_DIAG_OFF(effc++)
#endif #endif
struct ValueCounter : public BaseReaderHandler<> { struct ValueCounter : public BaseReaderHandler<> {
ValueCounter() : count_(1) {} // root ValueCounter() : count_(1) {} // root
bool EndObject(SizeType memberCount) { count_ += memberCount * 2; return true; } bool EndObject(SizeType memberCount) { count_ += memberCount * 2; return true; }
bool EndArray(SizeType elementCount) { count_ += elementCount; return true; } bool EndArray(SizeType elementCount) { count_ += elementCount; return true; }
SizeType count_; SizeType count_;
}; };
#ifdef __GNUC__ #ifdef __GNUC__
@ -198,113 +218,113 @@ RAPIDJSON_DIAG_POP
#endif #endif
TEST_F(RapidJson, DocumentAccept) { TEST_F(RapidJson, DocumentAccept) {
for (size_t i = 0; i < kTrialCount; i++) { for (size_t i = 0; i < kTrialCount; i++) {
ValueCounter counter; ValueCounter counter;
doc_.Accept(counter); doc_.Accept(counter);
EXPECT_EQ(4339u, counter.count_); EXPECT_EQ(4339u, counter.count_);
} }
} }
struct NullStream { struct NullStream {
NullStream() /*: length_(0)*/ {} NullStream() /*: length_(0)*/ {}
void Put(char) { /*++length_;*/ } void Put(char) { /*++length_;*/ }
void Flush() {} void Flush() {}
//size_t length_; //size_t length_;
}; };
TEST_F(RapidJson, Writer_NullStream) { TEST_F(RapidJson, Writer_NullStream) {
for (size_t i = 0; i < kTrialCount; i++) { for (size_t i = 0; i < kTrialCount; i++) {
NullStream s; NullStream s;
Writer<NullStream> writer(s); Writer<NullStream> writer(s);
doc_.Accept(writer); doc_.Accept(writer);
//if (i == 0) //if (i == 0)
// std::cout << s.length_ << std::endl; // std::cout << s.length_ << std::endl;
} }
} }
TEST_F(RapidJson, Writer_StringBuffer) { TEST_F(RapidJson, Writer_StringBuffer) {
for (size_t i = 0; i < kTrialCount; i++) { for (size_t i = 0; i < kTrialCount; i++) {
StringBuffer s(0, 1024 * 1024); StringBuffer s(0, 1024 * 1024);
Writer<StringBuffer> writer(s); Writer<StringBuffer> writer(s);
doc_.Accept(writer); doc_.Accept(writer);
const char* str = s.GetString(); const char* str = s.GetString();
(void)str; (void)str;
//if (i == 0) //if (i == 0)
// std::cout << strlen(str) << std::endl; // std::cout << strlen(str) << std::endl;
} }
} }
TEST_F(RapidJson, PrettyWriter_StringBuffer) { TEST_F(RapidJson, PrettyWriter_StringBuffer) {
for (size_t i = 0; i < kTrialCount; i++) { for (size_t i = 0; i < kTrialCount; i++) {
StringBuffer s(0, 2048 * 1024); StringBuffer s(0, 2048 * 1024);
PrettyWriter<StringBuffer> writer(s); PrettyWriter<StringBuffer> writer(s);
writer.SetIndent(' ', 1); writer.SetIndent(' ', 1);
doc_.Accept(writer); doc_.Accept(writer);
const char* str = s.GetString(); const char* str = s.GetString();
(void)str; (void)str;
//if (i == 0) //if (i == 0)
// std::cout << strlen(str) << std::endl; // std::cout << strlen(str) << std::endl;
} }
} }
TEST_F(RapidJson, internal_Pow10) { TEST_F(RapidJson, internal_Pow10) {
double sum = 0; double sum = 0;
for (size_t i = 0; i < kTrialCount * kTrialCount; i++) for (size_t i = 0; i < kTrialCount * kTrialCount; i++)
sum += internal::Pow10(int(i & 255)); sum += internal::Pow10(int(i & 255));
EXPECT_GT(sum, 0.0); EXPECT_GT(sum, 0.0);
} }
TEST_F(RapidJson, SIMD_SUFFIX(Whitespace)) { TEST_F(RapidJson, SIMD_SUFFIX(Whitespace)) {
for (size_t i = 0; i < kTrialCount; i++) { for (size_t i = 0; i < kTrialCount; i++) {
Document doc; Document doc;
ASSERT_TRUE(doc.Parse(whitespace_).IsArray()); ASSERT_TRUE(doc.Parse(whitespace_).IsArray());
} }
} }
TEST_F(RapidJson, UTF8_Validate) { TEST_F(RapidJson, UTF8_Validate) {
NullStream os; NullStream os;
for (size_t i = 0; i < kTrialCount; i++) { for (size_t i = 0; i < kTrialCount; i++) {
StringStream is(json_); StringStream is(json_);
bool result = true; bool result = true;
while (is.Peek() != '\0') while (is.Peek() != '\0')
result &= UTF8<>::Validate(is, os); result &= UTF8<>::Validate(is, os);
EXPECT_TRUE(result); EXPECT_TRUE(result);
} }
} }
// Depreciated. // Depreciated.
//TEST_F(RapidJson, FileStream_Read) { //TEST_F(RapidJson, FileStream_Read) {
// for (size_t i = 0; i < kTrialCount; i++) { // for (size_t i = 0; i < kTrialCount; i++) {
// FILE *fp = fopen(filename_, "rb"); // FILE *fp = fopen(filename_, "rb");
// FileStream s(fp); // FileStream s(fp);
// while (s.Take() != '\0') // while (s.Take() != '\0')
// ; // ;
// fclose(fp); // fclose(fp);
// } // }
//} //}
TEST_F(RapidJson, FileReadStream) { TEST_F(RapidJson, FileReadStream) {
for (size_t i = 0; i < kTrialCount; i++) { for (size_t i = 0; i < kTrialCount; i++) {
FILE *fp = fopen(filename_, "rb"); FILE *fp = fopen(filename_, "rb");
char buffer[65536]; char buffer[65536];
FileReadStream s(fp, buffer, sizeof(buffer)); FileReadStream s(fp, buffer, sizeof(buffer));
while (s.Take() != '\0') while (s.Take() != '\0')
; ;
fclose(fp); fclose(fp);
} }
} }
TEST_F(RapidJson, SIMD_SUFFIX(ReaderParse_DummyHandler_FileReadStream)) { TEST_F(RapidJson, SIMD_SUFFIX(ReaderParse_DummyHandler_FileReadStream)) {
for (size_t i = 0; i < kTrialCount; i++) { for (size_t i = 0; i < kTrialCount; i++) {
FILE *fp = fopen(filename_, "rb"); FILE *fp = fopen(filename_, "rb");
char buffer[65536]; char buffer[65536];
FileReadStream s(fp, buffer, sizeof(buffer)); FileReadStream s(fp, buffer, sizeof(buffer));
BaseReaderHandler<> h; BaseReaderHandler<> h;
Reader reader; Reader reader;
reader.Parse(s, h); reader.Parse(s, h);
fclose(fp); fclose(fp);
} }
} }
#endif // TEST_RAPIDJSON #endif // TEST_RAPIDJSON

View File

@ -1,3 +1,23 @@
// Copyright (C) 2011 Milo Yip
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "perftest.h" #include "perftest.h"
#if TEST_ULTRAJSON #if TEST_ULTRAJSON
@ -13,52 +33,52 @@ static char dummy = 0;
static void Object_objectAddKey(JSOBJ obj, JSOBJ name, JSOBJ value) {} static void Object_objectAddKey(JSOBJ obj, JSOBJ name, JSOBJ value) {}
static void Object_arrayAddItem(JSOBJ obj, JSOBJ value) {} static void Object_arrayAddItem(JSOBJ obj, JSOBJ value) {}
static JSOBJ Object_newString(wchar_t *start, wchar_t *end) { return &dummy; } static JSOBJ Object_newString(wchar_t *start, wchar_t *end) { return &dummy; }
static JSOBJ Object_newTrue(void) { return &dummy; } static JSOBJ Object_newTrue(void) { return &dummy; }
static JSOBJ Object_newFalse(void) { return &dummy; } static JSOBJ Object_newFalse(void) { return &dummy; }
static JSOBJ Object_newNull(void) { return &dummy; } static JSOBJ Object_newNull(void) { return &dummy; }
static JSOBJ Object_newObject(void) { return &dummy; } static JSOBJ Object_newObject(void) { return &dummy; }
static JSOBJ Object_newArray(void) { return &dummy; } static JSOBJ Object_newArray(void) { return &dummy; }
static JSOBJ Object_newInteger(JSINT32 value) { return &dummy; } static JSOBJ Object_newInteger(JSINT32 value) { return &dummy; }
static JSOBJ Object_newLong(JSINT64 value) { return &dummy; } static JSOBJ Object_newLong(JSINT64 value) { return &dummy; }
static JSOBJ Object_newDouble(double value) { return &dummy; } static JSOBJ Object_newDouble(double value) { return &dummy; }
static void Object_releaseObject(JSOBJ obj) {} static void Object_releaseObject(JSOBJ obj) {}
static JSONObjectDecoder decoder = { static JSONObjectDecoder decoder = {
Object_newString, Object_newString,
Object_objectAddKey, Object_objectAddKey,
Object_arrayAddItem, Object_arrayAddItem,
Object_newTrue, Object_newTrue,
Object_newFalse, Object_newFalse,
Object_newNull, Object_newNull,
Object_newObject, Object_newObject,
Object_newArray, Object_newArray,
Object_newInteger, Object_newInteger,
Object_newLong, Object_newLong,
Object_newDouble, Object_newDouble,
Object_releaseObject, Object_releaseObject,
malloc, malloc,
free, free,
realloc realloc
}; };
TEST_F(UltraJson, Decode) { TEST_F(UltraJson, Decode) {
for (int i = 0; i < kTrialCount; i++) { for (int i = 0; i < kTrialCount; i++) {
decoder.errorStr = NULL; decoder.errorStr = NULL;
decoder.errorOffset = NULL; decoder.errorOffset = NULL;
void *ret = JSON_DecodeObject(&decoder, json_, length_); void *ret = JSON_DecodeObject(&decoder, json_, length_);
ASSERT_TRUE(ret != 0); ASSERT_TRUE(ret != 0);
} }
} }
TEST_F(UltraJson, Whitespace) { TEST_F(UltraJson, Whitespace) {
for (int i = 0; i < kTrialCount; i++) { for (int i = 0; i < kTrialCount; i++) {
decoder.errorStr = NULL; decoder.errorStr = NULL;
decoder.errorOffset = NULL; decoder.errorOffset = NULL;
void *ret = JSON_DecodeObject(&decoder, whitespace_, whitespace_length_); void *ret = JSON_DecodeObject(&decoder, whitespace_, whitespace_length_);
ASSERT_TRUE(ret != 0); ASSERT_TRUE(ret != 0);
} }
} }
#endif // TEST_ULTRAJSON #endif // TEST_ULTRAJSON

View File

@ -1,3 +1,23 @@
// Copyright (C) 2011 Milo Yip
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "perftest.h" #include "perftest.h"
#if TEST_YAJL #if TEST_YAJL
@ -10,19 +30,19 @@ extern "C" {
class Yajl : public PerfTest { class Yajl : public PerfTest {
public: public:
virtual void SetUp() { virtual void SetUp() {
PerfTest::SetUp(); PerfTest::SetUp();
root_ = yajl_tree_parse(json_, NULL, 0); root_ = yajl_tree_parse(json_, NULL, 0);
ASSERT_TRUE(root_ != NULL); ASSERT_TRUE(root_ != NULL);
} }
virtual void TearDown() { virtual void TearDown() {
PerfTest::TearDown(); PerfTest::TearDown();
yajl_tree_free(root_); yajl_tree_free(root_);
} }
protected: protected:
yajl_val root_; yajl_val root_;
}; };
static int null_null(void *) { return 1; } static int null_null(void *) { return 1; }
@ -37,152 +57,152 @@ static int null_start_array(void*) { return 1; }
static int null_end_array(void *) { return 1; } static int null_end_array(void *) { return 1; }
static yajl_callbacks nullcallbacks = { static yajl_callbacks nullcallbacks = {
null_null, null_null,
null_boolean, null_boolean,
null_integer, null_integer,
null_double, null_double,
NULL, // yajl_number(). Here we want to test full-parsing performance. NULL, // yajl_number(). Here we want to test full-parsing performance.
null_string, null_string,
null_start_map, null_start_map,
null_map_key, null_map_key,
null_end_map, null_end_map,
null_start_array, null_start_array,
null_end_array null_end_array
}; };
TEST_F(Yajl, yajl_parse_nullcallbacks) { TEST_F(Yajl, yajl_parse_nullcallbacks) {
for (int i = 0; i < kTrialCount; i++) { for (int i = 0; i < kTrialCount; i++) {
yajl_handle hand = yajl_alloc(&nullcallbacks, NULL, NULL); yajl_handle hand = yajl_alloc(&nullcallbacks, NULL, NULL);
yajl_status stat = yajl_parse(hand, (unsigned char*)json_, length_); yajl_status stat = yajl_parse(hand, (unsigned char*)json_, length_);
//ASSERT_EQ(yajl_status_ok, stat); //ASSERT_EQ(yajl_status_ok, stat);
if (stat != yajl_status_ok) { if (stat != yajl_status_ok) {
unsigned char * str = yajl_get_error(hand, 1, (unsigned char*)json_, length_); unsigned char * str = yajl_get_error(hand, 1, (unsigned char*)json_, length_);
fprintf(stderr, "%s", (const char *) str); fprintf(stderr, "%s", (const char *) str);
} }
stat = yajl_complete_parse(hand); stat = yajl_complete_parse(hand);
ASSERT_EQ(yajl_status_ok, stat); ASSERT_EQ(yajl_status_ok, stat);
yajl_free(hand); yajl_free(hand);
} }
} }
TEST_F(Yajl, yajl_tree_parse) { TEST_F(Yajl, yajl_tree_parse) {
for (int i = 0; i < kTrialCount; i++) { for (int i = 0; i < kTrialCount; i++) {
yajl_val root = yajl_tree_parse(json_, NULL, 0); yajl_val root = yajl_tree_parse(json_, NULL, 0);
ASSERT_TRUE(root != NULL); ASSERT_TRUE(root != NULL);
yajl_tree_free(root); yajl_tree_free(root);
} }
} }
yajl_gen_status GenVal(yajl_gen g, yajl_val v) { yajl_gen_status GenVal(yajl_gen g, yajl_val v) {
yajl_gen_status status; yajl_gen_status status;
switch (v->type) { switch (v->type) {
case yajl_t_string: return yajl_gen_string(g, (unsigned char*)v->u.string, strlen(v->u.string)); case yajl_t_string: return yajl_gen_string(g, (unsigned char*)v->u.string, strlen(v->u.string));
case yajl_t_number: case yajl_t_number:
{ {
char buffer[100]; char buffer[100];
char *num = buffer; char *num = buffer;
size_t len; size_t len;
//if (YAJL_IS_INTEGER(v)) // buggy //if (YAJL_IS_INTEGER(v)) // buggy
if (v->u.number.flags & YAJL_NUMBER_INT_VALID) if (v->u.number.flags & YAJL_NUMBER_INT_VALID)
#if _MSC_VER #if _MSC_VER
len = sprintf(num, "%I64d", YAJL_GET_INTEGER(v)); len = sprintf(num, "%I64d", YAJL_GET_INTEGER(v));
#else #else
len = sprintf(num, "%lld", YAJL_GET_INTEGER(v)); len = sprintf(num, "%lld", YAJL_GET_INTEGER(v));
#endif #endif
//else if (YAJL_IS_DOUBLE(v)) // buggy //else if (YAJL_IS_DOUBLE(v)) // buggy
else if (v->u.number.flags & YAJL_NUMBER_DOUBLE_VALID) else if (v->u.number.flags & YAJL_NUMBER_DOUBLE_VALID)
len = sprintf(num, "%g", YAJL_GET_DOUBLE(v)); len = sprintf(num, "%g", YAJL_GET_DOUBLE(v));
else { else {
num = YAJL_GET_NUMBER(v); num = YAJL_GET_NUMBER(v);
len = strlen(buffer); len = strlen(buffer);
} }
return yajl_gen_number(g, num, len); return yajl_gen_number(g, num, len);
} }
case yajl_t_object: case yajl_t_object:
status = yajl_gen_map_open(g); status = yajl_gen_map_open(g);
if (status != yajl_gen_status_ok) if (status != yajl_gen_status_ok)
return status; return status;
for (size_t i = 0; i < v->u.object.len; i++) { for (size_t i = 0; i < v->u.object.len; i++) {
status = yajl_gen_string(g, (unsigned char *)v->u.object.keys[i], strlen(v->u.object.keys[i])); status = yajl_gen_string(g, (unsigned char *)v->u.object.keys[i], strlen(v->u.object.keys[i]));
if (status != yajl_gen_status_ok) if (status != yajl_gen_status_ok)
return status; return status;
status = GenVal(g, v->u.object.values[i]); status = GenVal(g, v->u.object.values[i]);
if (status != yajl_gen_status_ok) if (status != yajl_gen_status_ok)
return status; return status;
} }
return yajl_gen_map_close(g); return yajl_gen_map_close(g);
case yajl_t_array: case yajl_t_array:
status = yajl_gen_array_open(g); status = yajl_gen_array_open(g);
if (status != yajl_gen_status_ok) if (status != yajl_gen_status_ok)
return status; return status;
for (size_t i = 0; i < v->u.array.len; i++) { for (size_t i = 0; i < v->u.array.len; i++) {
status = GenVal(g, v->u.array.values[i]); status = GenVal(g, v->u.array.values[i]);
if (status != yajl_gen_status_ok) if (status != yajl_gen_status_ok)
return status; return status;
} }
return yajl_gen_array_close(g); return yajl_gen_array_close(g);
case yajl_t_true: return yajl_gen_bool(g, 1); case yajl_t_true: return yajl_gen_bool(g, 1);
case yajl_t_false: return yajl_gen_bool(g, 0); case yajl_t_false: return yajl_gen_bool(g, 0);
case yajl_t_null: return yajl_gen_null(g); case yajl_t_null: return yajl_gen_null(g);
} }
return yajl_gen_in_error_state; return yajl_gen_in_error_state;
} }
TEST_F(Yajl, yajl_gen) { TEST_F(Yajl, yajl_gen) {
for (int i = 0; i < kTrialCount; i++) { for (int i = 0; i < kTrialCount; i++) {
yajl_gen g = yajl_gen_alloc(NULL); yajl_gen g = yajl_gen_alloc(NULL);
yajl_gen_status status = GenVal(g, root_); yajl_gen_status status = GenVal(g, root_);
if (status != yajl_gen_status_ok) { if (status != yajl_gen_status_ok) {
std::cout << "gen error: " << status << std::endl; std::cout << "gen error: " << status << std::endl;
FAIL(); FAIL();
} }
const unsigned char * buf; const unsigned char * buf;
size_t len; size_t len;
status = yajl_gen_get_buf(g, &buf, &len); status = yajl_gen_get_buf(g, &buf, &len);
ASSERT_EQ(yajl_gen_status_ok, status); ASSERT_EQ(yajl_gen_status_ok, status);
//if (i == 0) //if (i == 0)
// std::cout << len << std::endl; // std::cout << len << std::endl;
yajl_gen_free(g); yajl_gen_free(g);
} }
} }
TEST_F(Yajl, yajl_gen_beautify) { TEST_F(Yajl, yajl_gen_beautify) {
for (int i = 0; i < kTrialCount; i++) { for (int i = 0; i < kTrialCount; i++) {
yajl_gen g = yajl_gen_alloc(NULL); yajl_gen g = yajl_gen_alloc(NULL);
yajl_gen_config(g, yajl_gen_beautify, 1); yajl_gen_config(g, yajl_gen_beautify, 1);
yajl_gen_config(g, yajl_gen_indent_string, " "); yajl_gen_config(g, yajl_gen_indent_string, " ");
yajl_gen_status status = GenVal(g, root_); yajl_gen_status status = GenVal(g, root_);
if (status != yajl_gen_status_ok) { if (status != yajl_gen_status_ok) {
std::cout << "gen error: " << status << std::endl; std::cout << "gen error: " << status << std::endl;
FAIL(); FAIL();
} }
const unsigned char * buf; const unsigned char * buf;
size_t len; size_t len;
status = yajl_gen_get_buf(g, &buf, &len); status = yajl_gen_get_buf(g, &buf, &len);
ASSERT_EQ(yajl_gen_status_ok, status); ASSERT_EQ(yajl_gen_status_ok, status);
//if (i == 0) //if (i == 0)
// std::cout << len << std::endl; // std::cout << len << std::endl;
yajl_gen_free(g); yajl_gen_free(g);
} }
} }
TEST_F(Yajl, Whitespace) { TEST_F(Yajl, Whitespace) {
for (int i = 0; i < kTrialCount; i++) { for (int i = 0; i < kTrialCount; i++) {
yajl_val root = yajl_tree_parse(whitespace_, NULL, 0); yajl_val root = yajl_tree_parse(whitespace_, NULL, 0);
ASSERT_TRUE(root != NULL); ASSERT_TRUE(root != NULL);
yajl_tree_free(root); yajl_tree_free(root);
} }
} }
#endif // TEST_YAJL #endif // TEST_YAJL

View File

@ -1,3 +1,23 @@
// Copyright (C) 2011 Milo Yip
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "unittest.h" #include "unittest.h"
#include "rapidjson/document.h" #include "rapidjson/document.h"
#include "rapidjson/writer.h" #include "rapidjson/writer.h"
@ -9,198 +29,198 @@
using namespace rapidjson; using namespace rapidjson;
TEST(Document, Parse) { TEST(Document, Parse) {
Document doc; Document doc;
doc.Parse(" { \"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()); EXPECT_TRUE(doc.IsObject());
EXPECT_TRUE(doc.HasMember("hello")); EXPECT_TRUE(doc.HasMember("hello"));
Value& hello = doc["hello"]; Value& hello = doc["hello"];
EXPECT_TRUE(hello.IsString()); EXPECT_TRUE(hello.IsString());
EXPECT_STREQ("world", hello.GetString()); EXPECT_STREQ("world", hello.GetString());
EXPECT_TRUE(doc.HasMember("t")); EXPECT_TRUE(doc.HasMember("t"));
Value& t = doc["t"]; Value& t = doc["t"];
EXPECT_TRUE(t.IsTrue()); EXPECT_TRUE(t.IsTrue());
EXPECT_TRUE(doc.HasMember("f")); EXPECT_TRUE(doc.HasMember("f"));
Value& f = doc["f"]; Value& f = doc["f"];
EXPECT_TRUE(f.IsFalse()); EXPECT_TRUE(f.IsFalse());
EXPECT_TRUE(doc.HasMember("n")); EXPECT_TRUE(doc.HasMember("n"));
Value& n = doc["n"]; Value& n = doc["n"];
EXPECT_TRUE(n.IsNull()); EXPECT_TRUE(n.IsNull());
EXPECT_TRUE(doc.HasMember("i")); EXPECT_TRUE(doc.HasMember("i"));
Value& i = doc["i"]; Value& i = doc["i"];
EXPECT_TRUE(i.IsNumber()); EXPECT_TRUE(i.IsNumber());
EXPECT_EQ(123, i.GetInt()); EXPECT_EQ(123, i.GetInt());
EXPECT_TRUE(doc.HasMember("pi")); EXPECT_TRUE(doc.HasMember("pi"));
Value& pi = doc["pi"]; Value& pi = doc["pi"];
EXPECT_TRUE(pi.IsNumber()); EXPECT_TRUE(pi.IsNumber());
EXPECT_EQ(3.1416, pi.GetDouble()); EXPECT_EQ(3.1416, pi.GetDouble());
EXPECT_TRUE(doc.HasMember("a")); EXPECT_TRUE(doc.HasMember("a"));
Value& a = doc["a"]; Value& a = doc["a"];
EXPECT_TRUE(a.IsArray()); EXPECT_TRUE(a.IsArray());
EXPECT_EQ(4u, a.Size()); EXPECT_EQ(4u, a.Size());
for (SizeType i = 0; i < 4; i++) for (SizeType i = 0; i < 4; i++)
EXPECT_EQ(i + 1, a[i].GetUint()); EXPECT_EQ(i + 1, a[i].GetUint());
} }
static FILE* OpenEncodedFile(const char* filename) { static FILE* OpenEncodedFile(const char* filename) {
char buffer[1024]; char buffer[1024];
sprintf(buffer, "encodings/%s", filename); sprintf(buffer, "encodings/%s", filename);
FILE *fp = fopen(buffer, "rb"); FILE *fp = fopen(buffer, "rb");
if (!fp) { if (!fp) {
sprintf(buffer, "../../bin/encodings/%s", filename); sprintf(buffer, "../../bin/encodings/%s", filename);
fp = fopen(buffer, "rb"); fp = fopen(buffer, "rb");
} }
return fp; return fp;
} }
TEST(Document, ParseStream_EncodedInputStream) { TEST(Document, ParseStream_EncodedInputStream) {
// UTF8 -> UTF16 // UTF8 -> UTF16
FILE* fp = OpenEncodedFile("utf8.json"); FILE* fp = OpenEncodedFile("utf8.json");
char buffer[256]; char buffer[256];
FileReadStream bis(fp, buffer, sizeof(buffer)); FileReadStream bis(fp, buffer, sizeof(buffer));
EncodedInputStream<UTF8<>, FileReadStream> eis(bis); EncodedInputStream<UTF8<>, FileReadStream> eis(bis);
GenericDocument<UTF16<> > d; GenericDocument<UTF16<> > d;
d.ParseStream<0, UTF8<> >(eis); d.ParseStream<0, UTF8<> >(eis);
EXPECT_FALSE(d.HasParseError()); EXPECT_FALSE(d.HasParseError());
fclose(fp); fclose(fp);
wchar_t expected[] = L"I can eat glass and it doesn't hurt me."; wchar_t expected[] = L"I can eat glass and it doesn't hurt me.";
GenericValue<UTF16<> >& v = d[L"en"]; GenericValue<UTF16<> >& v = d[L"en"];
EXPECT_TRUE(v.IsString()); EXPECT_TRUE(v.IsString());
EXPECT_EQ(sizeof(expected) / sizeof(wchar_t) - 1, v.GetStringLength()); EXPECT_EQ(sizeof(expected) / sizeof(wchar_t) - 1, v.GetStringLength());
EXPECT_EQ(0, StrCmp(expected, v.GetString())); EXPECT_EQ(0, StrCmp(expected, v.GetString()));
// UTF16 -> UTF8 in memory // UTF16 -> UTF8 in memory
StringBuffer bos; StringBuffer bos;
typedef EncodedOutputStream<UTF8<>, StringBuffer> OutputStream; typedef EncodedOutputStream<UTF8<>, StringBuffer> OutputStream;
OutputStream eos(bos, false); // Not writing BOM OutputStream eos(bos, false); // Not writing BOM
Writer<OutputStream, UTF16<>, UTF8<> > writer(eos); Writer<OutputStream, UTF16<>, UTF8<> > writer(eos);
d.Accept(writer); d.Accept(writer);
{ {
// Condense the original file and compare. // Condense the original file and compare.
FILE *fp = OpenEncodedFile("utf8.json"); FILE *fp = OpenEncodedFile("utf8.json");
FileReadStream is(fp, buffer, sizeof(buffer)); FileReadStream is(fp, buffer, sizeof(buffer));
Reader reader; Reader reader;
StringBuffer bos2; StringBuffer bos2;
Writer<StringBuffer> writer(bos2); Writer<StringBuffer> writer(bos2);
reader.Parse(is, writer); reader.Parse(is, writer);
EXPECT_EQ(bos.GetSize(), bos2.GetSize()); EXPECT_EQ(bos.GetSize(), bos2.GetSize());
EXPECT_EQ(0, memcmp(bos.GetString(), bos2.GetString(), bos2.GetSize())); EXPECT_EQ(0, memcmp(bos.GetString(), bos2.GetString(), bos2.GetSize()));
} }
} }
TEST(Document, ParseStream_AutoUTFInputStream) { TEST(Document, ParseStream_AutoUTFInputStream) {
// Any -> UTF8 // Any -> UTF8
FILE* fp = OpenEncodedFile("utf32be.json"); FILE* fp = OpenEncodedFile("utf32be.json");
char buffer[256]; char buffer[256];
FileReadStream bis(fp, buffer, sizeof(buffer)); FileReadStream bis(fp, buffer, sizeof(buffer));
AutoUTFInputStream<unsigned, FileReadStream> eis(bis); AutoUTFInputStream<unsigned, FileReadStream> eis(bis);
Document d; Document d;
d.ParseStream<0, AutoUTF<unsigned> >(eis); d.ParseStream<0, AutoUTF<unsigned> >(eis);
EXPECT_FALSE(d.HasParseError()); EXPECT_FALSE(d.HasParseError());
fclose(fp); fclose(fp);
char expected[] = "I can eat glass and it doesn't hurt me."; char expected[] = "I can eat glass and it doesn't hurt me.";
Value& v = d["en"]; Value& v = d["en"];
EXPECT_TRUE(v.IsString()); EXPECT_TRUE(v.IsString());
EXPECT_EQ(sizeof(expected) - 1, v.GetStringLength()); EXPECT_EQ(sizeof(expected) - 1, v.GetStringLength());
EXPECT_EQ(0, StrCmp(expected, v.GetString())); EXPECT_EQ(0, StrCmp(expected, v.GetString()));
// UTF8 -> UTF8 in memory // UTF8 -> UTF8 in memory
StringBuffer bos; StringBuffer bos;
Writer<StringBuffer> writer(bos); Writer<StringBuffer> writer(bos);
d.Accept(writer); d.Accept(writer);
{ {
// Condense the original file and compare. // Condense the original file and compare.
FILE *fp = OpenEncodedFile("utf8.json"); FILE *fp = OpenEncodedFile("utf8.json");
FileReadStream is(fp, buffer, sizeof(buffer)); FileReadStream is(fp, buffer, sizeof(buffer));
Reader reader; Reader reader;
StringBuffer bos2; StringBuffer bos2;
Writer<StringBuffer> writer(bos2); Writer<StringBuffer> writer(bos2);
reader.Parse(is, writer); reader.Parse(is, writer);
EXPECT_EQ(bos.GetSize(), bos2.GetSize()); EXPECT_EQ(bos.GetSize(), bos2.GetSize());
EXPECT_EQ(0, memcmp(bos.GetString(), bos2.GetString(), bos2.GetSize())); EXPECT_EQ(0, memcmp(bos.GetString(), bos2.GetString(), bos2.GetSize()));
} }
} }
TEST(Document, Swap) { TEST(Document, Swap) {
Document d1; Document d1;
Document::AllocatorType& a = d1.GetAllocator(); Document::AllocatorType& a = d1.GetAllocator();
d1.SetArray().PushBack(1, a).PushBack(2, a); d1.SetArray().PushBack(1, a).PushBack(2, a);
Value o; Value o;
o.SetObject().AddMember("a", 1, a); o.SetObject().AddMember("a", 1, a);
// Swap between Document and Value // Swap between Document and Value
d1.Swap(o); d1.Swap(o);
EXPECT_TRUE(d1.IsObject()); EXPECT_TRUE(d1.IsObject());
EXPECT_TRUE(o.IsArray()); EXPECT_TRUE(o.IsArray());
// Swap between Document and Document // Swap between Document and Document
Document d2; Document d2;
d2.SetArray().PushBack(3, a); d2.SetArray().PushBack(3, a);
d1.Swap(d2); d1.Swap(d2);
EXPECT_TRUE(d1.IsArray()); EXPECT_TRUE(d1.IsArray());
EXPECT_TRUE(d2.IsObject()); EXPECT_TRUE(d2.IsObject());
} }
// This should be slow due to assignment in inner-loop. // This should be slow due to assignment in inner-loop.
struct OutputStringStream : public std::ostringstream { struct OutputStringStream : public std::ostringstream {
typedef char Ch; typedef char Ch;
void Put(char c) { void Put(char c) {
put(c); put(c);
} }
void Flush() {} void Flush() {}
}; };
TEST(Document, AcceptWriter) { TEST(Document, AcceptWriter) {
Document doc; Document doc;
doc.Parse(" { \"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; OutputStringStream os;
Writer<OutputStringStream> writer(os); Writer<OutputStringStream> writer(os);
doc.Accept(writer); doc.Accept(writer);
EXPECT_EQ("{\"hello\":\"world\",\"t\":true,\"f\":false,\"n\":null,\"i\":123,\"pi\":3.1416,\"a\":[1,2,3,4]}", os.str()); EXPECT_EQ("{\"hello\":\"world\",\"t\":true,\"f\":false,\"n\":null,\"i\":123,\"pi\":3.1416,\"a\":[1,2,3,4]}", os.str());
} }
// Issue 44: SetStringRaw doesn't work with wchar_t // Issue 44: SetStringRaw doesn't work with wchar_t
TEST(Document, UTF16_Document) { TEST(Document, UTF16_Document) {
GenericDocument< UTF16<> > json; GenericDocument< UTF16<> > json;
json.Parse<kParseValidateEncodingFlag>(L"[{\"created_at\":\"Wed Oct 30 17:13:20 +0000 2012\"}]"); json.Parse<kParseValidateEncodingFlag>(L"[{\"created_at\":\"Wed Oct 30 17:13:20 +0000 2012\"}]");
ASSERT_TRUE(json.IsArray()); ASSERT_TRUE(json.IsArray());
GenericValue< UTF16<> >& v = json[0u]; GenericValue< UTF16<> >& v = json[0u];
ASSERT_TRUE(v.IsObject()); ASSERT_TRUE(v.IsObject());
GenericValue< UTF16<> >& s = v[L"created_at"]; GenericValue< UTF16<> >& s = v[L"created_at"];
ASSERT_TRUE(s.IsString()); ASSERT_TRUE(s.IsString());
EXPECT_EQ(0, wcscmp(L"Wed Oct 30 17:13:20 +0000 2012", s.GetString())); EXPECT_EQ(0, wcscmp(L"Wed Oct 30 17:13:20 +0000 2012", s.GetString()));
} }
// Issue 22: Memory corruption via operator= // Issue 22: Memory corruption via operator=
// Fixed by making unimplemented assignment operator private. // Fixed by making unimplemented assignment operator private.
//TEST(Document, Assignment) { //TEST(Document, Assignment) {
// Document d1; // Document d1;
// Document d2; // Document d2;
// d1 = d2; // d1 = d2;
//} //}

View File

@ -1,3 +1,23 @@
// Copyright (C) 2011 Milo Yip
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "unittest.h" #include "unittest.h"
#include "rapidjson/filereadstream.h" #include "rapidjson/filereadstream.h"
#include "rapidjson/filewritestream.h" #include "rapidjson/filewritestream.h"
@ -10,270 +30,270 @@ using namespace rapidjson;
class EncodedStreamTest : public ::testing::Test { class EncodedStreamTest : public ::testing::Test {
public: public:
EncodedStreamTest() : json_(), length_() {} EncodedStreamTest() : json_(), length_() {}
virtual void SetUp() { virtual void SetUp() {
json_ = ReadFile("utf8.json", true, &length_); json_ = ReadFile("utf8.json", true, &length_);
} }
virtual void TearDown() { virtual void TearDown() {
free(json_); free(json_);
json_ = 0; json_ = 0;
} }
private: private:
EncodedStreamTest(const EncodedStreamTest&); EncodedStreamTest(const EncodedStreamTest&);
EncodedStreamTest& operator=(const EncodedStreamTest&); EncodedStreamTest& operator=(const EncodedStreamTest&);
protected: protected:
static FILE* Open(const char* filename) { static FILE* Open(const char* filename) {
char buffer[1024]; char buffer[1024];
sprintf(buffer, "encodings/%s", filename); sprintf(buffer, "encodings/%s", filename);
FILE *fp = fopen(buffer, "rb"); FILE *fp = fopen(buffer, "rb");
if (!fp) { if (!fp) {
sprintf(buffer, "../../bin/encodings/%s", filename); sprintf(buffer, "../../bin/encodings/%s", filename);
fp = fopen(buffer, "rb"); fp = fopen(buffer, "rb");
} }
return fp; return fp;
} }
static char *ReadFile(const char* filename, bool appendPath, size_t* outLength) { static char *ReadFile(const char* filename, bool appendPath, size_t* outLength) {
FILE *fp = appendPath ? Open(filename) : fopen(filename, "rb"); FILE *fp = appendPath ? Open(filename) : fopen(filename, "rb");
if (!fp) { if (!fp) {
*outLength = 0; *outLength = 0;
return 0; return 0;
} }
fseek(fp, 0, SEEK_END); fseek(fp, 0, SEEK_END);
*outLength = (size_t)ftell(fp); *outLength = (size_t)ftell(fp);
fseek(fp, 0, SEEK_SET); fseek(fp, 0, SEEK_SET);
char* buffer = (char*)malloc(*outLength + 1); char* buffer = (char*)malloc(*outLength + 1);
size_t readLength = fread(buffer, 1, *outLength, fp); size_t readLength = fread(buffer, 1, *outLength, fp);
buffer[readLength] = '\0'; buffer[readLength] = '\0';
fclose(fp); fclose(fp);
return buffer; return buffer;
} }
template <typename FileEncoding, typename MemoryEncoding> template <typename FileEncoding, typename MemoryEncoding>
void TestEncodedInputStream(const char* filename) { void TestEncodedInputStream(const char* filename) {
// Test FileReadStream // Test FileReadStream
{ {
char buffer[16]; char buffer[16];
FILE *fp = Open(filename); FILE *fp = Open(filename);
ASSERT_TRUE(fp != 0); ASSERT_TRUE(fp != 0);
FileReadStream fs(fp, buffer, sizeof(buffer)); FileReadStream fs(fp, buffer, sizeof(buffer));
EncodedInputStream<FileEncoding, FileReadStream> eis(fs); EncodedInputStream<FileEncoding, FileReadStream> eis(fs);
StringStream s(json_); StringStream s(json_);
while (eis.Peek() != '\0') { while (eis.Peek() != '\0') {
unsigned expected, actual; unsigned expected, actual;
EXPECT_TRUE(UTF8<>::Decode(s, &expected)); EXPECT_TRUE(UTF8<>::Decode(s, &expected));
EXPECT_TRUE(MemoryEncoding::Decode(eis, &actual)); EXPECT_TRUE(MemoryEncoding::Decode(eis, &actual));
EXPECT_EQ(expected, actual); EXPECT_EQ(expected, actual);
} }
EXPECT_EQ('\0', s.Peek()); EXPECT_EQ('\0', s.Peek());
fclose(fp); fclose(fp);
} }
// Test MemoryStream // Test MemoryStream
{ {
size_t size; size_t size;
char* data = ReadFile(filename, true, &size); char* data = ReadFile(filename, true, &size);
MemoryStream ms(data, size); MemoryStream ms(data, size);
EncodedInputStream<FileEncoding, MemoryStream> eis(ms); EncodedInputStream<FileEncoding, MemoryStream> eis(ms);
StringStream s(json_); StringStream s(json_);
while (eis.Peek() != '\0') { while (eis.Peek() != '\0') {
unsigned expected, actual; unsigned expected, actual;
EXPECT_TRUE(UTF8<>::Decode(s, &expected)); EXPECT_TRUE(UTF8<>::Decode(s, &expected));
EXPECT_TRUE(MemoryEncoding::Decode(eis, &actual)); EXPECT_TRUE(MemoryEncoding::Decode(eis, &actual));
EXPECT_EQ(expected, actual); EXPECT_EQ(expected, actual);
} }
EXPECT_EQ('\0', s.Peek()); EXPECT_EQ('\0', s.Peek());
free(data); free(data);
} }
} }
void TestAutoUTFInputStream(const char *filename) { void TestAutoUTFInputStream(const char *filename) {
// Test FileReadStream // Test FileReadStream
{ {
char buffer[16]; char buffer[16];
FILE *fp = Open(filename); FILE *fp = Open(filename);
ASSERT_TRUE(fp != 0); ASSERT_TRUE(fp != 0);
FileReadStream fs(fp, buffer, sizeof(buffer)); FileReadStream fs(fp, buffer, sizeof(buffer));
AutoUTFInputStream<unsigned, FileReadStream> eis(fs); AutoUTFInputStream<unsigned, FileReadStream> eis(fs);
StringStream s(json_); StringStream s(json_);
while (eis.Peek() != '\0') { while (eis.Peek() != '\0') {
unsigned expected, actual; unsigned expected, actual;
EXPECT_TRUE(UTF8<>::Decode(s, &expected)); EXPECT_TRUE(UTF8<>::Decode(s, &expected));
EXPECT_TRUE(AutoUTF<unsigned>::Decode(eis, &actual)); EXPECT_TRUE(AutoUTF<unsigned>::Decode(eis, &actual));
EXPECT_EQ(expected, actual); EXPECT_EQ(expected, actual);
} }
EXPECT_EQ('\0', s.Peek()); EXPECT_EQ('\0', s.Peek());
fclose(fp); fclose(fp);
} }
// Test MemoryStream // Test MemoryStream
{ {
size_t size; size_t size;
char* data = ReadFile(filename, true, &size); char* data = ReadFile(filename, true, &size);
MemoryStream ms(data, size); MemoryStream ms(data, size);
AutoUTFInputStream<unsigned, MemoryStream> eis(ms); AutoUTFInputStream<unsigned, MemoryStream> eis(ms);
StringStream s(json_); StringStream s(json_);
while (eis.Peek() != '\0') { while (eis.Peek() != '\0') {
unsigned expected, actual; unsigned expected, actual;
EXPECT_TRUE(UTF8<>::Decode(s, &expected)); EXPECT_TRUE(UTF8<>::Decode(s, &expected));
EXPECT_TRUE(AutoUTF<unsigned>::Decode(eis, &actual)); EXPECT_TRUE(AutoUTF<unsigned>::Decode(eis, &actual));
EXPECT_EQ(expected, actual); EXPECT_EQ(expected, actual);
} }
EXPECT_EQ('\0', s.Peek()); EXPECT_EQ('\0', s.Peek());
free(data); free(data);
} }
} }
template <typename FileEncoding, typename MemoryEncoding> template <typename FileEncoding, typename MemoryEncoding>
void TestEncodedOutputStream(const char* expectedFilename, bool putBOM) { void TestEncodedOutputStream(const char* expectedFilename, bool putBOM) {
// Test FileWriteStream // Test FileWriteStream
{ {
char filename[L_tmpnam]; char filename[L_tmpnam];
TempFilename(filename); TempFilename(filename);
FILE *fp = fopen(filename, "wb"); FILE *fp = fopen(filename, "wb");
char buffer[16]; char buffer[16];
FileWriteStream os(fp, buffer, sizeof(buffer)); FileWriteStream os(fp, buffer, sizeof(buffer));
EncodedOutputStream<FileEncoding, FileWriteStream> eos(os, putBOM); EncodedOutputStream<FileEncoding, FileWriteStream> eos(os, putBOM);
StringStream s(json_); StringStream s(json_);
while (s.Peek() != '\0') { while (s.Peek() != '\0') {
bool success = Transcoder<UTF8<>, MemoryEncoding>::Transcode(s, eos); bool success = Transcoder<UTF8<>, MemoryEncoding>::Transcode(s, eos);
EXPECT_TRUE(success); EXPECT_TRUE(success);
} }
eos.Flush(); eos.Flush();
fclose(fp); fclose(fp);
EXPECT_TRUE(CompareFile(filename, expectedFilename)); EXPECT_TRUE(CompareFile(filename, expectedFilename));
remove(filename); remove(filename);
} }
// Test MemoryBuffer // Test MemoryBuffer
{ {
MemoryBuffer mb; MemoryBuffer mb;
EncodedOutputStream<FileEncoding, MemoryBuffer> eos(mb, putBOM); EncodedOutputStream<FileEncoding, MemoryBuffer> eos(mb, putBOM);
StringStream s(json_); StringStream s(json_);
while (s.Peek() != '\0') { while (s.Peek() != '\0') {
bool success = Transcoder<UTF8<>, MemoryEncoding>::Transcode(s, eos); bool success = Transcoder<UTF8<>, MemoryEncoding>::Transcode(s, eos);
EXPECT_TRUE(success); EXPECT_TRUE(success);
} }
eos.Flush(); eos.Flush();
EXPECT_TRUE(CompareBufferFile(mb.GetBuffer(), mb.GetSize(), expectedFilename)); EXPECT_TRUE(CompareBufferFile(mb.GetBuffer(), mb.GetSize(), expectedFilename));
} }
} }
void TestAutoUTFOutputStream(UTFType type, bool putBOM, const char *expectedFilename) { void TestAutoUTFOutputStream(UTFType type, bool putBOM, const char *expectedFilename) {
// Test FileWriteStream // Test FileWriteStream
{ {
char filename[L_tmpnam]; char filename[L_tmpnam];
TempFilename(filename); TempFilename(filename);
FILE *fp = fopen(filename, "wb"); FILE *fp = fopen(filename, "wb");
char buffer[16]; char buffer[16];
FileWriteStream os(fp, buffer, sizeof(buffer)); FileWriteStream os(fp, buffer, sizeof(buffer));
AutoUTFOutputStream<unsigned, FileWriteStream> eos(os, type, putBOM); AutoUTFOutputStream<unsigned, FileWriteStream> eos(os, type, putBOM);
StringStream s(json_); StringStream s(json_);
while (s.Peek() != '\0') { while (s.Peek() != '\0') {
bool success = Transcoder<UTF8<>, AutoUTF<unsigned> >::Transcode(s, eos); bool success = Transcoder<UTF8<>, AutoUTF<unsigned> >::Transcode(s, eos);
EXPECT_TRUE(success); EXPECT_TRUE(success);
} }
eos.Flush(); eos.Flush();
fclose(fp); fclose(fp);
EXPECT_TRUE(CompareFile(filename, expectedFilename)); EXPECT_TRUE(CompareFile(filename, expectedFilename));
remove(filename); remove(filename);
} }
// Test MemoryBuffer // Test MemoryBuffer
{ {
MemoryBuffer mb; MemoryBuffer mb;
AutoUTFOutputStream<unsigned, MemoryBuffer> eos(mb, type, putBOM); AutoUTFOutputStream<unsigned, MemoryBuffer> eos(mb, type, putBOM);
StringStream s(json_); StringStream s(json_);
while (s.Peek() != '\0') { while (s.Peek() != '\0') {
bool success = Transcoder<UTF8<>, AutoUTF<unsigned> >::Transcode(s, eos); bool success = Transcoder<UTF8<>, AutoUTF<unsigned> >::Transcode(s, eos);
EXPECT_TRUE(success); EXPECT_TRUE(success);
} }
eos.Flush(); eos.Flush();
EXPECT_TRUE(CompareBufferFile(mb.GetBuffer(), mb.GetSize(), expectedFilename)); EXPECT_TRUE(CompareBufferFile(mb.GetBuffer(), mb.GetSize(), expectedFilename));
} }
} }
bool CompareFile(const char* filename, const char* expectedFilename) { bool CompareFile(const char* filename, const char* expectedFilename) {
size_t actualLength, expectedLength; size_t actualLength, expectedLength;
char* actualBuffer = ReadFile(filename, false, &actualLength); char* actualBuffer = ReadFile(filename, false, &actualLength);
char* expectedBuffer = ReadFile(expectedFilename, true, &expectedLength); char* expectedBuffer = ReadFile(expectedFilename, true, &expectedLength);
bool ret = (expectedLength == actualLength) && memcmp(expectedBuffer, actualBuffer, actualLength) == 0; bool ret = (expectedLength == actualLength) && memcmp(expectedBuffer, actualBuffer, actualLength) == 0;
free(actualBuffer); free(actualBuffer);
free(expectedBuffer); free(expectedBuffer);
return ret; return ret;
} }
bool CompareBufferFile(const char* actualBuffer, size_t actualLength, const char* expectedFilename) { bool CompareBufferFile(const char* actualBuffer, size_t actualLength, const char* expectedFilename) {
size_t expectedLength; size_t expectedLength;
char* expectedBuffer = ReadFile(expectedFilename, true, &expectedLength); char* expectedBuffer = ReadFile(expectedFilename, true, &expectedLength);
bool ret = (expectedLength == actualLength) && memcmp(expectedBuffer, actualBuffer, actualLength) == 0; bool ret = (expectedLength == actualLength) && memcmp(expectedBuffer, actualBuffer, actualLength) == 0;
free(expectedBuffer); free(expectedBuffer);
return ret; return ret;
} }
char *json_; char *json_;
size_t length_; size_t length_;
}; };
TEST_F(EncodedStreamTest, EncodedInputStream) { TEST_F(EncodedStreamTest, EncodedInputStream) {
TestEncodedInputStream<UTF8<>, UTF8<> >("utf8.json"); TestEncodedInputStream<UTF8<>, UTF8<> >("utf8.json");
TestEncodedInputStream<UTF8<>, UTF8<> >("utf8bom.json"); TestEncodedInputStream<UTF8<>, UTF8<> >("utf8bom.json");
TestEncodedInputStream<UTF16LE<>, UTF16<> >("utf16le.json"); TestEncodedInputStream<UTF16LE<>, UTF16<> >("utf16le.json");
TestEncodedInputStream<UTF16LE<>, UTF16<> >("utf16lebom.json"); TestEncodedInputStream<UTF16LE<>, UTF16<> >("utf16lebom.json");
TestEncodedInputStream<UTF16BE<>, UTF16<> >("utf16be.json"); TestEncodedInputStream<UTF16BE<>, UTF16<> >("utf16be.json");
TestEncodedInputStream<UTF16BE<>, UTF16<> >("utf16bebom.json"); TestEncodedInputStream<UTF16BE<>, UTF16<> >("utf16bebom.json");
TestEncodedInputStream<UTF32LE<>, UTF32<> >("utf32le.json"); TestEncodedInputStream<UTF32LE<>, UTF32<> >("utf32le.json");
TestEncodedInputStream<UTF32LE<>, UTF32<> >("utf32lebom.json"); TestEncodedInputStream<UTF32LE<>, UTF32<> >("utf32lebom.json");
TestEncodedInputStream<UTF32BE<>, UTF32<> >("utf32be.json"); TestEncodedInputStream<UTF32BE<>, UTF32<> >("utf32be.json");
TestEncodedInputStream<UTF32BE<>, UTF32<> >("utf32bebom.json"); TestEncodedInputStream<UTF32BE<>, UTF32<> >("utf32bebom.json");
} }
TEST_F(EncodedStreamTest, AutoUTFInputStream) { TEST_F(EncodedStreamTest, AutoUTFInputStream) {
TestAutoUTFInputStream("utf8.json"); TestAutoUTFInputStream("utf8.json");
TestAutoUTFInputStream("utf8bom.json"); TestAutoUTFInputStream("utf8bom.json");
TestAutoUTFInputStream("utf16le.json"); TestAutoUTFInputStream("utf16le.json");
TestAutoUTFInputStream("utf16lebom.json"); TestAutoUTFInputStream("utf16lebom.json");
TestAutoUTFInputStream("utf16be.json"); TestAutoUTFInputStream("utf16be.json");
TestAutoUTFInputStream("utf16bebom.json"); TestAutoUTFInputStream("utf16bebom.json");
TestAutoUTFInputStream("utf32le.json"); TestAutoUTFInputStream("utf32le.json");
TestAutoUTFInputStream("utf32lebom.json"); TestAutoUTFInputStream("utf32lebom.json");
TestAutoUTFInputStream("utf32be.json"); TestAutoUTFInputStream("utf32be.json");
TestAutoUTFInputStream("utf32bebom.json"); TestAutoUTFInputStream("utf32bebom.json");
} }
TEST_F(EncodedStreamTest, EncodedOutputStream) { TEST_F(EncodedStreamTest, EncodedOutputStream) {
TestEncodedOutputStream<UTF8<>, UTF8<> >("utf8.json", false); TestEncodedOutputStream<UTF8<>, UTF8<> >("utf8.json", false);
TestEncodedOutputStream<UTF8<>, UTF8<> >("utf8bom.json", true); TestEncodedOutputStream<UTF8<>, UTF8<> >("utf8bom.json", true);
TestEncodedOutputStream<UTF16LE<>, UTF16<> >("utf16le.json", false); TestEncodedOutputStream<UTF16LE<>, UTF16<> >("utf16le.json", false);
TestEncodedOutputStream<UTF16LE<>, UTF16<> >("utf16lebom.json",true); TestEncodedOutputStream<UTF16LE<>, UTF16<> >("utf16lebom.json",true);
TestEncodedOutputStream<UTF16BE<>, UTF16<> >("utf16be.json", false); TestEncodedOutputStream<UTF16BE<>, UTF16<> >("utf16be.json", false);
TestEncodedOutputStream<UTF16BE<>, UTF16<> >("utf16bebom.json",true); TestEncodedOutputStream<UTF16BE<>, UTF16<> >("utf16bebom.json",true);
TestEncodedOutputStream<UTF32LE<>, UTF32<> >("utf32le.json", false); TestEncodedOutputStream<UTF32LE<>, UTF32<> >("utf32le.json", false);
TestEncodedOutputStream<UTF32LE<>, UTF32<> >("utf32lebom.json",true); TestEncodedOutputStream<UTF32LE<>, UTF32<> >("utf32lebom.json",true);
TestEncodedOutputStream<UTF32BE<>, UTF32<> >("utf32be.json", false); TestEncodedOutputStream<UTF32BE<>, UTF32<> >("utf32be.json", false);
TestEncodedOutputStream<UTF32BE<>, UTF32<> >("utf32bebom.json",true); TestEncodedOutputStream<UTF32BE<>, UTF32<> >("utf32bebom.json",true);
} }
TEST_F(EncodedStreamTest, AutoUTFOutputStream) { TEST_F(EncodedStreamTest, AutoUTFOutputStream) {
TestAutoUTFOutputStream(kUTF8, false, "utf8.json"); TestAutoUTFOutputStream(kUTF8, false, "utf8.json");
TestAutoUTFOutputStream(kUTF8, true, "utf8bom.json"); TestAutoUTFOutputStream(kUTF8, true, "utf8bom.json");
TestAutoUTFOutputStream(kUTF16LE, false, "utf16le.json"); TestAutoUTFOutputStream(kUTF16LE, false, "utf16le.json");
TestAutoUTFOutputStream(kUTF16LE, true, "utf16lebom.json"); TestAutoUTFOutputStream(kUTF16LE, true, "utf16lebom.json");
TestAutoUTFOutputStream(kUTF16BE, false, "utf16be.json"); TestAutoUTFOutputStream(kUTF16BE, false, "utf16be.json");
TestAutoUTFOutputStream(kUTF16BE, true, "utf16bebom.json"); TestAutoUTFOutputStream(kUTF16BE, true, "utf16bebom.json");
TestAutoUTFOutputStream(kUTF32LE, false, "utf32le.json"); TestAutoUTFOutputStream(kUTF32LE, false, "utf32le.json");
TestAutoUTFOutputStream(kUTF32LE, true, "utf32lebom.json"); TestAutoUTFOutputStream(kUTF32LE, true, "utf32lebom.json");
TestAutoUTFOutputStream(kUTF32BE, false, "utf32be.json"); TestAutoUTFOutputStream(kUTF32BE, false, "utf32be.json");
TestAutoUTFOutputStream(kUTF32BE, true, "utf32bebom.json"); TestAutoUTFOutputStream(kUTF32BE, true, "utf32bebom.json");
} }

View File

@ -1,3 +1,23 @@
// Copyright (C) 2011 Milo Yip
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "unittest.h" #include "unittest.h"
#include "rapidjson/filereadstream.h" #include "rapidjson/filereadstream.h"
#include "rapidjson/filewritestream.h" #include "rapidjson/filewritestream.h"
@ -10,216 +30,216 @@ using namespace rapidjson;
// http://www.unicode.org/Public/UNIDATA/Blocks.txt // http://www.unicode.org/Public/UNIDATA/Blocks.txt
static const unsigned kCodepointRanges[] = { static const unsigned kCodepointRanges[] = {
0x0000, 0x007F, // Basic Latin 0x0000, 0x007F, // Basic Latin
0x0080, 0x00FF, // Latin-1 Supplement 0x0080, 0x00FF, // Latin-1 Supplement
0x0100, 0x017F, // Latin Extended-A 0x0100, 0x017F, // Latin Extended-A
0x0180, 0x024F, // Latin Extended-B 0x0180, 0x024F, // Latin Extended-B
0x0250, 0x02AF, // IPA Extensions 0x0250, 0x02AF, // IPA Extensions
0x02B0, 0x02FF, // Spacing Modifier Letters 0x02B0, 0x02FF, // Spacing Modifier Letters
0x0300, 0x036F, // Combining Diacritical Marks 0x0300, 0x036F, // Combining Diacritical Marks
0x0370, 0x03FF, // Greek and Coptic 0x0370, 0x03FF, // Greek and Coptic
0x0400, 0x04FF, // Cyrillic 0x0400, 0x04FF, // Cyrillic
0x0500, 0x052F, // Cyrillic Supplement 0x0500, 0x052F, // Cyrillic Supplement
0x0530, 0x058F, // Armenian 0x0530, 0x058F, // Armenian
0x0590, 0x05FF, // Hebrew 0x0590, 0x05FF, // Hebrew
0x0600, 0x06FF, // Arabic 0x0600, 0x06FF, // Arabic
0x0700, 0x074F, // Syriac 0x0700, 0x074F, // Syriac
0x0750, 0x077F, // Arabic Supplement 0x0750, 0x077F, // Arabic Supplement
0x0780, 0x07BF, // Thaana 0x0780, 0x07BF, // Thaana
0x07C0, 0x07FF, // NKo 0x07C0, 0x07FF, // NKo
0x0800, 0x083F, // Samaritan 0x0800, 0x083F, // Samaritan
0x0840, 0x085F, // Mandaic 0x0840, 0x085F, // Mandaic
0x0900, 0x097F, // Devanagari 0x0900, 0x097F, // Devanagari
0x0980, 0x09FF, // Bengali 0x0980, 0x09FF, // Bengali
0x0A00, 0x0A7F, // Gurmukhi 0x0A00, 0x0A7F, // Gurmukhi
0x0A80, 0x0AFF, // Gujarati 0x0A80, 0x0AFF, // Gujarati
0x0B00, 0x0B7F, // Oriya 0x0B00, 0x0B7F, // Oriya
0x0B80, 0x0BFF, // Tamil 0x0B80, 0x0BFF, // Tamil
0x0C00, 0x0C7F, // Telugu 0x0C00, 0x0C7F, // Telugu
0x0C80, 0x0CFF, // Kannada 0x0C80, 0x0CFF, // Kannada
0x0D00, 0x0D7F, // Malayalam 0x0D00, 0x0D7F, // Malayalam
0x0D80, 0x0DFF, // Sinhala 0x0D80, 0x0DFF, // Sinhala
0x0E00, 0x0E7F, // Thai 0x0E00, 0x0E7F, // Thai
0x0E80, 0x0EFF, // Lao 0x0E80, 0x0EFF, // Lao
0x0F00, 0x0FFF, // Tibetan 0x0F00, 0x0FFF, // Tibetan
0x1000, 0x109F, // Myanmar 0x1000, 0x109F, // Myanmar
0x10A0, 0x10FF, // Georgian 0x10A0, 0x10FF, // Georgian
0x1100, 0x11FF, // Hangul Jamo 0x1100, 0x11FF, // Hangul Jamo
0x1200, 0x137F, // Ethiopic 0x1200, 0x137F, // Ethiopic
0x1380, 0x139F, // Ethiopic Supplement 0x1380, 0x139F, // Ethiopic Supplement
0x13A0, 0x13FF, // Cherokee 0x13A0, 0x13FF, // Cherokee
0x1400, 0x167F, // Unified Canadian Aboriginal Syllabics 0x1400, 0x167F, // Unified Canadian Aboriginal Syllabics
0x1680, 0x169F, // Ogham 0x1680, 0x169F, // Ogham
0x16A0, 0x16FF, // Runic 0x16A0, 0x16FF, // Runic
0x1700, 0x171F, // Tagalog 0x1700, 0x171F, // Tagalog
0x1720, 0x173F, // Hanunoo 0x1720, 0x173F, // Hanunoo
0x1740, 0x175F, // Buhid 0x1740, 0x175F, // Buhid
0x1760, 0x177F, // Tagbanwa 0x1760, 0x177F, // Tagbanwa
0x1780, 0x17FF, // Khmer 0x1780, 0x17FF, // Khmer
0x1800, 0x18AF, // Mongolian 0x1800, 0x18AF, // Mongolian
0x18B0, 0x18FF, // Unified Canadian Aboriginal Syllabics Extended 0x18B0, 0x18FF, // Unified Canadian Aboriginal Syllabics Extended
0x1900, 0x194F, // Limbu 0x1900, 0x194F, // Limbu
0x1950, 0x197F, // Tai Le 0x1950, 0x197F, // Tai Le
0x1980, 0x19DF, // New Tai Lue 0x1980, 0x19DF, // New Tai Lue
0x19E0, 0x19FF, // Khmer Symbols 0x19E0, 0x19FF, // Khmer Symbols
0x1A00, 0x1A1F, // Buginese 0x1A00, 0x1A1F, // Buginese
0x1A20, 0x1AAF, // Tai Tham 0x1A20, 0x1AAF, // Tai Tham
0x1B00, 0x1B7F, // Balinese 0x1B00, 0x1B7F, // Balinese
0x1B80, 0x1BBF, // Sundanese 0x1B80, 0x1BBF, // Sundanese
0x1BC0, 0x1BFF, // Batak 0x1BC0, 0x1BFF, // Batak
0x1C00, 0x1C4F, // Lepcha 0x1C00, 0x1C4F, // Lepcha
0x1C50, 0x1C7F, // Ol Chiki 0x1C50, 0x1C7F, // Ol Chiki
0x1CD0, 0x1CFF, // Vedic Extensions 0x1CD0, 0x1CFF, // Vedic Extensions
0x1D00, 0x1D7F, // Phonetic Extensions 0x1D00, 0x1D7F, // Phonetic Extensions
0x1D80, 0x1DBF, // Phonetic Extensions Supplement 0x1D80, 0x1DBF, // Phonetic Extensions Supplement
0x1DC0, 0x1DFF, // Combining Diacritical Marks Supplement 0x1DC0, 0x1DFF, // Combining Diacritical Marks Supplement
0x1E00, 0x1EFF, // Latin Extended Additional 0x1E00, 0x1EFF, // Latin Extended Additional
0x1F00, 0x1FFF, // Greek Extended 0x1F00, 0x1FFF, // Greek Extended
0x2000, 0x206F, // General Punctuation 0x2000, 0x206F, // General Punctuation
0x2070, 0x209F, // Superscripts and Subscripts 0x2070, 0x209F, // Superscripts and Subscripts
0x20A0, 0x20CF, // Currency Symbols 0x20A0, 0x20CF, // Currency Symbols
0x20D0, 0x20FF, // Combining Diacritical Marks for Symbols 0x20D0, 0x20FF, // Combining Diacritical Marks for Symbols
0x2100, 0x214F, // Letterlike Symbols 0x2100, 0x214F, // Letterlike Symbols
0x2150, 0x218F, // Number Forms 0x2150, 0x218F, // Number Forms
0x2190, 0x21FF, // Arrows 0x2190, 0x21FF, // Arrows
0x2200, 0x22FF, // Mathematical Operators 0x2200, 0x22FF, // Mathematical Operators
0x2300, 0x23FF, // Miscellaneous Technical 0x2300, 0x23FF, // Miscellaneous Technical
0x2400, 0x243F, // Control Pictures 0x2400, 0x243F, // Control Pictures
0x2440, 0x245F, // Optical Character Recognition 0x2440, 0x245F, // Optical Character Recognition
0x2460, 0x24FF, // Enclosed Alphanumerics 0x2460, 0x24FF, // Enclosed Alphanumerics
0x2500, 0x257F, // Box Drawing 0x2500, 0x257F, // Box Drawing
0x2580, 0x259F, // Block Elements 0x2580, 0x259F, // Block Elements
0x25A0, 0x25FF, // Geometric Shapes 0x25A0, 0x25FF, // Geometric Shapes
0x2600, 0x26FF, // Miscellaneous Symbols 0x2600, 0x26FF, // Miscellaneous Symbols
0x2700, 0x27BF, // Dingbats 0x2700, 0x27BF, // Dingbats
0x27C0, 0x27EF, // Miscellaneous Mathematical Symbols-A 0x27C0, 0x27EF, // Miscellaneous Mathematical Symbols-A
0x27F0, 0x27FF, // Supplemental Arrows-A 0x27F0, 0x27FF, // Supplemental Arrows-A
0x2800, 0x28FF, // Braille Patterns 0x2800, 0x28FF, // Braille Patterns
0x2900, 0x297F, // Supplemental Arrows-B 0x2900, 0x297F, // Supplemental Arrows-B
0x2980, 0x29FF, // Miscellaneous Mathematical Symbols-B 0x2980, 0x29FF, // Miscellaneous Mathematical Symbols-B
0x2A00, 0x2AFF, // Supplemental Mathematical Operators 0x2A00, 0x2AFF, // Supplemental Mathematical Operators
0x2B00, 0x2BFF, // Miscellaneous Symbols and Arrows 0x2B00, 0x2BFF, // Miscellaneous Symbols and Arrows
0x2C00, 0x2C5F, // Glagolitic 0x2C00, 0x2C5F, // Glagolitic
0x2C60, 0x2C7F, // Latin Extended-C 0x2C60, 0x2C7F, // Latin Extended-C
0x2C80, 0x2CFF, // Coptic 0x2C80, 0x2CFF, // Coptic
0x2D00, 0x2D2F, // Georgian Supplement 0x2D00, 0x2D2F, // Georgian Supplement
0x2D30, 0x2D7F, // Tifinagh 0x2D30, 0x2D7F, // Tifinagh
0x2D80, 0x2DDF, // Ethiopic Extended 0x2D80, 0x2DDF, // Ethiopic Extended
0x2DE0, 0x2DFF, // Cyrillic Extended-A 0x2DE0, 0x2DFF, // Cyrillic Extended-A
0x2E00, 0x2E7F, // Supplemental Punctuation 0x2E00, 0x2E7F, // Supplemental Punctuation
0x2E80, 0x2EFF, // CJK Radicals Supplement 0x2E80, 0x2EFF, // CJK Radicals Supplement
0x2F00, 0x2FDF, // Kangxi Radicals 0x2F00, 0x2FDF, // Kangxi Radicals
0x2FF0, 0x2FFF, // Ideographic Description Characters 0x2FF0, 0x2FFF, // Ideographic Description Characters
0x3000, 0x303F, // CJK Symbols and Punctuation 0x3000, 0x303F, // CJK Symbols and Punctuation
0x3040, 0x309F, // Hiragana 0x3040, 0x309F, // Hiragana
0x30A0, 0x30FF, // Katakana 0x30A0, 0x30FF, // Katakana
0x3100, 0x312F, // Bopomofo 0x3100, 0x312F, // Bopomofo
0x3130, 0x318F, // Hangul Compatibility Jamo 0x3130, 0x318F, // Hangul Compatibility Jamo
0x3190, 0x319F, // Kanbun 0x3190, 0x319F, // Kanbun
0x31A0, 0x31BF, // Bopomofo Extended 0x31A0, 0x31BF, // Bopomofo Extended
0x31C0, 0x31EF, // CJK Strokes 0x31C0, 0x31EF, // CJK Strokes
0x31F0, 0x31FF, // Katakana Phonetic Extensions 0x31F0, 0x31FF, // Katakana Phonetic Extensions
0x3200, 0x32FF, // Enclosed CJK Letters and Months 0x3200, 0x32FF, // Enclosed CJK Letters and Months
0x3300, 0x33FF, // CJK Compatibility 0x3300, 0x33FF, // CJK Compatibility
0x3400, 0x4DBF, // CJK Unified Ideographs Extension A 0x3400, 0x4DBF, // CJK Unified Ideographs Extension A
0x4DC0, 0x4DFF, // Yijing Hexagram Symbols 0x4DC0, 0x4DFF, // Yijing Hexagram Symbols
0x4E00, 0x9FFF, // CJK Unified Ideographs 0x4E00, 0x9FFF, // CJK Unified Ideographs
0xA000, 0xA48F, // Yi Syllables 0xA000, 0xA48F, // Yi Syllables
0xA490, 0xA4CF, // Yi Radicals 0xA490, 0xA4CF, // Yi Radicals
0xA4D0, 0xA4FF, // Lisu 0xA4D0, 0xA4FF, // Lisu
0xA500, 0xA63F, // Vai 0xA500, 0xA63F, // Vai
0xA640, 0xA69F, // Cyrillic Extended-B 0xA640, 0xA69F, // Cyrillic Extended-B
0xA6A0, 0xA6FF, // Bamum 0xA6A0, 0xA6FF, // Bamum
0xA700, 0xA71F, // Modifier Tone Letters 0xA700, 0xA71F, // Modifier Tone Letters
0xA720, 0xA7FF, // Latin Extended-D 0xA720, 0xA7FF, // Latin Extended-D
0xA800, 0xA82F, // Syloti Nagri 0xA800, 0xA82F, // Syloti Nagri
0xA830, 0xA83F, // Common Indic Number Forms 0xA830, 0xA83F, // Common Indic Number Forms
0xA840, 0xA87F, // Phags-pa 0xA840, 0xA87F, // Phags-pa
0xA880, 0xA8DF, // Saurashtra 0xA880, 0xA8DF, // Saurashtra
0xA8E0, 0xA8FF, // Devanagari Extended 0xA8E0, 0xA8FF, // Devanagari Extended
0xA900, 0xA92F, // Kayah Li 0xA900, 0xA92F, // Kayah Li
0xA930, 0xA95F, // Rejang 0xA930, 0xA95F, // Rejang
0xA960, 0xA97F, // Hangul Jamo Extended-A 0xA960, 0xA97F, // Hangul Jamo Extended-A
0xA980, 0xA9DF, // Javanese 0xA980, 0xA9DF, // Javanese
0xAA00, 0xAA5F, // Cham 0xAA00, 0xAA5F, // Cham
0xAA60, 0xAA7F, // Myanmar Extended-A 0xAA60, 0xAA7F, // Myanmar Extended-A
0xAA80, 0xAADF, // Tai Viet 0xAA80, 0xAADF, // Tai Viet
0xAB00, 0xAB2F, // Ethiopic Extended-A 0xAB00, 0xAB2F, // Ethiopic Extended-A
0xABC0, 0xABFF, // Meetei Mayek 0xABC0, 0xABFF, // Meetei Mayek
0xAC00, 0xD7AF, // Hangul Syllables 0xAC00, 0xD7AF, // Hangul Syllables
0xD7B0, 0xD7FF, // Hangul Jamo Extended-B 0xD7B0, 0xD7FF, // Hangul Jamo Extended-B
//0xD800, 0xDB7F, // High Surrogates //0xD800, 0xDB7F, // High Surrogates
//0xDB80, 0xDBFF, // High Private Use Surrogates //0xDB80, 0xDBFF, // High Private Use Surrogates
//0xDC00, 0xDFFF, // Low Surrogates //0xDC00, 0xDFFF, // Low Surrogates
0xE000, 0xF8FF, // Private Use Area 0xE000, 0xF8FF, // Private Use Area
0xF900, 0xFAFF, // CJK Compatibility Ideographs 0xF900, 0xFAFF, // CJK Compatibility Ideographs
0xFB00, 0xFB4F, // Alphabetic Presentation Forms 0xFB00, 0xFB4F, // Alphabetic Presentation Forms
0xFB50, 0xFDFF, // Arabic Presentation Forms-A 0xFB50, 0xFDFF, // Arabic Presentation Forms-A
0xFE00, 0xFE0F, // Variation Selectors 0xFE00, 0xFE0F, // Variation Selectors
0xFE10, 0xFE1F, // Vertical Forms 0xFE10, 0xFE1F, // Vertical Forms
0xFE20, 0xFE2F, // Combining Half Marks 0xFE20, 0xFE2F, // Combining Half Marks
0xFE30, 0xFE4F, // CJK Compatibility Forms 0xFE30, 0xFE4F, // CJK Compatibility Forms
0xFE50, 0xFE6F, // Small Form Variants 0xFE50, 0xFE6F, // Small Form Variants
0xFE70, 0xFEFF, // Arabic Presentation Forms-B 0xFE70, 0xFEFF, // Arabic Presentation Forms-B
0xFF00, 0xFFEF, // Halfwidth and Fullwidth Forms 0xFF00, 0xFFEF, // Halfwidth and Fullwidth Forms
0xFFF0, 0xFFFF, // Specials 0xFFF0, 0xFFFF, // Specials
0x10000, 0x1007F, // Linear B Syllabary 0x10000, 0x1007F, // Linear B Syllabary
0x10080, 0x100FF, // Linear B Ideograms 0x10080, 0x100FF, // Linear B Ideograms
0x10100, 0x1013F, // Aegean Numbers 0x10100, 0x1013F, // Aegean Numbers
0x10140, 0x1018F, // Ancient Greek Numbers 0x10140, 0x1018F, // Ancient Greek Numbers
0x10190, 0x101CF, // Ancient Symbols 0x10190, 0x101CF, // Ancient Symbols
0x101D0, 0x101FF, // Phaistos Disc 0x101D0, 0x101FF, // Phaistos Disc
0x10280, 0x1029F, // Lycian 0x10280, 0x1029F, // Lycian
0x102A0, 0x102DF, // Carian 0x102A0, 0x102DF, // Carian
0x10300, 0x1032F, // Old Italic 0x10300, 0x1032F, // Old Italic
0x10330, 0x1034F, // Gothic 0x10330, 0x1034F, // Gothic
0x10380, 0x1039F, // Ugaritic 0x10380, 0x1039F, // Ugaritic
0x103A0, 0x103DF, // Old Persian 0x103A0, 0x103DF, // Old Persian
0x10400, 0x1044F, // Deseret 0x10400, 0x1044F, // Deseret
0x10450, 0x1047F, // Shavian 0x10450, 0x1047F, // Shavian
0x10480, 0x104AF, // Osmanya 0x10480, 0x104AF, // Osmanya
0x10800, 0x1083F, // Cypriot Syllabary 0x10800, 0x1083F, // Cypriot Syllabary
0x10840, 0x1085F, // Imperial Aramaic 0x10840, 0x1085F, // Imperial Aramaic
0x10900, 0x1091F, // Phoenician 0x10900, 0x1091F, // Phoenician
0x10920, 0x1093F, // Lydian 0x10920, 0x1093F, // Lydian
0x10A00, 0x10A5F, // Kharoshthi 0x10A00, 0x10A5F, // Kharoshthi
0x10A60, 0x10A7F, // Old South Arabian 0x10A60, 0x10A7F, // Old South Arabian
0x10B00, 0x10B3F, // Avestan 0x10B00, 0x10B3F, // Avestan
0x10B40, 0x10B5F, // Inscriptional Parthian 0x10B40, 0x10B5F, // Inscriptional Parthian
0x10B60, 0x10B7F, // Inscriptional Pahlavi 0x10B60, 0x10B7F, // Inscriptional Pahlavi
0x10C00, 0x10C4F, // Old Turkic 0x10C00, 0x10C4F, // Old Turkic
0x10E60, 0x10E7F, // Rumi Numeral Symbols 0x10E60, 0x10E7F, // Rumi Numeral Symbols
0x11000, 0x1107F, // Brahmi 0x11000, 0x1107F, // Brahmi
0x11080, 0x110CF, // Kaithi 0x11080, 0x110CF, // Kaithi
0x12000, 0x123FF, // Cuneiform 0x12000, 0x123FF, // Cuneiform
0x12400, 0x1247F, // Cuneiform Numbers and Punctuation 0x12400, 0x1247F, // Cuneiform Numbers and Punctuation
0x13000, 0x1342F, // Egyptian Hieroglyphs 0x13000, 0x1342F, // Egyptian Hieroglyphs
0x16800, 0x16A3F, // Bamum Supplement 0x16800, 0x16A3F, // Bamum Supplement
0x1B000, 0x1B0FF, // Kana Supplement 0x1B000, 0x1B0FF, // Kana Supplement
0x1D000, 0x1D0FF, // Byzantine Musical Symbols 0x1D000, 0x1D0FF, // Byzantine Musical Symbols
0x1D100, 0x1D1FF, // Musical Symbols 0x1D100, 0x1D1FF, // Musical Symbols
0x1D200, 0x1D24F, // Ancient Greek Musical Notation 0x1D200, 0x1D24F, // Ancient Greek Musical Notation
0x1D300, 0x1D35F, // Tai Xuan Jing Symbols 0x1D300, 0x1D35F, // Tai Xuan Jing Symbols
0x1D360, 0x1D37F, // Counting Rod Numerals 0x1D360, 0x1D37F, // Counting Rod Numerals
0x1D400, 0x1D7FF, // Mathematical Alphanumeric Symbols 0x1D400, 0x1D7FF, // Mathematical Alphanumeric Symbols
0x1F000, 0x1F02F, // Mahjong Tiles 0x1F000, 0x1F02F, // Mahjong Tiles
0x1F030, 0x1F09F, // Domino Tiles 0x1F030, 0x1F09F, // Domino Tiles
0x1F0A0, 0x1F0FF, // Playing Cards 0x1F0A0, 0x1F0FF, // Playing Cards
0x1F100, 0x1F1FF, // Enclosed Alphanumeric Supplement 0x1F100, 0x1F1FF, // Enclosed Alphanumeric Supplement
0x1F200, 0x1F2FF, // Enclosed Ideographic Supplement 0x1F200, 0x1F2FF, // Enclosed Ideographic Supplement
0x1F300, 0x1F5FF, // Miscellaneous Symbols And Pictographs 0x1F300, 0x1F5FF, // Miscellaneous Symbols And Pictographs
0x1F600, 0x1F64F, // Emoticons 0x1F600, 0x1F64F, // Emoticons
0x1F680, 0x1F6FF, // Transport And Map Symbols 0x1F680, 0x1F6FF, // Transport And Map Symbols
0x1F700, 0x1F77F, // Alchemical Symbols 0x1F700, 0x1F77F, // Alchemical Symbols
0x20000, 0x2A6DF, // CJK Unified Ideographs Extension B 0x20000, 0x2A6DF, // CJK Unified Ideographs Extension B
0x2A700, 0x2B73F, // CJK Unified Ideographs Extension C 0x2A700, 0x2B73F, // CJK Unified Ideographs Extension C
0x2B740, 0x2B81F, // CJK Unified Ideographs Extension D 0x2B740, 0x2B81F, // CJK Unified Ideographs Extension D
0x2F800, 0x2FA1F, // CJK Compatibility Ideographs Supplement 0x2F800, 0x2FA1F, // CJK Compatibility Ideographs Supplement
0xE0000, 0xE007F, // Tags 0xE0000, 0xE007F, // Tags
0xE0100, 0xE01EF, // Variation Selectors Supplement 0xE0100, 0xE01EF, // Variation Selectors Supplement
0xF0000, 0xFFFFF, // Supplementary Private Use Area-A 0xF0000, 0xFFFFF, // Supplementary Private Use Area-A
0x100000, 0x10FFFF, // Supplementary Private Use Area-B 0x100000, 0x10FFFF, // Supplementary Private Use Area-B
0xFFFFFFFF 0xFFFFFFFF
}; };
// Copyright (c) 2008-2010 Bjoern Hoehrmann <bjoern@hoehrmann.de> // Copyright (c) 2008-2010 Bjoern Hoehrmann <bjoern@hoehrmann.de>
@ -229,184 +249,184 @@ static const unsigned kCodepointRanges[] = {
#define UTF8_REJECT 12u #define UTF8_REJECT 12u
static const unsigned char utf8d[] = { static const unsigned char utf8d[] = {
// The first part of the table maps bytes to character classes that // The first part of the table maps bytes to character classes that
// to reduce the size of the transition table and create bitmasks. // to reduce the size of the transition table and create bitmasks.
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8, 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8,
// The second part is a transition table that maps a combination // The second part is a transition table that maps a combination
// of a state of the automaton and a character class to a state. // of a state of the automaton and a character class to a state.
0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12, 0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12,
12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12, 12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12,
12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12, 12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12,
12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12, 12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12,
12,36,12,12,12,12,12,12,12,12,12,12, 12,36,12,12,12,12,12,12,12,12,12,12,
}; };
static unsigned inline decode(unsigned* state, unsigned* codep, unsigned byte) { static unsigned inline decode(unsigned* state, unsigned* codep, unsigned byte) {
unsigned type = utf8d[byte]; unsigned type = utf8d[byte];
*codep = (*state != UTF8_ACCEPT) ? *codep = (*state != UTF8_ACCEPT) ?
(byte & 0x3fu) | (*codep << 6) : (byte & 0x3fu) | (*codep << 6) :
(0xff >> type) & (byte); (0xff >> type) & (byte);
*state = utf8d[256 + *state + type]; *state = utf8d[256 + *state + type];
return *state; return *state;
} }
//static bool IsUTF8(unsigned char* s) { //static bool IsUTF8(unsigned char* s) {
// unsigned codepoint, state = 0; // unsigned codepoint, state = 0;
// //
// while (*s) // while (*s)
// decode(&state, &codepoint, *s++); // decode(&state, &codepoint, *s++);
// //
// return state == UTF8_ACCEPT; // return state == UTF8_ACCEPT;
//} //}
TEST(EncodingsTest, UTF8) { TEST(EncodingsTest, UTF8) {
StringBuffer os, os2; StringBuffer os, os2;
for (const unsigned* range = kCodepointRanges; *range != 0xFFFFFFFF; range += 2) { for (const unsigned* range = kCodepointRanges; *range != 0xFFFFFFFF; range += 2) {
for (unsigned codepoint = range[0]; codepoint <= range[1]; ++codepoint) { for (unsigned codepoint = range[0]; codepoint <= range[1]; ++codepoint) {
os.Clear(); os.Clear();
UTF8<>::Encode(os, codepoint); UTF8<>::Encode(os, codepoint);
const char* encodedStr = os.GetString(); const char* encodedStr = os.GetString();
// Decode with Hoehrmann // Decode with Hoehrmann
{ {
unsigned decodedCodepoint = 0; unsigned decodedCodepoint = 0;
unsigned state = 0; unsigned state = 0;
unsigned decodedCount = 0; unsigned decodedCount = 0;
for (const char* s = encodedStr; *s; ++s) for (const char* s = encodedStr; *s; ++s)
if (!decode(&state, &decodedCodepoint, (unsigned char)*s)) { if (!decode(&state, &decodedCodepoint, (unsigned char)*s)) {
EXPECT_EQ(codepoint, decodedCodepoint); EXPECT_EQ(codepoint, decodedCodepoint);
decodedCount++; decodedCount++;
} }
if (*encodedStr) // This decoder cannot handle U+0000 if (*encodedStr) // This decoder cannot handle U+0000
EXPECT_EQ(1u, decodedCount); // Should only contain one code point EXPECT_EQ(1u, decodedCount); // Should only contain one code point
EXPECT_EQ(UTF8_ACCEPT, state); EXPECT_EQ(UTF8_ACCEPT, state);
if (UTF8_ACCEPT != state) if (UTF8_ACCEPT != state)
std::cout << std::hex << codepoint << " " << decodedCodepoint << std::endl; std::cout << std::hex << codepoint << " " << decodedCodepoint << std::endl;
} }
// Decode // Decode
{ {
StringStream is(encodedStr); StringStream is(encodedStr);
unsigned decodedCodepoint; unsigned decodedCodepoint;
bool result = UTF8<>::Decode(is, &decodedCodepoint); bool result = UTF8<>::Decode(is, &decodedCodepoint);
EXPECT_TRUE(result); EXPECT_TRUE(result);
EXPECT_EQ(codepoint, decodedCodepoint); EXPECT_EQ(codepoint, decodedCodepoint);
if (!result || codepoint != decodedCodepoint) if (!result || codepoint != decodedCodepoint)
std::cout << std::hex << codepoint << " " << decodedCodepoint << std::endl; std::cout << std::hex << codepoint << " " << decodedCodepoint << std::endl;
} }
// Validate // Validate
{ {
StringStream is(encodedStr); StringStream is(encodedStr);
os2.Clear(); os2.Clear();
bool result = UTF8<>::Validate(is, os2); bool result = UTF8<>::Validate(is, os2);
EXPECT_TRUE(result); EXPECT_TRUE(result);
EXPECT_EQ(0, StrCmp(encodedStr, os2.GetString())); EXPECT_EQ(0, StrCmp(encodedStr, os2.GetString()));
} }
} }
} }
} }
TEST(EncodingsTest, UTF16) { TEST(EncodingsTest, UTF16) {
GenericStringBuffer<UTF16<> > os, os2; GenericStringBuffer<UTF16<> > os, os2;
GenericStringBuffer<UTF8<> > utf8os; GenericStringBuffer<UTF8<> > utf8os;
for (const unsigned* range = kCodepointRanges; *range != 0xFFFFFFFF; range += 2) { for (const unsigned* range = kCodepointRanges; *range != 0xFFFFFFFF; range += 2) {
for (unsigned codepoint = range[0]; codepoint <= range[1]; ++codepoint) { for (unsigned codepoint = range[0]; codepoint <= range[1]; ++codepoint) {
os.Clear(); os.Clear();
UTF16<>::Encode(os, codepoint); UTF16<>::Encode(os, codepoint);
const UTF16<>::Ch* encodedStr = os.GetString(); const UTF16<>::Ch* encodedStr = os.GetString();
// Encode with Hoehrmann's code // Encode with Hoehrmann's code
if (codepoint != 0) // cannot handle U+0000 if (codepoint != 0) // cannot handle U+0000
{ {
// encode with UTF8<> first // encode with UTF8<> first
utf8os.Clear(); utf8os.Clear();
UTF8<>::Encode(utf8os, codepoint); UTF8<>::Encode(utf8os, codepoint);
// transcode from UTF8 to UTF16 with Hoehrmann's code // transcode from UTF8 to UTF16 with Hoehrmann's code
unsigned decodedCodepoint = 0; unsigned decodedCodepoint = 0;
unsigned state = 0; unsigned state = 0;
UTF16<>::Ch buffer[3], *p = &buffer[0]; UTF16<>::Ch buffer[3], *p = &buffer[0];
for (const char* s = utf8os.GetString(); *s; ++s) { for (const char* s = utf8os.GetString(); *s; ++s) {
if (!decode(&state, &decodedCodepoint, (unsigned char)*s)) if (!decode(&state, &decodedCodepoint, (unsigned char)*s))
break; break;
} }
if (codepoint <= 0xFFFF) if (codepoint <= 0xFFFF)
*p++ = static_cast<UTF16<>::Ch>(decodedCodepoint); *p++ = static_cast<UTF16<>::Ch>(decodedCodepoint);
else { else {
// Encode code points above U+FFFF as surrogate pair. // Encode code points above U+FFFF as surrogate pair.
*p++ = static_cast<UTF16<>::Ch>(0xD7C0 + (decodedCodepoint >> 10)); *p++ = static_cast<UTF16<>::Ch>(0xD7C0 + (decodedCodepoint >> 10));
*p++ = static_cast<UTF16<>::Ch>(0xDC00 + (decodedCodepoint & 0x3FF)); *p++ = static_cast<UTF16<>::Ch>(0xDC00 + (decodedCodepoint & 0x3FF));
} }
*p++ = '\0'; *p++ = '\0';
EXPECT_EQ(0, StrCmp(buffer, encodedStr)); EXPECT_EQ(0, StrCmp(buffer, encodedStr));
} }
// Decode // Decode
{ {
GenericStringStream<UTF16<> > is(encodedStr); GenericStringStream<UTF16<> > is(encodedStr);
unsigned decodedCodepoint; unsigned decodedCodepoint;
bool result = UTF16<>::Decode(is, &decodedCodepoint); bool result = UTF16<>::Decode(is, &decodedCodepoint);
EXPECT_TRUE(result); EXPECT_TRUE(result);
EXPECT_EQ(codepoint, decodedCodepoint); EXPECT_EQ(codepoint, decodedCodepoint);
if (!result || codepoint != decodedCodepoint) if (!result || codepoint != decodedCodepoint)
std::cout << std::hex << codepoint << " " << decodedCodepoint << std::endl; std::cout << std::hex << codepoint << " " << decodedCodepoint << std::endl;
} }
// Validate // Validate
{ {
GenericStringStream<UTF16<> > is(encodedStr); GenericStringStream<UTF16<> > is(encodedStr);
os2.Clear(); os2.Clear();
bool result = UTF16<>::Validate(is, os2); bool result = UTF16<>::Validate(is, os2);
EXPECT_TRUE(result); EXPECT_TRUE(result);
EXPECT_EQ(0, StrCmp(encodedStr, os2.GetString())); EXPECT_EQ(0, StrCmp(encodedStr, os2.GetString()));
} }
} }
} }
} }
TEST(EncodingsTest, UTF32) { TEST(EncodingsTest, UTF32) {
GenericStringBuffer<UTF32<> > os, os2; GenericStringBuffer<UTF32<> > os, os2;
for (const unsigned* range = kCodepointRanges; *range != 0xFFFFFFFF; range += 2) { for (const unsigned* range = kCodepointRanges; *range != 0xFFFFFFFF; range += 2) {
for (unsigned codepoint = range[0]; codepoint <= range[1]; ++codepoint) { for (unsigned codepoint = range[0]; codepoint <= range[1]; ++codepoint) {
os.Clear(); os.Clear();
UTF32<>::Encode(os, codepoint); UTF32<>::Encode(os, codepoint);
const UTF32<>::Ch* encodedStr = os.GetString(); const UTF32<>::Ch* encodedStr = os.GetString();
// Decode // Decode
{ {
GenericStringStream<UTF32<> > is(encodedStr); GenericStringStream<UTF32<> > is(encodedStr);
unsigned decodedCodepoint; unsigned decodedCodepoint;
bool result = UTF32<>::Decode(is, &decodedCodepoint); bool result = UTF32<>::Decode(is, &decodedCodepoint);
EXPECT_TRUE(result); EXPECT_TRUE(result);
EXPECT_EQ(codepoint, decodedCodepoint); EXPECT_EQ(codepoint, decodedCodepoint);
if (!result || codepoint != decodedCodepoint) if (!result || codepoint != decodedCodepoint)
std::cout << std::hex << codepoint << " " << decodedCodepoint << std::endl; std::cout << std::hex << codepoint << " " << decodedCodepoint << std::endl;
} }
// Validate // Validate
{ {
GenericStringStream<UTF32<> > is(encodedStr); GenericStringStream<UTF32<> > is(encodedStr);
os2.Clear(); os2.Clear();
bool result = UTF32<>::Validate(is, os2); bool result = UTF32<>::Validate(is, os2);
EXPECT_TRUE(result); EXPECT_TRUE(result);
EXPECT_EQ(0, StrCmp(encodedStr, os2.GetString())); EXPECT_EQ(0, StrCmp(encodedStr, os2.GetString()));
} }
} }
} }
} }

View File

@ -1,3 +1,23 @@
// Copyright (C) 2011 Milo Yip
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "unittest.h" #include "unittest.h"
#include "rapidjson/filestream.h" #include "rapidjson/filestream.h"
#include "rapidjson/filereadstream.h" #include "rapidjson/filereadstream.h"
@ -8,96 +28,96 @@ using namespace rapidjson;
class FileStreamTest : public ::testing::Test { class FileStreamTest : public ::testing::Test {
public: public:
FileStreamTest() : filename_(), json_(), length_() {} FileStreamTest() : filename_(), json_(), length_() {}
virtual void SetUp() { virtual void SetUp() {
FILE *fp = fopen(filename_ = "data/sample.json", "rb"); FILE *fp = fopen(filename_ = "data/sample.json", "rb");
if (!fp) if (!fp)
fp = fopen(filename_ = "../../bin/data/sample.json", "rb"); fp = fopen(filename_ = "../../bin/data/sample.json", "rb");
ASSERT_TRUE(fp != 0); ASSERT_TRUE(fp != 0);
fseek(fp, 0, SEEK_END); fseek(fp, 0, SEEK_END);
length_ = (size_t)ftell(fp); length_ = (size_t)ftell(fp);
fseek(fp, 0, SEEK_SET); fseek(fp, 0, SEEK_SET);
json_ = (char*)malloc(length_ + 1); json_ = (char*)malloc(length_ + 1);
size_t readLength = fread(json_, 1, length_, fp); size_t readLength = fread(json_, 1, length_, fp);
json_[readLength] = '\0'; json_[readLength] = '\0';
fclose(fp); fclose(fp);
} }
virtual void TearDown() { virtual void TearDown() {
free(json_); free(json_);
json_ = 0; json_ = 0;
} }
private: private:
FileStreamTest(const FileStreamTest&); FileStreamTest(const FileStreamTest&);
FileStreamTest& operator=(const FileStreamTest&); FileStreamTest& operator=(const FileStreamTest&);
protected: protected:
const char* filename_; const char* filename_;
char *json_; char *json_;
size_t length_; size_t length_;
}; };
// Depreciated // Depreciated
//TEST_F(FileStreamTest, FileStream_Read) { //TEST_F(FileStreamTest, FileStream_Read) {
// FILE *fp = fopen(filename_, "rb"); // FILE *fp = fopen(filename_, "rb");
// ASSERT_TRUE(fp != 0); // ASSERT_TRUE(fp != 0);
// FileStream s(fp); // FileStream s(fp);
// //
// for (size_t i = 0; i < length_; i++) { // for (size_t i = 0; i < length_; i++) {
// EXPECT_EQ(json_[i], s.Peek()); // EXPECT_EQ(json_[i], s.Peek());
// EXPECT_EQ(json_[i], s.Peek()); // 2nd time should be the same // EXPECT_EQ(json_[i], s.Peek()); // 2nd time should be the same
// EXPECT_EQ(json_[i], s.Take()); // EXPECT_EQ(json_[i], s.Take());
// } // }
// //
// EXPECT_EQ(length_, s.Tell()); // EXPECT_EQ(length_, s.Tell());
// EXPECT_EQ('\0', s.Peek()); // EXPECT_EQ('\0', s.Peek());
// //
// fclose(fp); // fclose(fp);
//} //}
TEST_F(FileStreamTest, FileReadStream) { TEST_F(FileStreamTest, FileReadStream) {
FILE *fp = fopen(filename_, "rb"); FILE *fp = fopen(filename_, "rb");
ASSERT_TRUE(fp != 0); ASSERT_TRUE(fp != 0);
char buffer[65536]; char buffer[65536];
FileReadStream s(fp, buffer, sizeof(buffer)); FileReadStream s(fp, buffer, sizeof(buffer));
for (size_t i = 0; i < length_; i++) { for (size_t i = 0; i < length_; i++) {
EXPECT_EQ(json_[i], s.Peek()); EXPECT_EQ(json_[i], s.Peek());
EXPECT_EQ(json_[i], s.Peek()); // 2nd time should be the same EXPECT_EQ(json_[i], s.Peek()); // 2nd time should be the same
EXPECT_EQ(json_[i], s.Take()); EXPECT_EQ(json_[i], s.Take());
} }
EXPECT_EQ(length_, s.Tell()); EXPECT_EQ(length_, s.Tell());
EXPECT_EQ('\0', s.Peek()); EXPECT_EQ('\0', s.Peek());
fclose(fp); fclose(fp);
} }
TEST_F(FileStreamTest, FileWriteStream) { TEST_F(FileStreamTest, FileWriteStream) {
char filename[L_tmpnam]; char filename[L_tmpnam];
TempFilename(filename); TempFilename(filename);
FILE *fp = fopen(filename, "wb"); FILE *fp = fopen(filename, "wb");
char buffer[65536]; char buffer[65536];
FileWriteStream os(fp, buffer, sizeof(buffer)); FileWriteStream os(fp, buffer, sizeof(buffer));
for (size_t i = 0; i < length_; i++) for (size_t i = 0; i < length_; i++)
os.Put(json_[i]); os.Put(json_[i]);
os.Flush(); os.Flush();
fclose(fp); fclose(fp);
// Read it back to verify // Read it back to verify
fp = fopen(filename, "rb"); fp = fopen(filename, "rb");
FileReadStream is(fp, buffer, sizeof(buffer)); FileReadStream is(fp, buffer, sizeof(buffer));
for (size_t i = 0; i < length_; i++) for (size_t i = 0; i < length_; i++)
EXPECT_EQ(json_[i], is.Take()); EXPECT_EQ(json_[i], is.Take());
EXPECT_EQ(length_, is.Tell()); EXPECT_EQ(length_, is.Tell());
fclose(fp); fclose(fp);
//std::cout << filename << std::endl; //std::cout << filename << std::endl;
remove(filename); remove(filename);
} }

View File

@ -1,3 +1,23 @@
// Copyright (C) 2011 Milo Yip
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "unittest.h" #include "unittest.h"
#include "rapidjson/document.h" #include "rapidjson/document.h"
@ -5,66 +25,66 @@
using namespace rapidjson; using namespace rapidjson;
static char* ReadFile(const char* filename, size_t& length) { static char* ReadFile(const char* filename, size_t& length) {
FILE *fp = fopen(filename, "rb"); FILE *fp = fopen(filename, "rb");
if (!fp) if (!fp)
fp = fopen(filename, "rb"); fp = fopen(filename, "rb");
if (!fp) if (!fp)
return 0; return 0;
fseek(fp, 0, SEEK_END); fseek(fp, 0, SEEK_END);
length = (size_t)ftell(fp); length = (size_t)ftell(fp);
fseek(fp, 0, SEEK_SET); fseek(fp, 0, SEEK_SET);
char* json = (char*)malloc(length + 1); char* json = (char*)malloc(length + 1);
size_t readLength = fread(json, 1, length, fp); size_t readLength = fread(json, 1, length, fp);
json[readLength] = '\0'; json[readLength] = '\0';
fclose(fp); fclose(fp);
return json; return json;
} }
TEST(JsonChecker, Reader) { TEST(JsonChecker, Reader) {
char filename[256]; char filename[256];
// jsonchecker/failXX.json // jsonchecker/failXX.json
for (int i = 1; i <= 33; i++) { for (int i = 1; i <= 33; i++) {
if (i == 18) // fail18.json is valid in rapidjson, which has no limitation on depth of nesting. if (i == 18) // fail18.json is valid in rapidjson, which has no limitation on depth of nesting.
continue; continue;
sprintf(filename, "jsonchecker/fail%d.json", i); sprintf(filename, "jsonchecker/fail%d.json", i);
size_t length; size_t length;
char* json = ReadFile(filename, length); char* json = ReadFile(filename, length);
if (!json) { if (!json) {
sprintf(filename, "../../bin/jsonchecker/fail%d.json", i); sprintf(filename, "../../bin/jsonchecker/fail%d.json", i);
json = ReadFile(filename, length); json = ReadFile(filename, length);
if (!json) { if (!json) {
printf("jsonchecker file %s not found", filename); printf("jsonchecker file %s not found", filename);
continue; continue;
} }
} }
GenericDocument<UTF8<>, CrtAllocator> document; // Use Crt allocator to check exception-safety (no memory leak) GenericDocument<UTF8<>, CrtAllocator> document; // Use Crt allocator to check exception-safety (no memory leak)
if (!document.Parse((const char*)json).HasParseError()) if (!document.Parse((const char*)json).HasParseError())
FAIL(); FAIL();
//printf("%s(%u):%s\n", filename, (unsigned)document.GetErrorOffset(), document.GetParseError()); //printf("%s(%u):%s\n", filename, (unsigned)document.GetErrorOffset(), document.GetParseError());
free(json); free(json);
} }
// passX.json // passX.json
for (int i = 1; i <= 3; i++) { for (int i = 1; i <= 3; i++) {
sprintf(filename, "jsonchecker/pass%d.json", i); sprintf(filename, "jsonchecker/pass%d.json", i);
size_t length; size_t length;
char* json = ReadFile(filename, length); char* json = ReadFile(filename, length);
if (!json) { if (!json) {
sprintf(filename, "../../bin/jsonchecker/pass%d.json", i); sprintf(filename, "../../bin/jsonchecker/pass%d.json", i);
json = ReadFile(filename, length); json = ReadFile(filename, length);
if (!json) { if (!json) {
printf("jsonchecker file %s not found", filename); printf("jsonchecker file %s not found", filename);
continue; continue;
} }
} }
GenericDocument<UTF8<>, CrtAllocator> document; // Use Crt allocator to check exception-safety (no memory leak) GenericDocument<UTF8<>, CrtAllocator> document; // Use Crt allocator to check exception-safety (no memory leak)
document.Parse((const char*)json); document.Parse((const char*)json);
EXPECT_TRUE(!document.HasParseError()); EXPECT_TRUE(!document.HasParseError());
free(json); free(json);
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +1,30 @@
// Copyright (C) 2011 Milo Yip
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "unittest.h" #include "unittest.h"
int main(int argc, char **argv) { int main(int argc, char **argv) {
#if _MSC_VER #if _MSC_VER
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ); _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
//void *testWhetherMemoryLeakDetectionWorks = malloc(1); //void *testWhetherMemoryLeakDetectionWorks = malloc(1);
#endif #endif
::testing::InitGoogleTest(&argc, argv); ::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS(); return RUN_ALL_TESTS();
} }

View File

@ -1,3 +1,23 @@
// Copyright (C) 2011 Milo Yip
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#ifndef UNITTEST_H_ #ifndef UNITTEST_H_
#define UNITTEST_H_ #define UNITTEST_H_
@ -28,32 +48,32 @@
template <typename Ch> template <typename Ch>
inline unsigned StrLen(const Ch* s) { inline unsigned StrLen(const Ch* s) {
const Ch* p = s; const Ch* p = s;
while (*p) p++; while (*p) p++;
return unsigned(p - s); return unsigned(p - s);
} }
template<typename Ch> template<typename Ch>
inline int StrCmp(const Ch* s1, const Ch* s2) { inline int StrCmp(const Ch* s1, const Ch* s2) {
while(*s1 && (*s1 == *s2)) { s1++; s2++; } while(*s1 && (*s1 == *s2)) { s1++; s2++; }
return (unsigned)*s1 < (unsigned)*s2 ? -1 : (unsigned)*s1 > (unsigned)*s2; return (unsigned)*s1 < (unsigned)*s2 ? -1 : (unsigned)*s1 > (unsigned)*s2;
} }
template <typename Ch> template <typename Ch>
inline Ch* StrDup(const Ch* str) { inline Ch* StrDup(const Ch* str) {
size_t bufferSize = sizeof(Ch) * (StrLen(str) + 1); size_t bufferSize = sizeof(Ch) * (StrLen(str) + 1);
Ch* buffer = (Ch*)malloc(bufferSize); Ch* buffer = (Ch*)malloc(bufferSize);
memcpy(buffer, str, bufferSize); memcpy(buffer, str, bufferSize);
return buffer; return buffer;
} }
inline void TempFilename(char *filename) { inline void TempFilename(char *filename) {
filename = tmpnam(filename); filename = tmpnam(filename);
// For Visual Studio, tmpnam() adds a backslash in front. Remove it. // For Visual Studio, tmpnam() adds a backslash in front. Remove it.
if (filename[0] == '\\') if (filename[0] == '\\')
for (int i = 0; filename[i] != '\0'; i++) for (int i = 0; filename[i] != '\0'; i++)
filename[i] = filename[i + 1]; filename[i] = filename[i + 1];
} }
// Use exception for catching assert // Use exception for catching assert
@ -63,13 +83,13 @@ inline void TempFilename(char *filename) {
class AssertException : public std::exception { class AssertException : public std::exception {
public: public:
AssertException(const char* w) : what_(w) {} AssertException(const char* w) : what_(w) {}
AssertException(const AssertException& other) : what_(other.what_) {} AssertException(const AssertException& other) : what_(other.what_) {}
AssertException& operator=(const AssertException& rhs) { what_ = rhs.what_; return *this; } AssertException& operator=(const AssertException& rhs) { what_ = rhs.what_; return *this; }
virtual const char* what() const throw() { return what_; } virtual const char* what() const throw() { return what_; }
private: private:
const char* what_; const char* what_;
}; };
#define RAPIDJSON_ASSERT(x) if (!(x)) throw AssertException(RAPIDJSON_STRINGIFY(x)) #define RAPIDJSON_ASSERT(x) if (!(x)) throw AssertException(RAPIDJSON_STRINGIFY(x))

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,23 @@
// Copyright (C) 2011 Milo Yip
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "unittest.h" #include "unittest.h"
#include "rapidjson/document.h" #include "rapidjson/document.h"
@ -8,235 +28,235 @@
using namespace rapidjson; using namespace rapidjson;
TEST(Writer, Compact) { TEST(Writer, Compact) {
StringStream s("{ \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3] } "); StringStream s("{ \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3] } ");
StringBuffer buffer; StringBuffer buffer;
Writer<StringBuffer> writer(buffer); Writer<StringBuffer> writer(buffer);
Reader reader; Reader reader;
reader.Parse<0>(s, writer); reader.Parse<0>(s, writer);
EXPECT_STREQ("{\"hello\":\"world\",\"t\":true,\"f\":false,\"n\":null,\"i\":123,\"pi\":3.1416,\"a\":[1,2,3]}", buffer.GetString()); EXPECT_STREQ("{\"hello\":\"world\",\"t\":true,\"f\":false,\"n\":null,\"i\":123,\"pi\":3.1416,\"a\":[1,2,3]}", buffer.GetString());
EXPECT_EQ(77u, buffer.GetSize()); EXPECT_EQ(77u, buffer.GetSize());
EXPECT_TRUE(writer.IsComplete()); EXPECT_TRUE(writer.IsComplete());
} }
// json -> parse -> writer -> json // json -> parse -> writer -> json
#define TEST_ROUNDTRIP(json) \ #define TEST_ROUNDTRIP(json) \
{ \ { \
StringStream s(json); \ StringStream s(json); \
StringBuffer buffer; \ StringBuffer buffer; \
Writer<StringBuffer> writer(buffer); \ Writer<StringBuffer> writer(buffer); \
Reader reader; \ Reader reader; \
reader.Parse<0>(s, writer); \ reader.Parse<0>(s, writer); \
EXPECT_STREQ(json, buffer.GetString()); \ EXPECT_STREQ(json, buffer.GetString()); \
EXPECT_TRUE(writer.IsComplete()); \ EXPECT_TRUE(writer.IsComplete()); \
} }
TEST(Writer, Int) { TEST(Writer, Int) {
TEST_ROUNDTRIP("[-1]"); TEST_ROUNDTRIP("[-1]");
TEST_ROUNDTRIP("[-123]"); TEST_ROUNDTRIP("[-123]");
TEST_ROUNDTRIP("[-2147483648]"); TEST_ROUNDTRIP("[-2147483648]");
} }
TEST(Writer, UInt) { TEST(Writer, UInt) {
TEST_ROUNDTRIP("[0]"); TEST_ROUNDTRIP("[0]");
TEST_ROUNDTRIP("[1]"); TEST_ROUNDTRIP("[1]");
TEST_ROUNDTRIP("[123]"); TEST_ROUNDTRIP("[123]");
TEST_ROUNDTRIP("[2147483647]"); TEST_ROUNDTRIP("[2147483647]");
TEST_ROUNDTRIP("[4294967295]"); TEST_ROUNDTRIP("[4294967295]");
} }
TEST(Writer, Int64) { TEST(Writer, Int64) {
TEST_ROUNDTRIP("[-1234567890123456789]"); TEST_ROUNDTRIP("[-1234567890123456789]");
TEST_ROUNDTRIP("[-9223372036854775808]"); TEST_ROUNDTRIP("[-9223372036854775808]");
} }
TEST(Writer, Uint64) { TEST(Writer, Uint64) {
TEST_ROUNDTRIP("[1234567890123456789]"); TEST_ROUNDTRIP("[1234567890123456789]");
TEST_ROUNDTRIP("[9223372036854775807]"); TEST_ROUNDTRIP("[9223372036854775807]");
} }
TEST(Writer, String) { TEST(Writer, String) {
TEST_ROUNDTRIP("[\"Hello\"]"); TEST_ROUNDTRIP("[\"Hello\"]");
TEST_ROUNDTRIP("[\"Hello\\u0000World\"]"); TEST_ROUNDTRIP("[\"Hello\\u0000World\"]");
TEST_ROUNDTRIP("[\"\\\"\\\\/\\b\\f\\n\\r\\t\"]"); TEST_ROUNDTRIP("[\"\\\"\\\\/\\b\\f\\n\\r\\t\"]");
} }
TEST(Writer, Double) { TEST(Writer, Double) {
TEST_ROUNDTRIP("[1.2345,1.2345678,0.123456789012,1234567.8]"); TEST_ROUNDTRIP("[1.2345,1.2345678,0.123456789012,1234567.8]");
} }
TEST(Writer, Transcode) { TEST(Writer, Transcode) {
const char json[] = "{\"hello\":\"world\",\"t\":true,\"f\":false,\"n\":null,\"i\":123,\"pi\":3.1416,\"a\":[1,2,3],\"dollar\":\"\x24\",\"cents\":\"\xC2\xA2\",\"euro\":\"\xE2\x82\xAC\",\"gclef\":\"\xF0\x9D\x84\x9E\"}"; const char json[] = "{\"hello\":\"world\",\"t\":true,\"f\":false,\"n\":null,\"i\":123,\"pi\":3.1416,\"a\":[1,2,3],\"dollar\":\"\x24\",\"cents\":\"\xC2\xA2\",\"euro\":\"\xE2\x82\xAC\",\"gclef\":\"\xF0\x9D\x84\x9E\"}";
// UTF8 -> UTF16 -> UTF8 // UTF8 -> UTF16 -> UTF8
{ {
StringStream s(json); StringStream s(json);
StringBuffer buffer; StringBuffer buffer;
Writer<StringBuffer, UTF16<>, UTF8<> > writer(buffer); Writer<StringBuffer, UTF16<>, UTF8<> > writer(buffer);
GenericReader<UTF8<>, UTF16<> > reader; GenericReader<UTF8<>, UTF16<> > reader;
reader.Parse(s, writer); reader.Parse(s, writer);
EXPECT_STREQ(json, buffer.GetString()); EXPECT_STREQ(json, buffer.GetString());
} }
// UTF8 -> UTF8 -> ASCII -> UTF8 -> UTF8 // UTF8 -> UTF8 -> ASCII -> UTF8 -> UTF8
{ {
StringStream s(json); StringStream s(json);
StringBuffer buffer; StringBuffer buffer;
Writer<StringBuffer, UTF8<>, ASCII<> > writer(buffer); Writer<StringBuffer, UTF8<>, ASCII<> > writer(buffer);
Reader reader; Reader reader;
reader.Parse(s, writer); reader.Parse(s, writer);
StringBuffer buffer2; StringBuffer buffer2;
Writer<StringBuffer> writer2(buffer2); Writer<StringBuffer> writer2(buffer2);
GenericReader<ASCII<>, UTF8<> > reader2; GenericReader<ASCII<>, UTF8<> > reader2;
StringStream s2(buffer.GetString()); StringStream s2(buffer.GetString());
reader2.Parse(s2, writer2); reader2.Parse(s2, writer2);
EXPECT_STREQ(json, buffer2.GetString()); EXPECT_STREQ(json, buffer2.GetString());
} }
} }
#include <sstream> #include <sstream>
class OStreamWrapper { class OStreamWrapper {
public: public:
typedef char Ch; typedef char Ch;
OStreamWrapper(std::ostream& os) : os_(os) {} OStreamWrapper(std::ostream& os) : os_(os) {}
Ch Peek() const { assert(false); return '\0'; } Ch Peek() const { assert(false); return '\0'; }
Ch Take() { assert(false); return '\0'; } Ch Take() { assert(false); return '\0'; }
size_t Tell() const { return 0; } size_t Tell() const { return 0; }
Ch* PutBegin() { assert(false); return 0; } Ch* PutBegin() { assert(false); return 0; }
void Put(Ch c) { os_.put(c); } void Put(Ch c) { os_.put(c); }
void Flush() { os_.flush(); } void Flush() { os_.flush(); }
size_t PutEnd(Ch*) { assert(false); return 0; } size_t PutEnd(Ch*) { assert(false); return 0; }
private: private:
OStreamWrapper(const OStreamWrapper&); OStreamWrapper(const OStreamWrapper&);
OStreamWrapper& operator=(const OStreamWrapper&); OStreamWrapper& operator=(const OStreamWrapper&);
std::ostream& os_; std::ostream& os_;
}; };
TEST(Writer, OStreamWrapper) { TEST(Writer, OStreamWrapper) {
StringStream s("{ \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3] } "); StringStream s("{ \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3] } ");
std::stringstream ss; std::stringstream ss;
OStreamWrapper os(ss); OStreamWrapper os(ss);
Writer<OStreamWrapper> writer(os); Writer<OStreamWrapper> writer(os);
Reader reader; Reader reader;
reader.Parse<0>(s, writer); reader.Parse<0>(s, writer);
std::string actual = ss.str(); std::string actual = ss.str();
EXPECT_STREQ("{\"hello\":\"world\",\"t\":true,\"f\":false,\"n\":null,\"i\":123,\"pi\":3.1416,\"a\":[1,2,3]}", actual.c_str()); EXPECT_STREQ("{\"hello\":\"world\",\"t\":true,\"f\":false,\"n\":null,\"i\":123,\"pi\":3.1416,\"a\":[1,2,3]}", actual.c_str());
} }
TEST(Writer, AssertRootMustBeArrayOrObject) { TEST(Writer, AssertRootMustBeArrayOrObject) {
#define T(x)\ #define T(x)\
{\ {\
StringBuffer buffer;\ StringBuffer buffer;\
Writer<StringBuffer> writer(buffer);\ Writer<StringBuffer> writer(buffer);\
ASSERT_THROW(x, AssertException);\ ASSERT_THROW(x, AssertException);\
} }
T(writer.Bool(false)); T(writer.Bool(false));
T(writer.Bool(true)); T(writer.Bool(true));
T(writer.Null()); T(writer.Null());
T(writer.Int(0)); T(writer.Int(0));
T(writer.Uint(0)); T(writer.Uint(0));
T(writer.Int64(0)); T(writer.Int64(0));
T(writer.Uint64(0)); T(writer.Uint64(0));
T(writer.Double(0)); T(writer.Double(0));
T(writer.String("foo")); T(writer.String("foo"));
#undef T #undef T
} }
TEST(Writer, AssertIncorrectObjectLevel) { TEST(Writer, AssertIncorrectObjectLevel) {
StringBuffer buffer; StringBuffer buffer;
Writer<StringBuffer> writer(buffer); Writer<StringBuffer> writer(buffer);
writer.StartObject(); writer.StartObject();
writer.EndObject(); writer.EndObject();
ASSERT_THROW(writer.EndObject(), AssertException); ASSERT_THROW(writer.EndObject(), AssertException);
} }
TEST(Writer, AssertIncorrectArrayLevel) { TEST(Writer, AssertIncorrectArrayLevel) {
StringBuffer buffer; StringBuffer buffer;
Writer<StringBuffer> writer(buffer); Writer<StringBuffer> writer(buffer);
writer.StartArray(); writer.StartArray();
writer.EndArray(); writer.EndArray();
ASSERT_THROW(writer.EndArray(), AssertException); ASSERT_THROW(writer.EndArray(), AssertException);
} }
TEST(Writer, AssertIncorrectEndObject) { TEST(Writer, AssertIncorrectEndObject) {
StringBuffer buffer; StringBuffer buffer;
Writer<StringBuffer> writer(buffer); Writer<StringBuffer> writer(buffer);
writer.StartObject(); writer.StartObject();
ASSERT_THROW(writer.EndArray(), AssertException); ASSERT_THROW(writer.EndArray(), AssertException);
} }
TEST(Writer, AssertIncorrectEndArray) { TEST(Writer, AssertIncorrectEndArray) {
StringBuffer buffer; StringBuffer buffer;
Writer<StringBuffer> writer(buffer); Writer<StringBuffer> writer(buffer);
writer.StartObject(); writer.StartObject();
ASSERT_THROW(writer.EndArray(), AssertException); ASSERT_THROW(writer.EndArray(), AssertException);
} }
TEST(Writer, AssertObjectKeyNotString) { TEST(Writer, AssertObjectKeyNotString) {
#define T(x)\ #define T(x)\
{\ {\
StringBuffer buffer;\ StringBuffer buffer;\
Writer<StringBuffer> writer(buffer);\ Writer<StringBuffer> writer(buffer);\
writer.StartObject();\ writer.StartObject();\
ASSERT_THROW(x, AssertException); \ ASSERT_THROW(x, AssertException); \
} }
T(writer.Bool(false)); T(writer.Bool(false));
T(writer.Bool(true)); T(writer.Bool(true));
T(writer.Null()); T(writer.Null());
T(writer.Int(0)); T(writer.Int(0));
T(writer.Uint(0)); T(writer.Uint(0));
T(writer.Int64(0)); T(writer.Int64(0));
T(writer.Uint64(0)); T(writer.Uint64(0));
T(writer.Double(0)); T(writer.Double(0));
T(writer.StartObject()); T(writer.StartObject());
T(writer.StartArray()); T(writer.StartArray());
#undef T #undef T
} }
TEST(Writer, AssertMultipleRoot) { TEST(Writer, AssertMultipleRoot) {
StringBuffer buffer; StringBuffer buffer;
Writer<StringBuffer> writer(buffer); Writer<StringBuffer> writer(buffer);
writer.StartObject(); writer.StartObject();
writer.EndObject(); writer.EndObject();
ASSERT_THROW(writer.StartObject(), AssertException); ASSERT_THROW(writer.StartObject(), AssertException);
} }
TEST(Writer, RootObjectIsComplete) { TEST(Writer, RootObjectIsComplete) {
StringBuffer buffer; StringBuffer buffer;
Writer<StringBuffer> writer(buffer); Writer<StringBuffer> writer(buffer);
EXPECT_FALSE(writer.IsComplete()); EXPECT_FALSE(writer.IsComplete());
writer.StartObject(); writer.StartObject();
EXPECT_FALSE(writer.IsComplete()); EXPECT_FALSE(writer.IsComplete());
writer.String("foo"); writer.String("foo");
EXPECT_FALSE(writer.IsComplete()); EXPECT_FALSE(writer.IsComplete());
writer.Int(1); writer.Int(1);
EXPECT_FALSE(writer.IsComplete()); EXPECT_FALSE(writer.IsComplete());
writer.EndObject(); writer.EndObject();
EXPECT_TRUE(writer.IsComplete()); EXPECT_TRUE(writer.IsComplete());
} }
TEST(Writer, RootArrayIsComplete) { TEST(Writer, RootArrayIsComplete) {
StringBuffer buffer; StringBuffer buffer;
Writer<StringBuffer> writer(buffer); Writer<StringBuffer> writer(buffer);
EXPECT_FALSE(writer.IsComplete()); EXPECT_FALSE(writer.IsComplete());
writer.StartArray(); writer.StartArray();
EXPECT_FALSE(writer.IsComplete()); EXPECT_FALSE(writer.IsComplete());
writer.String("foo"); writer.String("foo");
EXPECT_FALSE(writer.IsComplete()); EXPECT_FALSE(writer.IsComplete());
writer.Int(1); writer.Int(1);
EXPECT_FALSE(writer.IsComplete()); EXPECT_FALSE(writer.IsComplete());
writer.EndArray(); writer.EndArray();
EXPECT_TRUE(writer.IsComplete()); EXPECT_TRUE(writer.IsComplete());
} }