Commit 57b91300 authored by Milo Yip's avatar Milo Yip

Merge remote-tracking branch 'origin/master' into issue120floatprecision_customstrtod

Conflicts:
	include/rapidjson/internal/dtoa.h
	test/unittest/readertest.cpp
parents 22ca9312 8d4405cf
......@@ -6,5 +6,6 @@
/build/gmake
/build/vs*/
/doc/html
/doc/doxygen_*.db
/thirdparty/lib
/intermediate
......@@ -24,11 +24,15 @@ before_install:
install: true
before_script:
- pushd build && premake4 'gmake' && popd
- (cd build && premake4 'gmake')
# hack to avoid Valgrind bug (https://bugs.kde.org/show_bug.cgi?id=326469),
# exposed by merging PR#163 (using -march=native)
- (cd build/gmake && sed -i 's/march=native/msse4.2/' *.make)
script:
- make -C build/gmake -f test.make config=${CONF}${BITS}
- make -C build/gmake -f example.make config=${CONF}${BITS}
- if [ "$CONF" = "debug" ] && ( objdump -t -C intermediate/${CONF}/gmake/unittest/x${BITS}/namespacetest.o | grep rapidjson ) ; then echo "Symbol check failed!" ; false; fi
- pushd bin
- ./unittest_${CONF}_x${BITS}_gmake
- valgrind --leak-check=full --error-exitcode=1 ./unittest_${CONF}_x${BITS}_gmake
......
......@@ -1993,6 +1993,8 @@ INCLUDE_FILE_PATTERNS =
PREDEFINED = \
RAPIDJSON_DOXYGEN_RUNNING \
RAPIDJSON_NAMESPACE_BEGIN="namespace rapidjson {" \
RAPIDJSON_NAMESPACE_END="}" \
RAPIDJSON_REMOVEFPTR_(x)=x \
RAPIDJSON_ENABLEIF_RETURN(cond,returntype)="RAPIDJSON_REMOVEFPTR_ returntype" \
RAPIDJSON_DISABLEIF_RETURN(cond,returntype)="RAPIDJSON_REMOVEFPTR_ returntype"
......
......@@ -64,7 +64,7 @@ solution "test"
defines { "_CRT_SECURE_NO_WARNINGS" }
configuration "gmake"
buildoptions "-msse4.2 -Wall -Wextra"
buildoptions "-march=native -Wall -Wextra"
project "gtest"
kind "StaticLib"
......@@ -114,7 +114,6 @@ solution "test"
files {
"../include/**.h",
"../test/perftest/**.cpp",
"../test/perftest/**.c",
"../test/perftest/**.h",
}
......@@ -122,9 +121,6 @@ solution "test"
"../include/",
"../thirdparty/gtest/include/",
"../thirdparty/",
"../thirdparty/jsoncpp/include/",
"../thirdparty/libjson/",
"../thirdparty/yajl/include/",
}
libdirs "../thirdparty/lib"
......
......@@ -2,9 +2,15 @@
The old performance article for RapidJSON 0.1 is provided [here](https://code.google.com/p/rapidjson/wiki/Performance).
This file will be updated with new version and better procedures.
The (third-party) performance tests have been removed from this repository
and are now part of a dedicated [native JSON benchmark collection] [1].
In the meantime, you may also refer to the following third-party benchmarks.
This file will be updated with a summary of benchmarking results based on
the above benchmark collection in the future.
[1]: https://github.com/miloyip/nativejson-benchmark
Additionally, you may refer to the following third-party benchmarks.
## Third-party benchmarks
......
......@@ -27,21 +27,21 @@ For example, here is a JSON.
While a `Reader` parses this JSON, it publishes the following events to the handler sequentially:
~~~~~~~~~~
BeginObject()
String("hello", 5, true)
StartObject()
Key("hello", 5, true)
String("world", 5, true)
String("t", 1, true)
Key("t", 1, true)
Bool(true)
String("f", 1, true)
Key("f", 1, true)
Bool(false)
String("n", 1, true)
Key("n", 1, true)
Null()
String("i")
Key("i")
UInt(123)
String("pi")
Key("pi")
Double(3.1416)
String("a")
BeginArray()
Key("a")
StartArray()
Uint(1)
Uint(2)
Uint(3)
......@@ -72,6 +72,10 @@ struct MyHandler {
return true;
}
bool StartObject() { cout << "StartObject()" << endl; return true; }
bool Key(const char* str, SizeType length, bool copy) {
cout << "Key(" << str << ", " << length << ", " << boolalpha << copy << ")" << endl;
return true;
}
bool EndObject(SizeType memberCount) { cout << "EndObject(" << memberCount << ")" << endl; return true; }
bool StartArray() { cout << "StartArray()" << endl; return true; }
bool EndArray(SizeType elementCount) { cout << "EndArray(" << elementCount << ")" << endl; return true; }
......@@ -104,6 +108,7 @@ class Handler {
bool Double(double d);
bool String(const Ch* str, SizeType length, bool copy);
bool StartObject();
bool Key(const Ch* str, SizeType length, bool copy);
bool EndObject(SizeType memberCount);
bool StartArray();
bool EndArray(SizeType elementCount);
......@@ -118,7 +123,7 @@ When the `Reader` encounters a JSON number, it chooses a suitable C++ type mappi
`String(const char* str, SizeType length, bool copy)` is called when the `Reader` encounters a string. The first parameter is pointer to the string. The second parameter is the length of the string (excluding the null terminator). Note that RapidJSON supports null character `'\0'` inside a string. If such situation happens, `strlen(str) < length`. The last `copy` indicates whether the handler needs to make a copy of the string. For normal parsing, `copy = true`. Only when *insitu* parsing is used, `copy = false`. And beware that, the character type depends on the target encoding, which will be explained later.
When the `Reader` encounters the beginning of an object, it calls `StartObject()`. An object in JSON is a set of name-value pairs. If the object contains members it first calls `String()` for the name of member, and then calls functions depending on the type of the value. These calls of name-value pairs repeats until calling `EndObject(SizeType memberCount)`. Note that the `memberCount` parameter is just an aid for the handler, user may not need this parameter.
When the `Reader` encounters the beginning of an object, it calls `StartObject()`. An object in JSON is a set of name-value pairs. If the object contains members it first calls `Key()` for the name of member, and then calls functions depending on the type of the value. These calls of name-value pairs repeats until calling `EndObject(SizeType memberCount)`. Note that the `memberCount` parameter is just an aid for the handler, user may not need this parameter.
Array is similar to object but simpler. At the beginning of an array, the `Reader` calls `BeginArary()`. If there is elements, it calls functions according to the types of element. Similarly, in the last call `EndArray(SizeType elementCount)`, the parameter `elementCount` is just an aid for the handler.
......@@ -189,19 +194,19 @@ void main() {
Writer<StringBuffer> writer(s);
writer.StartObject();
writer.String("hello");
writer.Key("hello");
writer.String("world");
writer.String("t");
writer.Key("t");
writer.Bool(true);
writer.String("f");
writer.Key("f");
writer.Bool(false);
writer.String("n");
writer.Key("n");
writer.Null();
writer.String("i");
writer.Key("i");
writer.Uint(123);
writer.String("pi");
writer.Key("pi");
writer.Double(3.1416);
writer.String("a");
writer.Key("a");
writer.StartArray();
for (unsigned i = 0; i < 4; i++)
writer.Uint(i);
......@@ -216,7 +221,7 @@ void main() {
{"hello":"world","t":true,"f":false,"n":null,"i":123,"pi":3.1416,"a":[0,1,2,3]}
~~~~~~~~~~
There is two `String()` overloads. One is the same as defined in handler concept with 3 parameters. It can handle string with null characters. Another one is the simpler version used in the above example.
There are two `String()` and `Key()` overloads. One is the same as defined in handler concept with 3 parameters. It can handle string with null characters. Another one is the simpler version used in the above example.
Note that, the example code does not pass any parameters in `EndArray()` and `EndObject()`. An `SizeType` can be passed but it will be simply ignored by `Writer`.
......@@ -294,7 +299,8 @@ using namespace rapidjson;
typedef map<string, string> MessageMap;
struct MessageHandler : public BaseReaderHandler<> {
struct MessageHandler
: public BaseReaderHandler<UTF8<>, MessageHandler> {
MessageHandler() : state_(kExpectObjectStart) {
}
......@@ -420,6 +426,7 @@ struct CapitalizeFilter {
return out_.String(&buffer_.front(), length, true); // true = output handler need to copy the string
}
bool StartObject() { return out_.StartObject(); }
bool Key(const char* str, SizeType length, bool copy) { return String(str, length, copy); }
bool EndObject(SizeType memberCount) { return out_.EndObject(memberCount); }
bool StartArray() { return out_.StartArray(); }
bool EndArray(SizeType elementCount) { return out_.EndArray(elementCount); }
......
# Stream
In RapidJSON, `rapidjson::Stream` is a concept for reading/writing JSON. Here we first show how to use streams provided. And then see how to create a custom streams.
In RapidJSON, `rapidjson::Stream` is a concept for reading/writing JSON. Here we first show how to use streams provided. And then see how to create a custom stream.
[TOC]
......
......@@ -121,9 +121,7 @@ In the following, details about querying individual types are discussed.
By default, `SizeType` is typedef of `unsigned`. In most systems, array is limited to store up to 2^32-1 elements.
You may access the elements in array by integer literal, for example, `a[1]`, `a[2]`. However, `a[0]` will generate a compiler error. It is because two overloaded operators `operator[](SizeType)` and `operator[](const char*)` is available, and C++ can treat `0` as a null pointer. Workarounds:
* `a[SizeType(0)]`
* `a[0u]`
You may access the elements in array by integer literal, for example, `a[0]`, `a[1]`, `a[2]`.
Array is similar to `std::vector`, instead of using indices, you may also use iterator to access all the elements.
~~~~~~~~~~cpp
......@@ -334,9 +332,10 @@ Sometimes, it is convenient to construct a Value in place, before passing it to
~~~~~~~~~~cpp
Value a(kArrayType);
// a.PushBack(Value(42)); // will not compile
a.PushBack(Value().SetInt(42)); // fluent API
a.PushBack(Value(42).Move()); // same as above
Document::AllocatorType& allocator = document.GetAllocator();
// a.PushBack(Value(42), allocator); // will not compile
a.PushBack(Value().SetInt(42), allocator); // fluent API
a.PushBack(Value(42).Move(), allocator); // same as above
~~~~~~~~~~
## Create String {#CreateString}
......
......@@ -17,7 +17,8 @@ RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(effc++)
#endif
struct MessageHandler : public BaseReaderHandler<> {
struct MessageHandler
: public BaseReaderHandler<UTF8<>, MessageHandler> {
MessageHandler() : messages_(), state_(kExpectObjectStart), name_() {}
bool StartObject() {
......
......@@ -72,12 +72,8 @@ int main(int, char*[]) {
for (SizeType i = 0; i < a.Size(); i++) // rapidjson uses SizeType instead of size_t.
printf("a[%d] = %d\n", i, a[i].GetInt());
// Note:
//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 z = a[0u].GetInt(); // This works too.
int y = a[0].GetInt();
(void)y;
(void)z;
// Iterating array with iterators
printf("a = ");
......
......@@ -23,7 +23,7 @@
#include "rapidjson.h"
namespace rapidjson {
RAPIDJSON_NAMESPACE_BEGIN
///////////////////////////////////////////////////////////////////////////////
// Allocator
......@@ -68,9 +68,9 @@ concept Allocator {
class CrtAllocator {
public:
static const bool kNeedFree = true;
void* Malloc(size_t size) { return malloc(size); }
void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) { (void)originalSize; return realloc(originalPtr, newSize); }
static void Free(void *ptr) { free(ptr); }
void* Malloc(size_t size) { return std::malloc(size); }
void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) { (void)originalSize; return std::realloc(originalPtr, newSize); }
static void Free(void *ptr) { std::free(ptr); }
};
///////////////////////////////////////////////////////////////////////////////
......@@ -104,9 +104,6 @@ public:
MemoryPoolAllocator(size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) :
chunkHead_(0), chunk_capacity_(chunkSize), userBuffer_(0), baseAllocator_(baseAllocator), ownBaseAllocator_(0)
{
if (!baseAllocator_)
ownBaseAllocator_ = baseAllocator_ = new BaseAllocator();
AddChunk(chunk_capacity_);
}
//! Constructor with user-supplied buffer.
......@@ -135,7 +132,7 @@ public:
*/
~MemoryPoolAllocator() {
Clear();
delete ownBaseAllocator_;
RAPIDJSON_DELETE(ownBaseAllocator_);
}
//! Deallocates all memory chunks, excluding the user-supplied buffer.
......@@ -170,7 +167,7 @@ public:
//! Allocates a memory block. (concept Allocator)
void* Malloc(size_t size) {
size = RAPIDJSON_ALIGN(size);
if (chunkHead_->size + size > chunkHead_->capacity)
if (chunkHead_ == 0 || chunkHead_->size + size > chunkHead_->capacity)
AddChunk(chunk_capacity_ > size ? chunk_capacity_ : size);
void *buffer = reinterpret_cast<char *>(chunkHead_ + 1) + chunkHead_->size;
......@@ -200,7 +197,7 @@ public:
// Realloc process: allocate and copy memory, do not free original buffer.
void* newBuffer = Malloc(newSize);
RAPIDJSON_ASSERT(newBuffer != 0); // Do not handle out-of-memory explicitly.
return memcpy(newBuffer, originalPtr, originalSize);
return std::memcpy(newBuffer, originalPtr, originalSize);
}
//! Frees a memory block (concept Allocator)
......@@ -216,6 +213,8 @@ private:
/*! \param capacity Capacity of the chunk in bytes.
*/
void AddChunk(size_t capacity) {
if (!baseAllocator_)
ownBaseAllocator_ = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator());
ChunkHeader* chunk = reinterpret_cast<ChunkHeader*>(baseAllocator_->Malloc(sizeof(ChunkHeader) + capacity));
chunk->capacity = capacity;
chunk->size = 0;
......@@ -241,6 +240,6 @@ private:
BaseAllocator* ownBaseAllocator_; //!< base allocator created by this object.
};
} // namespace rapidjson
RAPIDJSON_NAMESPACE_END
#endif // RAPIDJSON_ENCODINGS_H_
This diff is collapsed.
......@@ -28,7 +28,7 @@ RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(effc++)
#endif
namespace rapidjson {
RAPIDJSON_NAMESPACE_BEGIN
//! Input byte stream wrapper with a statically bound encoding.
/*!
......@@ -281,7 +281,7 @@ private:
#undef RAPIDJSON_ENCODINGS_FUNC
} // namespace rapidjson
RAPIDJSON_NAMESPACE_END
#ifdef __GNUC__
RAPIDJSON_DIAG_POP
......
......@@ -32,7 +32,7 @@ RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(effc++)
#endif
namespace rapidjson {
RAPIDJSON_NAMESPACE_BEGIN
///////////////////////////////////////////////////////////////////////////////
// Encoding
......@@ -621,7 +621,7 @@ struct Transcoder<Encoding, Encoding> {
}
};
} // namespace rapidjson
RAPIDJSON_NAMESPACE_END
#if defined(__GNUC__) || defined(_MSV_VER)
RAPIDJSON_DIAG_POP
......
......@@ -23,7 +23,7 @@
#include "error.h"
namespace rapidjson {
RAPIDJSON_NAMESPACE_BEGIN
//! Maps error code of parsing into error message.
/*!
......@@ -66,6 +66,6 @@ inline const RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErro
}
}
} // namespace rapidjson
RAPIDJSON_NAMESPACE_END
#endif // RAPIDJSON_ERROR_EN_H__
......@@ -51,7 +51,7 @@
#define RAPIDJSON_ERROR_STRING(x) x
#endif
namespace rapidjson {
RAPIDJSON_NAMESPACE_BEGIN
///////////////////////////////////////////////////////////////////////////////
// ParseErrorCode
......@@ -145,6 +145,6 @@ private:
*/
typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetParseErrorFunc)(ParseErrorCode);
} // namespace rapidjson
RAPIDJSON_NAMESPACE_END
#endif // RAPIDJSON_ERROR_ERROR_H__
......@@ -24,7 +24,7 @@
#include "rapidjson.h"
#include <cstdio>
namespace rapidjson {
RAPIDJSON_NAMESPACE_BEGIN
//! File byte stream for input using fread().
/*!
......@@ -89,6 +89,6 @@ private:
bool eof_;
};
} // namespace rapidjson
RAPIDJSON_NAMESPACE_END
#endif // RAPIDJSON_FILESTREAM_H_
......@@ -24,9 +24,9 @@
#include "rapidjson.h"
#include <cstdio>
namespace rapidjson {
RAPIDJSON_NAMESPACE_BEGIN
//! (Depreciated) Wrapper of C file stream for input or output.
//! (Deprecated) Wrapper of C file stream for input or output.
/*!
This simple wrapper does not check the validity of the stream.
\note implements Stream concept
......@@ -68,6 +68,6 @@ private:
size_t count_;
};
} // namespace rapidjson
RAPIDJSON_NAMESPACE_END
#endif // RAPIDJSON_FILESTREAM_H_
......@@ -24,7 +24,7 @@
#include "rapidjson.h"
#include <cstdio>
namespace rapidjson {
RAPIDJSON_NAMESPACE_BEGIN
//! Wrapper of C file stream for input using fread().
/*!
......@@ -48,7 +48,7 @@ public:
void PutN(char c, size_t n) {
size_t avail = static_cast<size_t>(bufferEnd_ - current_);
while (n > avail) {
memset(current_, c, avail);
std::memset(current_, c, avail);
current_ += avail;
Flush();
n -= avail;
......@@ -56,7 +56,7 @@ public:
}
if (n > 0) {
memset(current_, c, n);
std::memset(current_, c, n);
current_ += n;
}
}
......@@ -92,6 +92,6 @@ inline void PutN(FileWriteStream& stream, char c, size_t n) {
stream.PutN(c, n);
}
} // namespace rapidjson
RAPIDJSON_NAMESPACE_END
#endif // RAPIDJSON_FILESTREAM_H_
......@@ -28,7 +28,7 @@
#include "itoa.h" // GetDigitsLut()
#include "diyfp.h"
namespace rapidjson {
RAPIDJSON_NAMESPACE_BEGIN
namespace internal {
#ifdef __GNUC__
......@@ -91,7 +91,7 @@ inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buff
#endif
}
if (d || *len)
buffer[(*len)++] = '0' + static_cast<char>(d);
buffer[(*len)++] = static_cast<char>('0' + static_cast<char>(d));
kappa--;
uint64_t tmp = (static_cast<uint64_t>(p1) << -one.e) + p2;
if (tmp <= delta) {
......@@ -107,7 +107,7 @@ inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buff
delta *= 10;
char d = static_cast<char>(p2 >> -one.e);
if (d || *len)
buffer[(*len)++] = '0' + d;
buffer[(*len)++] = static_cast<char>('0' + d);
p2 &= one.f - 1;
kappa--;
if (p2 < delta) {
......@@ -139,7 +139,7 @@ inline char* WriteExponent(int K, char* buffer) {
}
if (K >= 100) {
*buffer++ = '0' + static_cast<char>(K / 100);
*buffer++ = static_cast<char>('0' + static_cast<char>(K / 100));
K %= 100;
const char* d = GetDigitsLut() + K * 2;
*buffer++ = d[0];
......@@ -151,7 +151,7 @@ inline char* WriteExponent(int K, char* buffer) {
*buffer++ = d[1];
}
else
*buffer++ = '0' + static_cast<char>(K);
*buffer++ = static_cast<char>('0' + static_cast<char>(K));
return buffer;
}
......@@ -169,14 +169,14 @@ inline char* Prettify(char* buffer, int length, int k) {
}
else if (0 < kk && kk <= 21) {
// 1234e-2 -> 12.34
memmove(&buffer[kk + 1], &buffer[kk], length - kk);
std::memmove(&buffer[kk + 1], &buffer[kk], length - kk);
buffer[kk] = '.';
return &buffer[length + 1];
}
else if (-6 < kk && kk <= 0) {
// 1234e-6 -> 0.001234
const int offset = 2 - kk;
memmove(&buffer[offset], &buffer[0], length);
std::memmove(&buffer[offset], &buffer[0], length);
buffer[0] = '0';
buffer[1] = '.';
for (int i = 2; i < offset; i++)
......@@ -190,7 +190,7 @@ inline char* Prettify(char* buffer, int length, int k) {
}
else {
// 1234e30 -> 1.234e33
memmove(&buffer[2], &buffer[1], length - 1);
std::memmove(&buffer[2], &buffer[1], length - 1);
buffer[1] = '.';
buffer[length + 1] = 'e';
return WriteExponent(kk - 1, &buffer[0 + length + 2]);
......@@ -220,6 +220,6 @@ RAPIDJSON_DIAG_POP
#endif
} // namespace internal
} // namespace rapidjson
RAPIDJSON_NAMESPACE_END
#endif // RAPIDJSON_DTOA_
......@@ -21,7 +21,7 @@
#ifndef RAPIDJSON_ITOA_
#define RAPIDJSON_ITOA_
namespace rapidjson {
RAPIDJSON_NAMESPACE_BEGIN
namespace internal {
inline const char* GetDigitsLut() {
......@@ -91,7 +91,7 @@ inline char* u32toa(uint32_t value, char* buffer) {
*buffer++ = cDigitsLut[i + 1];
}
else
*buffer++ = '0' + static_cast<char>(a);
*buffer++ = static_cast<char>('0' + static_cast<char>(a));
const uint32_t b = value / 10000; // 0 to 9999
const uint32_t c = value % 10000; // 0 to 9999
......@@ -227,14 +227,14 @@ inline char* u64toa(uint64_t value, char* buffer) {
value %= kTen16;
if (a < 10)
*buffer++ = '0' + static_cast<char>(a);
*buffer++ = static_cast<char>('0' + static_cast<char>(a));
else if (a < 100) {
const uint32_t i = a << 1;
*buffer++ = cDigitsLut[i];
*buffer++ = cDigitsLut[i + 1];
}
else if (a < 1000) {
*buffer++ = '0' + static_cast<char>(a / 100);
*buffer++ = static_cast<char>('0' + static_cast<char>(a / 100));
const uint32_t i = (a % 100) << 1;
*buffer++ = cDigitsLut[i];
......@@ -301,6 +301,6 @@ inline char* i64toa(int64_t value, char* buffer) {
}
} // namespace internal
} // namespace rapidjson
RAPIDJSON_NAMESPACE_END
#endif // RAPIDJSON_ITOA_
......@@ -39,7 +39,7 @@ RAPIDJSON_DIAG_OFF(6334)
#endif
//@cond RAPIDJSON_INTERNAL
namespace rapidjson {
RAPIDJSON_NAMESPACE_BEGIN
namespace internal {
// Helper to wrap/convert arbitrary types to void, useful for arbitrary type matching
......@@ -157,29 +157,29 @@ template <typename T> struct RemoveSfinaeTag;
template <typename T> struct RemoveSfinaeTag<SfinaeTag&(*)(T)> { typedef T Type; };
#define RAPIDJSON_REMOVEFPTR_(type) \
typename ::rapidjson::internal::RemoveSfinaeTag \
< ::rapidjson::internal::SfinaeTag&(*) type>::Type
typename ::RAPIDJSON_NAMESPACE::internal::RemoveSfinaeTag \
< ::RAPIDJSON_NAMESPACE::internal::SfinaeTag&(*) type>::Type
#define RAPIDJSON_ENABLEIF(cond) \
typename ::rapidjson::internal::EnableIf \
typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \
<RAPIDJSON_REMOVEFPTR_(cond)>::Type * = NULL
#define RAPIDJSON_DISABLEIF(cond) \
typename ::rapidjson::internal::DisableIf \
typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \
<RAPIDJSON_REMOVEFPTR_(cond)>::Type * = NULL
#define RAPIDJSON_ENABLEIF_RETURN(cond,returntype) \
typename ::rapidjson::internal::EnableIf \
typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \
<RAPIDJSON_REMOVEFPTR_(cond), \
RAPIDJSON_REMOVEFPTR_(returntype)>::Type
#define RAPIDJSON_DISABLEIF_RETURN(cond,returntype) \
typename ::rapidjson::internal::DisableIf \
typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \
<RAPIDJSON_REMOVEFPTR_(cond), \
RAPIDJSON_REMOVEFPTR_(returntype)>::Type
} // namespace internal
} // namespace rapidjson
RAPIDJSON_NAMESPACE_END
//@endcond
#if defined(__GNUC__) || defined(_MSC_VER)
......
......@@ -21,7 +21,7 @@
#ifndef RAPIDJSON_POW10_
#define RAPIDJSON_POW10_
namespace rapidjson {
RAPIDJSON_NAMESPACE_BEGIN
namespace internal {
//! Computes integer powers of 10 in double (10.0^n).
......@@ -54,6 +54,6 @@ inline double Pow10(int n) {
}
} // namespace internal
} // namespace rapidjson
RAPIDJSON_NAMESPACE_END
#endif // RAPIDJSON_POW10_
......@@ -21,7 +21,7 @@
#ifndef RAPIDJSON_INTERNAL_STACK_H_
#define RAPIDJSON_INTERNAL_STACK_H_
namespace rapidjson {
RAPIDJSON_NAMESPACE_BEGIN
namespace internal {
///////////////////////////////////////////////////////////////////////////////
......@@ -35,16 +35,55 @@ class Stack {
public:
// Optimization note: Do not allocate memory for stack_ in constructor.
// Do it lazily when first Push() -> Expand() -> Resize().
Stack(Allocator* allocator, size_t stackCapacity) : allocator_(allocator), ownAllocator(0), stack_(0), stackTop_(0), stackEnd_(0), initialCapacity_(stackCapacity) {
Stack(Allocator* allocator, size_t stackCapacity) : allocator_(allocator), ownAllocator_(0), stack_(0), stackTop_(0), stackEnd_(0), initialCapacity_(stackCapacity) {
RAPIDJSON_ASSERT(stackCapacity > 0);
if (!allocator_)
ownAllocator = allocator_ = new Allocator();
}
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
Stack(Stack&& rhs)
: allocator_(rhs.allocator_),
ownAllocator_(rhs.ownAllocator_),
stack_(rhs.stack_),
stackTop_(rhs.stackTop_),
stackEnd_(rhs.stackEnd_),
initialCapacity_(rhs.initialCapacity_)
{
rhs.allocator_ = 0;
rhs.ownAllocator_ = 0;
rhs.stack_ = 0;
rhs.stackTop_ = 0;
rhs.stackEnd_ = 0;
rhs.initialCapacity_ = 0;
}
#endif
~Stack() {
Allocator::Free(stack_);
delete ownAllocator; // Only delete if it is owned by the stack
Destroy();
}
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
Stack& operator=(Stack&& rhs) {
if (&rhs != this)
{
Destroy();
allocator_ = rhs.allocator_;
ownAllocator_ = rhs.ownAllocator_;
stack_ = rhs.stack_;
stackTop_ = rhs.stackTop_;
stackEnd_ = rhs.stackEnd_;
initialCapacity_ = rhs.initialCapacity_;
rhs.allocator_ = 0;
rhs.ownAllocator_ = 0;
rhs.stack_ = 0;
rhs.stackTop_ = 0;
rhs.stackEnd_ = 0;
rhs.initialCapacity_ = 0;
}
return *this;
}
#endif
void Clear() { stackTop_ = stack_; }
......@@ -98,7 +137,15 @@ private:
template<typename T>
void Expand(size_t count) {
// Only expand the capacity if the current stack exists. Otherwise just create a stack with initial capacity.
size_t newCapacity = (stack_ == 0) ? initialCapacity_ : GetCapacity() * 2;
size_t newCapacity;
if (stack_ == 0) {
if (!allocator_)
ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator());
newCapacity = initialCapacity_;
} else {
newCapacity = GetCapacity();
newCapacity += (newCapacity + 1) / 2;
}
size_t newSize = GetSize() + sizeof(T) * count;
if (newCapacity < newSize)
newCapacity = newSize;
......@@ -113,12 +160,17 @@ private:
stackEnd_ = stack_ + newCapacity;
}
void Destroy() {
Allocator::Free(stack_);
RAPIDJSON_DELETE(ownAllocator_); // Only delete if it is owned by the stack
}
// Prohibit copy constructor & assignment operator.
Stack(const Stack&);
Stack& operator=(const Stack&);
Allocator* allocator_;
Allocator* ownAllocator;
Allocator* ownAllocator_;
char *stack_;
char *stackTop_;
char *stackEnd_;
......@@ -126,6 +178,6 @@ private:
};
} // namespace internal
} // namespace rapidjson
RAPIDJSON_NAMESPACE_END
#endif // RAPIDJSON_STACK_H_
......@@ -21,7 +21,7 @@
#ifndef RAPIDJSON_INTERNAL_STRFUNC_H_
#define RAPIDJSON_INTERNAL_STRFUNC_H_
namespace rapidjson {
RAPIDJSON_NAMESPACE_BEGIN
namespace internal {
//! Custom strlen() which works on different character types.
......@@ -38,6 +38,6 @@ inline SizeType StrLen(const Ch* s) {
}
} // namespace internal
} // namespace rapidjson
RAPIDJSON_NAMESPACE_END
#endif // RAPIDJSON_INTERNAL_STRFUNC_H_
......@@ -24,7 +24,7 @@
#include "rapidjson.h"
#include "internal/stack.h"
namespace rapidjson {
RAPIDJSON_NAMESPACE_BEGIN
//! Represents an in-memory output byte stream.
/*!
......@@ -68,9 +68,9 @@ typedef GenericMemoryBuffer<> MemoryBuffer;
//! Implement specialized version of PutN() with memset() for better performance.
template<>
inline void PutN(MemoryBuffer& memoryBuffer, char c, size_t n) {
memset(memoryBuffer.stack_.Push<char>(n), c, n * sizeof(c));
std::memset(memoryBuffer.stack_.Push<char>(n), c, n * sizeof(c));
}
} // namespace rapidjson
RAPIDJSON_NAMESPACE_END
#endif // RAPIDJSON_MEMORYBUFFER_H_
......@@ -23,7 +23,7 @@
#include "rapidjson.h"
namespace rapidjson {
RAPIDJSON_NAMESPACE_BEGIN
//! Represents an in-memory input byte stream.
/*!
......@@ -42,7 +42,7 @@ struct MemoryStream {
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_ == end_) ? '\0' : *src_; }
Ch Take() { return (src_ == end_) ? '\0' : *src_++; }
size_t Tell() const { return static_cast<size_t>(src_ - begin_); }
......@@ -62,6 +62,6 @@ struct MemoryStream {
size_t size_; //!< Size of the stream.
};
} // namespace rapidjson
RAPIDJSON_NAMESPACE_END
#endif // RAPIDJSON_MEMORYBUFFER_H_
......@@ -28,7 +28,7 @@ RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(effc++)
#endif
namespace rapidjson {
RAPIDJSON_NAMESPACE_BEGIN
//! Writer with indentation and spacing.
/*!
......@@ -196,7 +196,7 @@ private:
PrettyWriter& operator=(const PrettyWriter&);
};
} // namespace rapidjson
RAPIDJSON_NAMESPACE_END
#ifdef __GNUC__
RAPIDJSON_DIAG_POP
......
......@@ -45,8 +45,54 @@
different translation units of a single application.
*/
#include <cstdlib> // malloc(), realloc(), free()
#include <cstring> // memcpy()
#include <cstdlib> // malloc(), realloc(), free(), size_t
#include <cstring> // memset(), memcpy(), memmove(), memcmp()
///////////////////////////////////////////////////////////////////////////////
// RAPIDJSON_NAMESPACE_(BEGIN|END)
/*! \def RAPIDJSON_NAMESPACE
\ingroup RAPIDJSON_CONFIG
\brief provide custom rapidjson namespace
In order to avoid symbol clashes and/or "One Definition Rule" errors
between multiple inclusions of (different versions of) RapidJSON in
a single binary, users can customize the name of the main RapidJSON
namespace.
In case of a single nesting level, defining \c RAPIDJSON_NAMESPACE
to a custom name (e.g. \c MyRapidJSON) is sufficient. If multiple
levels are needed, both \ref RAPIDJSON_NAMESPACE_BEGIN and \ref
RAPIDJSON_NAMESPACE_END need to be defined as well:
\code
// in some .cpp file
#define RAPIDJSON_NAMESPACE my::rapidjson
#define RAPIDJSON_NAMESPACE_BEGIN namespace my { namespace rapidjson {
#define RAPIDJSON_NAMESPACE_END } }
#include "rapidjson/..."
\endcode
\see rapidjson
*/
/*! \def RAPIDJSON_NAMESPACE_BEGIN
\ingroup RAPIDJSON_CONFIG
\brief provide custom rapidjson namespace (opening expression)
\see RAPIDJSON_NAMESPACE
*/
/*! \def RAPIDJSON_NAMESPACE_END
\ingroup RAPIDJSON_CONFIG
\brief provide custom rapidjson namespace (closing expression)
\see RAPIDJSON_NAMESPACE
*/
#ifndef RAPIDJSON_NAMESPACE
#define RAPIDJSON_NAMESPACE rapidjson
#endif
#ifndef RAPIDJSON_NAMESPACE_BEGIN
#define RAPIDJSON_NAMESPACE_BEGIN namespace RAPIDJSON_NAMESPACE {
#endif
#ifndef RAPIDJSON_NAMESPACE_END
#define RAPIDJSON_NAMESPACE_END }
#endif
///////////////////////////////////////////////////////////////////////////////
// RAPIDJSON_NO_INT64DEFINE
......@@ -238,16 +284,21 @@
#ifdef RAPIDJSON_DOXYGEN_RUNNING
#define RAPIDJSON_NO_SIZETYPEDEFINE
#endif
namespace rapidjson {
RAPIDJSON_NAMESPACE_BEGIN
//! Size type (for string lengths, array sizes, etc.)
/*! RapidJSON uses 32-bit array/string indices even on 64-bit platforms,
instead of using \c size_t. Users may override the SizeType by defining
\ref RAPIDJSON_NO_SIZETYPEDEFINE.
*/
typedef unsigned SizeType;
} // namespace rapidjson
RAPIDJSON_NAMESPACE_END
#endif
// always import std::size_t to rapidjson namespace
RAPIDJSON_NAMESPACE_BEGIN
using std::size_t;
RAPIDJSON_NAMESPACE_END
///////////////////////////////////////////////////////////////////////////////
// RAPIDJSON_ASSERT
......@@ -270,12 +321,11 @@ typedef unsigned SizeType;
// Adopt from boost
#ifndef RAPIDJSON_STATIC_ASSERT
//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN
namespace rapidjson {
RAPIDJSON_NAMESPACE_BEGIN
template <bool x> struct STATIC_ASSERTION_FAILURE;
template <> struct STATIC_ASSERTION_FAILURE<true> { enum { value = 1 }; };
template<int x> struct StaticAssertTest {};
} // namespace rapidjson
RAPIDJSON_NAMESPACE_END
#define RAPIDJSON_JOIN(X, Y) RAPIDJSON_DO_JOIN(X, Y)
#define RAPIDJSON_DO_JOIN(X, Y) RAPIDJSON_DO_JOIN2(X, Y)
......@@ -293,8 +343,9 @@ template<int x> struct StaticAssertTest {};
\param x compile-time condition
\hideinitializer
*/
#define RAPIDJSON_STATIC_ASSERT(x) typedef ::rapidjson::StaticAssertTest<\
sizeof(::rapidjson::STATIC_ASSERTION_FAILURE<bool(x) >)>\
#define RAPIDJSON_STATIC_ASSERT(x) \
typedef ::RAPIDJSON_NAMESPACE::StaticAssertTest< \
sizeof(::RAPIDJSON_NAMESPACE::STATIC_ASSERTION_FAILURE<bool(x) >)> \
RAPIDJSON_JOIN(StaticAssertTypedef, __LINE__) RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE
#endif
......@@ -395,14 +446,29 @@ template<int x> struct StaticAssertTest {};
//!@endcond
///////////////////////////////////////////////////////////////////////////////
// new/delete
#ifndef RAPIDJSON_NEW
///! customization point for global \c new
#define RAPIDJSON_NEW(x) new x
#endif
#ifndef RAPIDJSON_DELETE
///! customization point for global \c delete
#define RAPIDJSON_DELETE(x) delete x
#endif
///////////////////////////////////////////////////////////////////////////////
// Allocators and Encodings
#include "allocators.h"
#include "encodings.h"
//! main RapidJSON namespace
namespace rapidjson {
/*! \namespace rapidjson
\brief main RapidJSON namespace
\see RAPIDJSON_NAMESPACE
*/
RAPIDJSON_NAMESPACE_BEGIN
///////////////////////////////////////////////////////////////////////////////
// Stream
......@@ -557,6 +623,6 @@ enum Type {
kNumberType = 6 //!< number
};
} // namespace rapidjson
RAPIDJSON_NAMESPACE_END
#endif // RAPIDJSON_RAPIDJSON_H_
......@@ -121,7 +121,7 @@ RAPIDJSON_DIAG_OFF(effc++)
#include "error/error.h" // ParseErrorCode, ParseResult
namespace rapidjson {
RAPIDJSON_NAMESPACE_BEGIN
///////////////////////////////////////////////////////////////////////////////
// ParseFlag
......@@ -1422,7 +1422,7 @@ private:
//! Reader with UTF8 encoding and default allocator.
typedef GenericReader<UTF8<>, UTF8<> > Reader;
} // namespace rapidjson
RAPIDJSON_NAMESPACE_END
#ifdef __GNUC__
RAPIDJSON_DIAG_POP
......
......@@ -24,7 +24,7 @@
#include "rapidjson.h"
#include "internal/stack.h"
namespace rapidjson {
RAPIDJSON_NAMESPACE_BEGIN
//! Represents an in-memory output stream.
/*!
......@@ -71,9 +71,9 @@ typedef GenericStringBuffer<UTF8<> > StringBuffer;
//! Implement specialized version of PutN() with memset() for better performance.
template<>
inline void PutN(GenericStringBuffer<UTF8<> >& stream, char c, size_t n) {
memset(stream.stack_.Push<char>(n), c, n * sizeof(c));
std::memset(stream.stack_.Push<char>(n), c, n * sizeof(c));
}
} // namespace rapidjson
RAPIDJSON_NAMESPACE_END
#endif // RAPIDJSON_STRINGBUFFER_H_
......@@ -34,7 +34,7 @@ RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant
#endif
namespace rapidjson {
RAPIDJSON_NAMESPACE_BEGIN
//! JSON writer
/*! Writer implements the concept Handler.
......@@ -380,7 +380,7 @@ inline bool Writer<StringBuffer>::WriteDouble(double d) {
return true;
}
} // namespace rapidjson
RAPIDJSON_NAMESPACE_END
#ifdef _MSC_VER
RAPIDJSON_DIAG_POP
......
......@@ -43,12 +43,13 @@ RapidJSON is a header-only C++ library. Just copy the `include/rapidjson` folder
To build the tests and examples:
1. Obtain [premake4](http://industriousone.com/premake/download).
2. Copy premake4 executable to `rapidjson/build` (or system path).
3. Change directory to `rapidjson/build/`, run `premake.bat` on Windows, `premake.sh` on Linux or other platforms.
4. On Windows, build the solution at `rapidjson/build/vs2008/` or `/vs2010/`.
5. On other platforms, run GNU `make` at `rapidjson/build/gmake/` (e.g., `make -f test.make config=release32`; `make -f example.make config=debug32`).
6. On success, the executables are generated at `rapidjson/bin`.
1. Execute `git submodule update --init` to get the files of thirdparty submodules (google test).
2. Obtain [premake4](http://industriousone.com/premake/download).
3. Copy premake4 executable to `rapidjson/build` (or system path).
4. Change directory to `rapidjson/build/`, run `premake.bat` on Windows, `premake.sh` on Linux or other platforms.
5. On Windows, build the solution at `rapidjson/build/vs2008/` or `/vs2010/`.
6. On other platforms, run GNU `make` at `rapidjson/build/gmake/` (e.g., `make -f test.make config=release32`; `make -f example.make config=debug32`).
7. On success, the executables are generated at `rapidjson/bin`.
To build the [Doxygen](http://doxygen.org) documentation:
......
......@@ -22,25 +22,18 @@
#define PERFTEST_H_
#define TEST_RAPIDJSON 1
#define TEST_JSONCPP 0
#define TEST_YAJL 0
#define TEST_ULTRAJSON 0
#define TEST_PLATFORM 0
#define TEST_MISC 0
#define TEST_VERSION_CODE(x,y,z) \
(((x)*100000) + ((y)*100) + (z))
// Only gcc >4.3 supports SSE4.2
#if TEST_RAPIDJSON && !(defined(__GNUC__) && TEST_VERSION_CODE(__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__) < TEST_VERSION_CODE(4,3,0))
//#define RAPIDJSON_SSE2
#define RAPIDJSON_SSE42
#endif
#if TEST_YAJL
#include "yajl/yajl_common.h"
#undef YAJL_MAX_DEPTH
#define YAJL_MAX_DEPTH 1024
// __SSE2__ and __SSE4_2__ are recognized by gcc, clang, and the Intel compiler.
// We use -march=native with gmake to enable -msse2 and -msse4.2, if supported.
#if defined(__SSE4_2__)
# define RAPIDJSON_SSE42
#elif defined(__SSE2__)
# define RAPIDJSON_SSE2
#endif
////////////////////////////////////////////////////////////////////////////////
......
......@@ -28,6 +28,8 @@
#include "rapidjson/stringbuffer.h"
#include "rapidjson/filestream.h"
#include "rapidjson/filereadstream.h"
#include "rapidjson/encodedstream.h"
#include "rapidjson/memorystream.h"
#ifdef RAPIDJSON_SSE2
#define SIMD_SUFFIX(name) name##_SSE2
......@@ -176,6 +178,26 @@ TEST_F(RapidJson, SIMD_SUFFIX(DocumentParse_CrtAllocator)) {
}
}
TEST_F(RapidJson, SIMD_SUFFIX(DocumentParseEncodedInputStream_MemoryStream)) {
for (size_t i = 0; i < kTrialCount; i++) {
MemoryStream ms(json_, length_);
EncodedInputStream<UTF8<>, MemoryStream> is(ms);
Document doc;
doc.ParseStream<0, UTF8<> >(is);
ASSERT_TRUE(doc.IsObject());
}
}
TEST_F(RapidJson, SIMD_SUFFIX(DocumentParseAutoUTFInputStream_MemoryStream)) {
for (size_t i = 0; i < kTrialCount; i++) {
MemoryStream ms(json_, length_);
AutoUTFInputStream<unsigned, MemoryStream> is(ms);
Document doc;
doc.ParseStream<0, AutoUTF<unsigned> >(is);
ASSERT_TRUE(doc.IsObject());
}
}
template<typename T>
size_t Traverse(const T& value) {
size_t count = 1;
......@@ -302,7 +324,7 @@ TEST_F(RapidJson, UTF8_Validate) {
}
}
// Depreciated.
// Deprecated.
//TEST_F(RapidJson, FileStream_Read) {
// for (size_t i = 0; i < kTrialCount; i++) {
// FILE *fp = fopen(filename_, "rb");
......
// 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"
#if TEST_ULTRAJSON
#include "ultrajson/ultrajsondec.c"
#include "ultrajson/ultrajsonenc.c"
class UltraJson : public PerfTest {
};
static char dummy = 0;
static void Object_objectAddKey(JSOBJ obj, JSOBJ name, 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_newTrue(void) { return &dummy; }
static JSOBJ Object_newFalse(void) { return &dummy; }
static JSOBJ Object_newNull(void) { return &dummy; }
static JSOBJ Object_newObject(void) { return &dummy; }
static JSOBJ Object_newArray(void) { return &dummy; }
static JSOBJ Object_newInteger(JSINT32 value) { return &dummy; }
static JSOBJ Object_newLong(JSINT64 value) { return &dummy; }
static JSOBJ Object_newDouble(double value) { return &dummy; }
static void Object_releaseObject(JSOBJ obj) {}
static JSONObjectDecoder decoder = {
Object_newString,
Object_objectAddKey,
Object_arrayAddItem,
Object_newTrue,
Object_newFalse,
Object_newNull,
Object_newObject,
Object_newArray,
Object_newInteger,
Object_newLong,
Object_newDouble,
Object_releaseObject,
malloc,
free,
realloc
};
TEST_F(UltraJson, Decode) {
for (int i = 0; i < kTrialCount; i++) {
decoder.errorStr = NULL;
decoder.errorOffset = NULL;
void *ret = JSON_DecodeObject(&decoder, json_, length_);
ASSERT_TRUE(ret != 0);
}
}
TEST_F(UltraJson, Whitespace) {
for (int i = 0; i < kTrialCount; i++) {
decoder.errorStr = NULL;
decoder.errorOffset = NULL;
void *ret = JSON_DecodeObject(&decoder, whitespace_, whitespace_length_);
ASSERT_TRUE(ret != 0);
}
}
#endif // TEST_ULTRAJSON
#include "perftest.h"
#if TEST_YAJL
#ifdef _MSC_VER
#include <float.h>
#define isinf !_finite
#define isnan _isnan
#define snprintf _snprintf
#endif
#include "yajl/src/yajl.c"
#include "yajl/src/yajl_alloc.c"
#include "yajl/src/yajl_buf.c"
#include "yajl/src/yajl_encode.c"
#include "yajl/src/yajl_gen.c"
#include "yajl/src/yajl_lex.c"
#include "yajl/src/yajl_parser.c"
#include "yajl/src/yajl_tree.c"
#include "yajl/src/yajl_version.c"
#endif // TEST_YAJL
// 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"
#if TEST_YAJL
extern "C" {
#include "yajl/yajl_gen.h"
#include "yajl/yajl_parse.h"
#include "yajl/yajl_tree.h"
};
class Yajl : public PerfTest {
public:
virtual void SetUp() {
PerfTest::SetUp();
root_ = yajl_tree_parse(json_, NULL, 0);
ASSERT_TRUE(root_ != NULL);
}
virtual void TearDown() {
PerfTest::TearDown();
yajl_tree_free(root_);
}
protected:
yajl_val root_;
};
static int null_null(void *) { return 1; }
static int null_boolean(void *, int) { return 1; }
static int null_integer(void *, long long) { return 1; }
static int null_double(void *, double) { return 1; }
static int null_string(void *, const unsigned char*, size_t) { return 1; }
static int null_start_map(void *) { return 1; }
static int null_map_key(void *, const unsigned char*, size_t) { return 1; }
static int null_end_map(void *) { return 1; }
static int null_start_array(void*) { return 1; }
static int null_end_array(void *) { return 1; }
static yajl_callbacks nullcallbacks = {
null_null,
null_boolean,
null_integer,
null_double,
NULL, // yajl_number(). Here we want to test full-parsing performance.
null_string,
null_start_map,
null_map_key,
null_end_map,
null_start_array,
null_end_array
};
TEST_F(Yajl, yajl_parse_nullcallbacks) {
for (int i = 0; i < kTrialCount; i++) {
yajl_handle hand = yajl_alloc(&nullcallbacks, NULL, NULL);
yajl_status stat = yajl_parse(hand, (unsigned char*)json_, length_);
//ASSERT_EQ(yajl_status_ok, stat);
if (stat != yajl_status_ok) {
unsigned char * str = yajl_get_error(hand, 1, (unsigned char*)json_, length_);
fprintf(stderr, "%s", (const char *) str);
}
stat = yajl_complete_parse(hand);
ASSERT_EQ(yajl_status_ok, stat);
yajl_free(hand);
}
}
TEST_F(Yajl, yajl_tree_parse) {
for (int i = 0; i < kTrialCount; i++) {
yajl_val root = yajl_tree_parse(json_, NULL, 0);
ASSERT_TRUE(root != NULL);
yajl_tree_free(root);
}
}
yajl_gen_status GenVal(yajl_gen g, yajl_val v) {
yajl_gen_status status;
switch (v->type) {
case yajl_t_string: return yajl_gen_string(g, (unsigned char*)v->u.string, strlen(v->u.string));
case yajl_t_number:
{
char buffer[100];
char *num = buffer;
size_t len;
//if (YAJL_IS_INTEGER(v)) // buggy
if (v->u.number.flags & YAJL_NUMBER_INT_VALID)
#if _MSC_VER
len = sprintf(num, "%I64d", YAJL_GET_INTEGER(v));
#else
len = sprintf(num, "%lld", YAJL_GET_INTEGER(v));
#endif
//else if (YAJL_IS_DOUBLE(v)) // buggy
else if (v->u.number.flags & YAJL_NUMBER_DOUBLE_VALID)
len = sprintf(num, "%g", YAJL_GET_DOUBLE(v));
else {
num = YAJL_GET_NUMBER(v);
len = strlen(buffer);
}
return yajl_gen_number(g, num, len);
}
case yajl_t_object:
status = yajl_gen_map_open(g);
if (status != yajl_gen_status_ok)
return status;
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]));
if (status != yajl_gen_status_ok)
return status;
status = GenVal(g, v->u.object.values[i]);
if (status != yajl_gen_status_ok)
return status;
}
return yajl_gen_map_close(g);
case yajl_t_array:
status = yajl_gen_array_open(g);
if (status != yajl_gen_status_ok)
return status;
for (size_t i = 0; i < v->u.array.len; i++) {
status = GenVal(g, v->u.array.values[i]);
if (status != yajl_gen_status_ok)
return status;
}
return yajl_gen_array_close(g);
case yajl_t_true: return yajl_gen_bool(g, 1);
case yajl_t_false: return yajl_gen_bool(g, 0);
case yajl_t_null: return yajl_gen_null(g);
}
return yajl_gen_in_error_state;
}
TEST_F(Yajl, yajl_gen) {
for (int i = 0; i < kTrialCount; i++) {
yajl_gen g = yajl_gen_alloc(NULL);
yajl_gen_status status = GenVal(g, root_);
if (status != yajl_gen_status_ok) {
std::cout << "gen error: " << status << std::endl;
FAIL();
}
const unsigned char * buf;
size_t len;
status = yajl_gen_get_buf(g, &buf, &len);
ASSERT_EQ(yajl_gen_status_ok, status);
//if (i == 0)
// std::cout << len << std::endl;
yajl_gen_free(g);
}
}
TEST_F(Yajl, yajl_gen_beautify) {
for (int i = 0; i < kTrialCount; i++) {
yajl_gen g = yajl_gen_alloc(NULL);
yajl_gen_config(g, yajl_gen_beautify, 1);
yajl_gen_config(g, yajl_gen_indent_string, " ");
yajl_gen_status status = GenVal(g, root_);
if (status != yajl_gen_status_ok) {
std::cout << "gen error: " << status << std::endl;
FAIL();
}
const unsigned char * buf;
size_t len;
status = yajl_gen_get_buf(g, &buf, &len);
ASSERT_EQ(yajl_gen_status_ok, status);
//if (i == 0)
// std::cout << len << std::endl;
yajl_gen_free(g);
}
}
TEST_F(Yajl, Whitespace) {
for (int i = 0; i < kTrialCount; i++) {
yajl_val root = yajl_tree_parse(whitespace_, NULL, 0);
ASSERT_TRUE(root != NULL);
yajl_tree_free(root);
}
}
#endif // TEST_YAJL
......@@ -218,7 +218,7 @@ TEST(Document, UTF16_Document) {
json.Parse<kParseValidateEncodingFlag>(L"[{\"created_at\":\"Wed Oct 30 17:13:20 +0000 2012\"}]");
ASSERT_TRUE(json.IsArray());
GenericValue< UTF16<> >& v = json[0u];
GenericValue< UTF16<> >& v = json[0];
ASSERT_TRUE(v.IsObject());
GenericValue< UTF16<> >& s = v[L"created_at"];
......@@ -227,6 +227,224 @@ TEST(Document, UTF16_Document) {
EXPECT_EQ(0, wcscmp(L"Wed Oct 30 17:13:20 +0000 2012", s.GetString()));
}
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
#include <type_traits>
TEST(Document, Traits) {
static_assert( std::is_constructible<Document>::value, "");
static_assert( std::is_default_constructible<Document>::value, "");
static_assert(!std::is_copy_constructible<Document>::value, "");
static_assert( std::is_move_constructible<Document>::value, "");
static_assert(!std::is_nothrow_constructible<Document>::value, "");
static_assert(!std::is_nothrow_default_constructible<Document>::value, "");
static_assert(!std::is_nothrow_copy_constructible<Document>::value, "");
static_assert( std::is_nothrow_move_constructible<Document>::value, "");
static_assert( std::is_assignable<Document,Document>::value, "");
static_assert(!std::is_copy_assignable<Document>::value, "");
static_assert( std::is_move_assignable<Document>::value, "");
static_assert( std::is_nothrow_assignable<Document,Document>::value, "");
static_assert(!std::is_nothrow_copy_assignable<Document>::value, "");
static_assert( std::is_nothrow_move_assignable<Document>::value, "");
static_assert( std::is_destructible<Document>::value, "");
static_assert( std::is_nothrow_destructible<Document>::value, "");
}
template <typename Allocator>
struct DocumentMove: public ::testing::Test {
};
typedef ::testing::Types< CrtAllocator, MemoryPoolAllocator<> > MoveAllocatorTypes;
TYPED_TEST_CASE(DocumentMove, MoveAllocatorTypes);
TYPED_TEST(DocumentMove, MoveConstructor) {
typedef TypeParam Allocator;
typedef GenericDocument<UTF8<>, Allocator> Document;
Allocator allocator;
Document a(&allocator);
a.Parse("[\"one\", \"two\", \"three\"]");
EXPECT_FALSE(a.HasParseError());
EXPECT_TRUE(a.IsArray());
EXPECT_EQ(3u, a.Size());
EXPECT_EQ(&a.GetAllocator(), &allocator);
// Document b(a); // does not compile (!is_copy_constructible)
Document b(std::move(a));
EXPECT_TRUE(a.IsNull());
EXPECT_TRUE(b.IsArray());
EXPECT_EQ(3u, b.Size());
EXPECT_EQ(&a.GetAllocator(), (void*)0);
EXPECT_EQ(&b.GetAllocator(), &allocator);
b.Parse("{\"Foo\": \"Bar\", \"Baz\": 42}");
EXPECT_FALSE(b.HasParseError());
EXPECT_TRUE(b.IsObject());
EXPECT_EQ(2u, b.MemberCount());
// Document c = a; // does not compile (!is_copy_constructible)
Document c = std::move(b);
EXPECT_TRUE(b.IsNull());
EXPECT_TRUE(c.IsObject());
EXPECT_EQ(2u, c.MemberCount());
EXPECT_EQ(&b.GetAllocator(), (void*)0);
EXPECT_EQ(&c.GetAllocator(), &allocator);
}
TYPED_TEST(DocumentMove, MoveConstructorParseError) {
typedef TypeParam Allocator;
typedef GenericDocument<UTF8<>, Allocator> Document;
ParseResult noError;
Document a;
a.Parse("{ 4 = 4]");
ParseResult error(a.GetParseError(), a.GetErrorOffset());
EXPECT_TRUE(a.HasParseError());
EXPECT_NE(error.Code(), noError.Code());
EXPECT_NE(error.Offset(), noError.Offset());
Document b(std::move(a));
EXPECT_FALSE(a.HasParseError());
EXPECT_TRUE(b.HasParseError());
EXPECT_EQ(a.GetParseError(), noError.Code());
EXPECT_EQ(b.GetParseError(), error.Code());
EXPECT_EQ(a.GetErrorOffset(), noError.Offset());
EXPECT_EQ(b.GetErrorOffset(), error.Offset());
Document c(std::move(b));
EXPECT_FALSE(b.HasParseError());
EXPECT_TRUE(c.HasParseError());
EXPECT_EQ(b.GetParseError(), noError.Code());
EXPECT_EQ(c.GetParseError(), error.Code());
EXPECT_EQ(b.GetErrorOffset(), noError.Offset());
EXPECT_EQ(c.GetErrorOffset(), error.Offset());
}
TYPED_TEST(DocumentMove, MoveConstructorStack) {
typedef TypeParam Allocator;
typedef UTF8<> Encoding;
typedef GenericDocument<Encoding, Allocator> Document;
Document a;
size_t defaultCapacity = a.GetStackCapacity();
// Trick Document into getting GetStackCapacity() to return non-zero
typedef GenericReader<Encoding, Encoding, Allocator> Reader;
Reader reader(&a.GetAllocator());
GenericStringStream<Encoding> is("[\"one\", \"two\", \"three\"]");
reader.template Parse<kParseDefaultFlags>(is, a);
size_t capacity = a.GetStackCapacity();
EXPECT_GT(capacity, 0u);
Document b(std::move(a));
EXPECT_EQ(a.GetStackCapacity(), defaultCapacity);
EXPECT_EQ(b.GetStackCapacity(), capacity);
Document c = std::move(b);
EXPECT_EQ(b.GetStackCapacity(), defaultCapacity);
EXPECT_EQ(c.GetStackCapacity(), capacity);
}
TYPED_TEST(DocumentMove, MoveAssignment) {
typedef TypeParam Allocator;
typedef GenericDocument<UTF8<>, Allocator> Document;
Allocator allocator;
Document a(&allocator);
a.Parse("[\"one\", \"two\", \"three\"]");
EXPECT_FALSE(a.HasParseError());
EXPECT_TRUE(a.IsArray());
EXPECT_EQ(3u, a.Size());
EXPECT_EQ(&a.GetAllocator(), &allocator);
// Document b; b = a; // does not compile (!is_copy_assignable)
Document b;
b = std::move(a);
EXPECT_TRUE(a.IsNull());
EXPECT_TRUE(b.IsArray());
EXPECT_EQ(3u, b.Size());
EXPECT_EQ(&a.GetAllocator(), (void*)0);
EXPECT_EQ(&b.GetAllocator(), &allocator);
b.Parse("{\"Foo\": \"Bar\", \"Baz\": 42}");
EXPECT_FALSE(b.HasParseError());
EXPECT_TRUE(b.IsObject());
EXPECT_EQ(2u, b.MemberCount());
// Document c; c = a; // does not compile (see static_assert)
Document c;
c = std::move(b);
EXPECT_TRUE(b.IsNull());
EXPECT_TRUE(c.IsObject());
EXPECT_EQ(2u, c.MemberCount());
EXPECT_EQ(&b.GetAllocator(), (void*)0);
EXPECT_EQ(&c.GetAllocator(), &allocator);
}
TYPED_TEST(DocumentMove, MoveAssignmentParseError) {
typedef TypeParam Allocator;
typedef GenericDocument<UTF8<>, Allocator> Document;
ParseResult noError;
Document a;
a.Parse("{ 4 = 4]");
ParseResult error(a.GetParseError(), a.GetErrorOffset());
EXPECT_TRUE(a.HasParseError());
EXPECT_NE(error.Code(), noError.Code());
EXPECT_NE(error.Offset(), noError.Offset());
Document b;
b = std::move(a);
EXPECT_FALSE(a.HasParseError());
EXPECT_TRUE(b.HasParseError());
EXPECT_EQ(a.GetParseError(), noError.Code());
EXPECT_EQ(b.GetParseError(), error.Code());
EXPECT_EQ(a.GetErrorOffset(), noError.Offset());
EXPECT_EQ(b.GetErrorOffset(), error.Offset());
Document c;
c = std::move(b);
EXPECT_FALSE(b.HasParseError());
EXPECT_TRUE(c.HasParseError());
EXPECT_EQ(b.GetParseError(), noError.Code());
EXPECT_EQ(c.GetParseError(), error.Code());
EXPECT_EQ(b.GetErrorOffset(), noError.Offset());
EXPECT_EQ(c.GetErrorOffset(), error.Offset());
}
TYPED_TEST(DocumentMove, MoveAssignmentStack) {
typedef TypeParam Allocator;
typedef UTF8<> Encoding;
typedef GenericDocument<Encoding, Allocator> Document;
Document a;
size_t defaultCapacity = a.GetStackCapacity();
// Trick Document into getting GetStackCapacity() to return non-zero
typedef GenericReader<Encoding, Encoding, Allocator> Reader;
Reader reader(&a.GetAllocator());
GenericStringStream<Encoding> is("[\"one\", \"two\", \"three\"]");
reader.template Parse<kParseDefaultFlags>(is, a);
size_t capacity = a.GetStackCapacity();
EXPECT_GT(capacity, 0u);
Document b;
b = std::move(a);
EXPECT_EQ(a.GetStackCapacity(), defaultCapacity);
EXPECT_EQ(b.GetStackCapacity(), capacity);
Document c;
c = std::move(b);
EXPECT_EQ(b.GetStackCapacity(), defaultCapacity);
EXPECT_EQ(c.GetStackCapacity(), capacity);
}
#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS
// Issue 22: Memory corruption via operator=
// Fixed by making unimplemented assignment operator private.
//TEST(Document, Assignment) {
......
......@@ -60,7 +60,7 @@ protected:
size_t length_;
};
// Depreciated
// Deprecated
//TEST_F(FileStreamTest, FileStream_Read) {
// FILE *fp = fopen(filename_, "rb");
// ASSERT_TRUE(fp != 0);
......
......@@ -18,60 +18,59 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include "perftest.h"
#include "unittest.h"
#if TEST_JSONCPP
// test another instantiation of RapidJSON in a different namespace
#include "jsoncpp/src/lib_json/json_reader.cpp"
#include "jsoncpp/src/lib_json/json_value.cpp"
#include "jsoncpp/src/lib_json/json_writer.cpp"
#define RAPIDJSON_NAMESPACE my::rapid::json
#define RAPIDJSON_NAMESPACE_BEGIN namespace my { namespace rapid { namespace json {
#define RAPIDJSON_NAMESPACE_END } } }
using namespace Json;
// include lots of RapidJSON files
class JsonCpp : public PerfTest {
public:
virtual void SetUp() {
PerfTest::SetUp();
Reader reader;
ASSERT_TRUE(reader.parse(json_, root_));
}
#include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include "rapidjson/filereadstream.h"
#include "rapidjson/filewritestream.h"
#include "rapidjson/encodedstream.h"
#include "rapidjson/stringbuffer.h"
protected:
Value root_;
};
static const char json[] = "{\"hello\":\"world\",\"t\":true,\"f\":false,\"n\":null,\"i\":123,\"pi\":3.1416,\"a\":[1,2,3,4]}";
TEST_F(JsonCpp, ReaderParse) {
for (int i = 0; i < kTrialCount; i++) {
Value root;
Reader reader;
ASSERT_TRUE(reader.parse(json_, root));
}
}
TEST(NamespaceTest,Using) {
using namespace RAPIDJSON_NAMESPACE;
typedef GenericDocument<UTF8<>, CrtAllocator> DocumentType;
DocumentType doc;
TEST_F(JsonCpp, FastWriter) {
for (int i = 0; i < kTrialCount; i++) {
FastWriter writer;
std::string str = writer.write(root_);
//if (i == 0)
// std::cout << str.length() << std::endl;
}
doc.Parse(json);
EXPECT_TRUE(!doc.HasParseError());
}
TEST_F(JsonCpp, StyledWriter) {
for (int i = 0; i < kTrialCount; i++) {
StyledWriter writer;
std::string str = writer.write(root_);
//if (i == 0)
// std::cout << str.length() << std::endl;
}
}
TEST(NamespaceTest,Direct) {
typedef RAPIDJSON_NAMESPACE::Document Document;
typedef RAPIDJSON_NAMESPACE::Reader Reader;
typedef RAPIDJSON_NAMESPACE::StringStream StringStream;
typedef RAPIDJSON_NAMESPACE::StringBuffer StringBuffer;
typedef RAPIDJSON_NAMESPACE::Writer<StringBuffer> WriterType;
TEST_F(JsonCpp, Whitespace) {
for (int i = 0; i < kTrialCount; i++) {
Value root;
Reader reader;
ASSERT_TRUE(reader.parse(whitespace_, root));
}
}
StringStream s(json);
StringBuffer buffer;
WriterType writer(buffer);
buffer.ShrinkToFit();
Reader reader;
reader.Parse(s, writer);
#endif // TEST_JSONCPP
EXPECT_STREQ(json, buffer.GetString());
EXPECT_EQ(sizeof(json)-1, buffer.GetSize());
EXPECT_TRUE(writer.IsComplete());
Document doc;
doc.Parse(buffer.GetString());
EXPECT_TRUE(!doc.HasParseError());
buffer.Clear();
writer.Reset(buffer);
doc.Accept(writer);
EXPECT_STREQ(json, buffer.GetString());
EXPECT_TRUE(writer.IsComplete());
}
......@@ -23,6 +23,7 @@
#include "rapidjson/reader.h"
#include "rapidjson/internal/dtoa.h"
#include "rapidjson/internal/itoa.h"
#include "rapidjson/memorystream.h"
using namespace rapidjson;
......@@ -825,6 +826,15 @@ TEST(Reader, ParseObject_Error) {
// Must be a comma or '}' after an object member
TEST_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, "{\"a\":1]");
// This tests that MemoryStream is checking the length in Peek().
{
MemoryStream ms("{\"a\"", 1);
BaseReaderHandler<> h;
Reader reader;
EXPECT_FALSE(reader.Parse<kParseStopWhenDoneFlag>(ms, h));
EXPECT_EQ(kParseErrorObjectMissName, reader.GetParseErrorCode());
}
}
#undef TEST_ERROR
......
// 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 "rapidjson/stringbuffer.h"
#include "rapidjson/writer.h"
using namespace rapidjson;
TEST(StringBuffer, InitialSize) {
StringBuffer buffer;
EXPECT_EQ(0u, buffer.GetSize());
EXPECT_STREQ("", buffer.GetString());
}
TEST(StringBuffer, Put) {
StringBuffer buffer;
buffer.Put('A');
EXPECT_EQ(1u, buffer.GetSize());
EXPECT_STREQ("A", buffer.GetString());
}
TEST(StringBuffer, Clear) {
StringBuffer buffer;
buffer.Put('A');
buffer.Put('B');
buffer.Put('C');
buffer.Clear();
EXPECT_EQ(0u, buffer.GetSize());
EXPECT_STREQ("", buffer.GetString());
}
TEST(StringBuffer, Push) {
StringBuffer buffer;
buffer.Push(5);
EXPECT_EQ(5u, buffer.GetSize());
}
TEST(StringBuffer, Pop) {
StringBuffer buffer;
buffer.Put('A');
buffer.Put('B');
buffer.Put('C');
buffer.Put('D');
buffer.Put('E');
buffer.Pop(3);
EXPECT_EQ(2u, buffer.GetSize());
EXPECT_STREQ("AB", buffer.GetString());
}
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
#include <type_traits>
TEST(StringBuffer, Traits) {
static_assert( std::is_constructible<StringBuffer>::value, "");
static_assert( std::is_default_constructible<StringBuffer>::value, "");
static_assert(!std::is_copy_constructible<StringBuffer>::value, "");
static_assert( std::is_move_constructible<StringBuffer>::value, "");
static_assert(!std::is_nothrow_constructible<StringBuffer>::value, "");
static_assert(!std::is_nothrow_default_constructible<StringBuffer>::value, "");
static_assert(!std::is_nothrow_copy_constructible<StringBuffer>::value, "");
static_assert(!std::is_nothrow_move_constructible<StringBuffer>::value, "");
static_assert( std::is_assignable<StringBuffer,StringBuffer>::value, "");
static_assert(!std::is_copy_assignable<StringBuffer>::value, "");
static_assert( std::is_move_assignable<StringBuffer>::value, "");
static_assert(!std::is_nothrow_assignable<StringBuffer,StringBuffer>::value, "");
static_assert(!std::is_nothrow_copy_assignable<StringBuffer>::value, "");
static_assert(!std::is_nothrow_move_assignable<StringBuffer>::value, "");
static_assert( std::is_destructible<StringBuffer>::value, "");
static_assert( std::is_nothrow_destructible<StringBuffer>::value, "");
}
TEST(StringBuffer, MoveConstructor) {
StringBuffer x;
x.Put('A');
x.Put('B');
x.Put('C');
x.Put('D');
EXPECT_EQ(4u, x.GetSize());
EXPECT_STREQ("ABCD", x.GetString());
// StringBuffer y(x); // does not compile (!is_copy_constructible)
StringBuffer y(std::move(x));
EXPECT_EQ(0u, x.GetSize());
EXPECT_EQ(4u, y.GetSize());
EXPECT_STREQ("ABCD", y.GetString());
// StringBuffer z = y; // does not compile (!is_copy_assignable)
StringBuffer z = std::move(y);
EXPECT_EQ(0u, y.GetSize());
EXPECT_EQ(4u, z.GetSize());
EXPECT_STREQ("ABCD", z.GetString());
}
TEST(StringBuffer, MoveAssignment) {
StringBuffer x;
x.Put('A');
x.Put('B');
x.Put('C');
x.Put('D');
EXPECT_EQ(4u, x.GetSize());
EXPECT_STREQ("ABCD", x.GetString());
StringBuffer y;
// y = x; // does not compile (!is_copy_assignable)
y = std::move(x);
EXPECT_EQ(0u, x.GetSize());
EXPECT_EQ(4u, y.GetSize());
EXPECT_STREQ("ABCD", y.GetString());
}
#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS
......@@ -39,6 +39,33 @@ TEST(Value, DefaultConstructor) {
//}
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
#include <type_traits>
TEST(Value, Traits) {
typedef GenericValue<UTF8<>, CrtAllocator> Value;
static_assert( std::is_constructible<Value>::value, "");
static_assert( std::is_default_constructible<Value>::value, "");
static_assert(!std::is_copy_constructible<Value>::value, "");
static_assert( std::is_move_constructible<Value>::value, "");
static_assert( std::is_nothrow_constructible<Value>::value, "");
static_assert( std::is_nothrow_default_constructible<Value>::value, "");
static_assert(!std::is_nothrow_copy_constructible<Value>::value, "");
static_assert( std::is_nothrow_move_constructible<Value>::value, "");
static_assert( std::is_assignable<Value,Value>::value, "");
static_assert(!std::is_copy_assignable<Value>::value, "");
static_assert( std::is_move_assignable<Value>::value, "");
static_assert( std::is_nothrow_assignable<Value,Value>::value, "");
static_assert(!std::is_nothrow_copy_assignable<Value>::value, "");
static_assert( std::is_nothrow_move_assignable<Value>::value, "");
static_assert( std::is_destructible<Value>::value, "");
static_assert( std::is_nothrow_destructible<Value>::value, "");
}
TEST(Value, MoveConstructor) {
typedef GenericValue<UTF8<>, CrtAllocator> Value;
Value::AllocatorType allocator;
......@@ -49,18 +76,19 @@ TEST(Value, MoveConstructor) {
EXPECT_TRUE(x.IsArray());
EXPECT_EQ(4u, x.Size());
// Value y(x); // should not compile
// Value y(x); // does not compile (!is_copy_constructible)
Value y(std::move(x));
EXPECT_TRUE(x.IsNull());
EXPECT_TRUE(y.IsArray());
EXPECT_EQ(4u, y.Size());
// Value z = y; // should not compile
// Value z = y; // does not compile (!is_copy_assignable)
Value z = std::move(y);
EXPECT_TRUE(y.IsNull());
EXPECT_TRUE(z.IsArray());
EXPECT_EQ(4u, z.Size());
}
#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS
TEST(Value, AssignmentOperator) {
......@@ -672,17 +700,17 @@ TEST(Value, Array) {
EXPECT_FALSE(y.Empty());
EXPECT_EQ(5u, y.Size());
EXPECT_TRUE(x[SizeType(0)].IsNull());
EXPECT_TRUE(x[1u].IsTrue());
EXPECT_TRUE(x[2u].IsFalse());
EXPECT_TRUE(x[3u].IsInt());
EXPECT_EQ(123, x[3u].GetInt());
EXPECT_TRUE(x[1].IsTrue());
EXPECT_TRUE(x[2].IsFalse());
EXPECT_TRUE(x[3].IsInt());
EXPECT_EQ(123, x[3].GetInt());
EXPECT_TRUE(y[SizeType(0)].IsNull());
EXPECT_TRUE(y[1u].IsTrue());
EXPECT_TRUE(y[2u].IsFalse());
EXPECT_TRUE(y[3u].IsInt());
EXPECT_EQ(123, y[3u].GetInt());
EXPECT_TRUE(y[4u].IsString());
EXPECT_STREQ("foo", y[4u].GetString());
EXPECT_TRUE(y[1].IsTrue());
EXPECT_TRUE(y[2].IsFalse());
EXPECT_TRUE(y[3].IsInt());
EXPECT_EQ(123, y[3].GetInt());
EXPECT_TRUE(y[4].IsString());
EXPECT_STREQ("foo", y[4].GetString());
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
// PushBack(GenericValue&&, Allocator&);
......@@ -691,11 +719,11 @@ TEST(Value, Array) {
y.PushBack(Value(true), allocator);
y.PushBack(std::move(Value(kArrayType).PushBack(Value(1), allocator).PushBack("foo", allocator)), allocator);
EXPECT_EQ(2u, y.Size());
EXPECT_TRUE(y[0u].IsTrue());
EXPECT_TRUE(y[1u].IsArray());
EXPECT_EQ(2u, y[1u].Size());
EXPECT_TRUE(y[1u][0u].IsInt());
EXPECT_TRUE(y[1u][1u].IsString());
EXPECT_TRUE(y[0].IsTrue());
EXPECT_TRUE(y[1].IsArray());
EXPECT_EQ(2u, y[1].Size());
EXPECT_TRUE(y[1][0].IsInt());
EXPECT_TRUE(y[1][1].IsString());
}
#endif
......@@ -741,9 +769,9 @@ TEST(Value, Array) {
x.PopBack();
EXPECT_EQ(4u, x.Size());
EXPECT_TRUE(y[SizeType(0)].IsNull());
EXPECT_TRUE(y[1u].IsTrue());
EXPECT_TRUE(y[2u].IsFalse());
EXPECT_TRUE(y[3u].IsInt());
EXPECT_TRUE(y[1].IsTrue());
EXPECT_TRUE(y[2].IsFalse());
EXPECT_TRUE(y[3].IsInt());
// Clear()
x.Clear();
......@@ -764,23 +792,23 @@ TEST(Value, Array) {
EXPECT_EQ(x.Begin(), itr);
EXPECT_EQ(9u, x.Size());
for (int i = 0; i < 9; i++)
EXPECT_EQ(i + 1, x[i][0u].GetInt());
EXPECT_EQ(i + 1, x[i][0].GetInt());
// Ease the last
itr = x.Erase(x.End() - 1);
EXPECT_EQ(x.End(), itr);
EXPECT_EQ(8u, x.Size());
for (int i = 0; i < 8; i++)
EXPECT_EQ(i + 1, x[i][0u].GetInt());
EXPECT_EQ(i + 1, x[i][0].GetInt());
// Erase the middle
itr = x.Erase(x.Begin() + 4);
EXPECT_EQ(x.Begin() + 4, itr);
EXPECT_EQ(7u, x.Size());
for (int i = 0; i < 4; i++)
EXPECT_EQ(i + 1, x[i][0u].GetInt());
EXPECT_EQ(i + 1, x[i][0].GetInt());
for (int i = 4; i < 7; i++)
EXPECT_EQ(i + 2, x[i][0u].GetInt());
EXPECT_EQ(i + 2, x[i][0].GetInt());
// Erase(ValueIterator, ValueIterator)
// Exhaustive test with all 0 <= first < n, first <= last <= n cases
......@@ -800,9 +828,9 @@ TEST(Value, Array) {
size_t removeCount = last - first;
EXPECT_EQ(n - removeCount, x.Size());
for (unsigned i = 0; i < first; i++)
EXPECT_EQ(i, x[i][0u].GetUint());
EXPECT_EQ(i, x[i][0].GetUint());
for (unsigned i = first; i < n - removeCount; i++)
EXPECT_EQ(i + removeCount, x[i][0u].GetUint());
EXPECT_EQ(i + removeCount, x[i][0].GetUint());
}
}
......@@ -1012,7 +1040,7 @@ TEST(Value, Object) {
for (; itr != x.MemberEnd(); ++itr) {
int i = (itr - x.MemberBegin()) + 1;
EXPECT_STREQ(itr->name.GetString(), keys[i]);
EXPECT_EQ(i, itr->value[0u].GetInt());
EXPECT_EQ(i, itr->value[0].GetInt());
}
// Erase the last
......@@ -1023,7 +1051,7 @@ TEST(Value, Object) {
for (; itr != x.MemberEnd(); ++itr) {
int i = (itr - x.MemberBegin()) + 1;
EXPECT_STREQ(itr->name.GetString(), keys[i]);
EXPECT_EQ(i, itr->value[0u].GetInt());
EXPECT_EQ(i, itr->value[0].GetInt());
}
// Erase the middle
......@@ -1035,7 +1063,7 @@ TEST(Value, Object) {
int i = (itr - x.MemberBegin());
i += (i<4) ? 1 : 2;
EXPECT_STREQ(itr->name.GetString(), keys[i]);
EXPECT_EQ(i, itr->value[0u].GetInt());
EXPECT_EQ(i, itr->value[0].GetInt());
}
// EraseMember(ConstMemberIterator, ConstMemberIterator)
......@@ -1056,9 +1084,9 @@ TEST(Value, Object) {
size_t removeCount = last - first;
EXPECT_EQ(n - removeCount, x.MemberCount());
for (unsigned i = 0; i < first; i++)
EXPECT_EQ(i, x[keys[i]][0u].GetUint());
EXPECT_EQ(i, x[keys[i]][0].GetUint());
for (unsigned i = first; i < n - removeCount; i++)
EXPECT_EQ(i + removeCount, x[keys[i+removeCount]][0u].GetUint());
EXPECT_EQ(i + removeCount, x[keys[i+removeCount]][0].GetUint());
}
}
......
Baptiste Lepilleur <blep@users.sourceforge.net>
The json-cpp library and this documentation are in Public Domain.
* Introduction:
=============
JSON (JavaScript Object Notation) is a lightweight data-interchange format.
It can represent integer, real number, string, an ordered sequence of
value, and a collection of name/value pairs.
JsonCpp is a simple API to manipulate JSON value, handle serialization
and unserialization to string.
It can also preserve existing comment in unserialization/serialization steps,
making it a convenient format to store user input files.
Unserialization parsing is user friendly and provides precise error reports.
* Building/Testing:
=================
JsonCpp uses Scons (http://www.scons.org) as a build system. Scons requires
python to be installed (http://www.python.org).
You download scons-local distribution from the following url:
http://sourceforge.net/project/showfiles.php?group_id=30337&package_id=67375
Unzip it in the directory where you found this README file. scons.py Should be
at the same level as README.
python scons.py platform=PLTFRM [TARGET]
where PLTFRM may be one of:
suncc Sun C++ (Solaris)
vacpp Visual Age C++ (AIX)
mingw
msvc6 Microsoft Visual Studio 6 service pack 5-6
msvc70 Microsoft Visual Studio 2002
msvc71 Microsoft Visual Studio 2003
msvc80 Microsoft Visual Studio 2005
linux-gcc Gnu C++ (linux, also reported to work for Mac OS X)
adding platform is fairly simple. You need to change the Sconstruct file
to do so.
and TARGET may be:
check: build library and run unit tests.
* Running the test manually:
==========================
cd test
# This will run the Reader/Writer tests
python runjsontests.py "path to jsontest.exe"
# This will run the Reader/Writer tests, using JSONChecker test suite
# (http://www.json.org/JSON_checker/).
# Notes: not all tests pass: JsonCpp is too lenient (for example,
# it allows an integer to start with '0'). The goal is to improve
# strict mode parsing to get all tests to pass.
python runjsontests.py --with-json-checker "path to jsontest.exe"
# This will run the unit tests (mostly Value)
python rununittests.py "path to test_lib_json.exe"
You can run the tests using valgrind:
python rununittests.py --valgrind "path to test_lib_json.exe"
* Building the documentation:
===========================
Run the python script doxybuild.py from the top directory:
python doxybuild.py --open --with-dot
See doxybuild.py --help for options.
* Adding a reader/writer test:
============================
To add a test, you need to create two files in test/data:
- a TESTNAME.json file, that contains the input document in JSON format.
- a TESTNAME.expected file, that contains a flatened representation of
the input document.
TESTNAME.expected file format:
- each line represents a JSON element of the element tree represented
by the input document.
- each line has two parts: the path to access the element separated from
the element value by '='. Array and object values are always empty
(e.g. represented by either [] or {}).
- element path: '.' represented the root element, and is used to separate
object members. [N] is used to specify the value of an array element
at index N.
See test_complex_01.json and test_complex_01.expected to better understand
element path.
* Understanding reader/writer test output:
========================================
When a test is run, output files are generated aside the input test files.
Below is a short description of the content of each file:
- test_complex_01.json: input JSON document
- test_complex_01.expected: flattened JSON element tree used to check if
parsing was corrected.
- test_complex_01.actual: flattened JSON element tree produced by
jsontest.exe from reading test_complex_01.json
- test_complex_01.rewrite: JSON document written by jsontest.exe using the
Json::Value parsed from test_complex_01.json and serialized using
Json::StyledWritter.
- test_complex_01.actual-rewrite: flattened JSON element tree produced by
jsontest.exe from reading test_complex_01.rewrite.
test_complex_01.process-output: jsontest.exe output, typically useful to
understand parsing error.
#ifndef JSON_AUTOLINK_H_INCLUDED
# define JSON_AUTOLINK_H_INCLUDED
# include "config.h"
# ifdef JSON_IN_CPPTL
# include <cpptl/cpptl_autolink.h>
# endif
# if !defined(JSON_NO_AUTOLINK) && !defined(JSON_DLL_BUILD) && !defined(JSON_IN_CPPTL)
# define CPPTL_AUTOLINK_NAME "json"
# undef CPPTL_AUTOLINK_DLL
# ifdef JSON_DLL
# define CPPTL_AUTOLINK_DLL
# endif
# include "autolink.h"
# endif
#endif // JSON_AUTOLINK_H_INCLUDED
#ifndef JSON_CONFIG_H_INCLUDED
# define JSON_CONFIG_H_INCLUDED
/// If defined, indicates that json library is embedded in CppTL library.
//# define JSON_IN_CPPTL 1
/// If defined, indicates that json may leverage CppTL library
//# define JSON_USE_CPPTL 1
/// If defined, indicates that cpptl vector based map should be used instead of std::map
/// as Value container.
//# define JSON_USE_CPPTL_SMALLMAP 1
/// If defined, indicates that Json specific container should be used
/// (hash table & simple deque container with customizable allocator).
/// THIS FEATURE IS STILL EXPERIMENTAL!
//# define JSON_VALUE_USE_INTERNAL_MAP 1
/// Force usage of standard new/malloc based allocator instead of memory pool based allocator.
/// The memory pools allocator used optimization (initializing Value and ValueInternalLink
/// as if it was a POD) that may cause some validation tool to report errors.
/// Only has effects if JSON_VALUE_USE_INTERNAL_MAP is defined.
//# define JSON_USE_SIMPLE_INTERNAL_ALLOCATOR 1
/// If defined, indicates that Json use exception to report invalid type manipulation
/// instead of C assert macro.
# define JSON_USE_EXCEPTION 1
# ifdef JSON_IN_CPPTL
# include <cpptl/config.h>
# ifndef JSON_USE_CPPTL
# define JSON_USE_CPPTL 1
# endif
# endif
# ifdef JSON_IN_CPPTL
# define JSON_API CPPTL_API
# elif defined(JSON_DLL_BUILD)
# define JSON_API __declspec(dllexport)
# elif defined(JSON_DLL)
# define JSON_API __declspec(dllimport)
# else
# define JSON_API
# endif
#endif // JSON_CONFIG_H_INCLUDED
#ifndef CPPTL_JSON_FEATURES_H_INCLUDED
# define CPPTL_JSON_FEATURES_H_INCLUDED
# include "forwards.h"
namespace Json {
/** \brief Configuration passed to reader and writer.
* This configuration object can be used to force the Reader or Writer
* to behave in a standard conforming way.
*/
class JSON_API Features
{
public:
/** \brief A configuration that allows all features and assumes all strings are UTF-8.
* - C & C++ comments are allowed
* - Root object can be any JSON value
* - Assumes Value strings are encoded in UTF-8
*/
static Features all();
/** \brief A configuration that is strictly compatible with the JSON specification.
* - Comments are forbidden.
* - Root object must be either an array or an object value.
* - Assumes Value strings are encoded in UTF-8
*/
static Features strictMode();
/** \brief Initialize the configuration like JsonConfig::allFeatures;
*/
Features();
/// \c true if comments are allowed. Default: \c true.
bool allowComments_;
/// \c true if root must be either an array or an object value. Default: \c false.
bool strictRoot_;
};
} // namespace Json
#endif // CPPTL_JSON_FEATURES_H_INCLUDED
#ifndef JSON_FORWARDS_H_INCLUDED
# define JSON_FORWARDS_H_INCLUDED
# include "config.h"
namespace Json {
// writer.h
class FastWriter;
class StyledWriter;
// reader.h
class Reader;
// features.h
class Features;
// value.h
typedef int Int;
typedef unsigned int UInt;
class StaticString;
class Path;
class PathArgument;
class Value;
class ValueIteratorBase;
class ValueIterator;
class ValueConstIterator;
#ifdef JSON_VALUE_USE_INTERNAL_MAP
class ValueAllocator;
class ValueMapAllocator;
class ValueInternalLink;
class ValueInternalArray;
class ValueInternalMap;
#endif // #ifdef JSON_VALUE_USE_INTERNAL_MAP
} // namespace Json
#endif // JSON_FORWARDS_H_INCLUDED
#ifndef JSON_JSON_H_INCLUDED
# define JSON_JSON_H_INCLUDED
# include "autolink.h"
# include "value.h"
# include "reader.h"
# include "writer.h"
# include "features.h"
#endif // JSON_JSON_H_INCLUDED
#ifndef CPPTL_JSON_READER_H_INCLUDED
# define CPPTL_JSON_READER_H_INCLUDED
# include "features.h"
# include "value.h"
# include <deque>
# include <stack>
# include <string>
# include <iostream>
namespace Json {
/** \brief Unserialize a <a HREF="http://www.json.org">JSON</a> document into a Value.
*
*/
class JSON_API Reader
{
public:
typedef char Char;
typedef const Char *Location;
/** \brief Constructs a Reader allowing all features
* for parsing.
*/
Reader();
/** \brief Constructs a Reader allowing the specified feature set
* for parsing.
*/
Reader( const Features &features );
/** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a> document.
* \param document UTF-8 encoded string containing the document to read.
* \param root [out] Contains the root value of the document if it was
* successfully parsed.
* \param collectComments \c true to collect comment and allow writing them back during
* serialization, \c false to discard comments.
* This parameter is ignored if Features::allowComments_
* is \c false.
* \return \c true if the document was successfully parsed, \c false if an error occurred.
*/
bool parse( const std::string &document,
Value &root,
bool collectComments = true );
/** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a> document.
* \param document UTF-8 encoded string containing the document to read.
* \param root [out] Contains the root value of the document if it was
* successfully parsed.
* \param collectComments \c true to collect comment and allow writing them back during
* serialization, \c false to discard comments.
* This parameter is ignored if Features::allowComments_
* is \c false.
* \return \c true if the document was successfully parsed, \c false if an error occurred.
*/
bool parse( const char *beginDoc, const char *endDoc,
Value &root,
bool collectComments = true );
/// \brief Parse from input stream.
/// \see Json::operator>>(std::istream&, Json::Value&).
bool parse( std::istream &is,
Value &root,
bool collectComments = true );
/** \brief Returns a user friendly string that list errors in the parsed document.
* \return Formatted error message with the list of errors with their location in
* the parsed document. An empty string is returned if no error occurred
* during parsing.
*/
std::string getFormatedErrorMessages() const;
private:
enum TokenType
{
tokenEndOfStream = 0,
tokenObjectBegin,
tokenObjectEnd,
tokenArrayBegin,
tokenArrayEnd,
tokenString,
tokenNumber,
tokenTrue,
tokenFalse,
tokenNull,
tokenArraySeparator,
tokenMemberSeparator,
tokenComment,
tokenError
};
class Token
{
public:
TokenType type_;
Location start_;
Location end_;
};
class ErrorInfo
{
public:
Token token_;
std::string message_;
Location extra_;
};
typedef std::deque<ErrorInfo> Errors;
bool expectToken( TokenType type, Token &token, const char *message );
bool readToken( Token &token );
void skipSpaces();
bool match( Location pattern,
int patternLength );
bool readComment();
bool readCStyleComment();
bool readCppStyleComment();
bool readString();
void readNumber();
bool readValue();
bool readObject( Token &token );
bool readArray( Token &token );
bool decodeNumber( Token &token );
bool decodeString( Token &token );
bool decodeString( Token &token, std::string &decoded );
bool decodeDouble( Token &token );
bool decodeUnicodeCodePoint( Token &token,
Location &current,
Location end,
unsigned int &unicode );
bool decodeUnicodeEscapeSequence( Token &token,
Location &current,
Location end,
unsigned int &unicode );
bool addError( const std::string &message,
Token &token,
Location extra = 0 );
bool recoverFromError( TokenType skipUntilToken );
bool addErrorAndRecover( const std::string &message,
Token &token,
TokenType skipUntilToken );
void skipUntilSpace();
Value &currentValue();
Char getNextChar();
void getLocationLineAndColumn( Location location,
int &line,
int &column ) const;
std::string getLocationLineAndColumn( Location location ) const;
void addComment( Location begin,
Location end,
CommentPlacement placement );
void skipCommentTokens( Token &token );
typedef std::stack<Value *> Nodes;
Nodes nodes_;
Errors errors_;
std::string document_;
Location begin_;
Location end_;
Location current_;
Location lastValueEnd_;
Value *lastValue_;
std::string commentsBefore_;
Features features_;
bool collectComments_;
};
/** \brief Read from 'sin' into 'root'.
Always keep comments from the input JSON.
This can be used to read a file into a particular sub-object.
For example:
\code
Json::Value root;
cin >> root["dir"]["file"];
cout << root;
\endcode
Result:
\verbatim
{
"dir": {
"file": {
// The input stream JSON would be nested here.
}
}
}
\endverbatim
\throw std::exception on parse error.
\see Json::operator<<()
*/
std::istream& operator>>( std::istream&, Value& );
} // namespace Json
#endif // CPPTL_JSON_READER_H_INCLUDED
This diff is collapsed.
#ifndef JSON_WRITER_H_INCLUDED
# define JSON_WRITER_H_INCLUDED
# include "value.h"
# include <vector>
# include <string>
# include <iostream>
namespace Json {
class Value;
/** \brief Abstract class for writers.
*/
class JSON_API Writer
{
public:
virtual ~Writer();
virtual std::string write( const Value &root ) = 0;
};
/** \brief Outputs a Value in <a HREF="http://www.json.org">JSON</a> format without formatting (not human friendly).
*
* The JSON document is written in a single line. It is not intended for 'human' consumption,
* but may be usefull to support feature such as RPC where bandwith is limited.
* \sa Reader, Value
*/
class JSON_API FastWriter : public Writer
{
public:
FastWriter();
virtual ~FastWriter(){}
void enableYAMLCompatibility();
public: // overridden from Writer
virtual std::string write( const Value &root );
private:
void writeValue( const Value &value );
std::string document_;
bool yamlCompatiblityEnabled_;
};
/** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> format in a human friendly way.
*
* The rules for line break and indent are as follow:
* - Object value:
* - if empty then print {} without indent and line break
* - if not empty the print '{', line break & indent, print one value per line
* and then unindent and line break and print '}'.
* - Array value:
* - if empty then print [] without indent and line break
* - if the array contains no object value, empty array or some other value types,
* and all the values fit on one lines, then print the array on a single line.
* - otherwise, it the values do not fit on one line, or the array contains
* object or non empty array, then print one value per line.
*
* If the Value have comments then they are outputed according to their #CommentPlacement.
*
* \sa Reader, Value, Value::setComment()
*/
class JSON_API StyledWriter: public Writer
{
public:
StyledWriter();
virtual ~StyledWriter(){}
public: // overridden from Writer
/** \brief Serialize a Value in <a HREF="http://www.json.org">JSON</a> format.
* \param root Value to serialize.
* \return String containing the JSON document that represents the root value.
*/
virtual std::string write( const Value &root );
private:
void writeValue( const Value &value );
void writeArrayValue( const Value &value );
bool isMultineArray( const Value &value );
void pushValue( const std::string &value );
void writeIndent();
void writeWithIndent( const std::string &value );
void indent();
void unindent();
void writeCommentBeforeValue( const Value &root );
void writeCommentAfterValueOnSameLine( const Value &root );
bool hasCommentForValue( const Value &value );
static std::string normalizeEOL( const std::string &text );
typedef std::vector<std::string> ChildValues;
ChildValues childValues_;
std::string document_;
std::string indentString_;
int rightMargin_;
int indentSize_;
bool addChildValues_;
};
/** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> format in a human friendly way,
to a stream rather than to a string.
*
* The rules for line break and indent are as follow:
* - Object value:
* - if empty then print {} without indent and line break
* - if not empty the print '{', line break & indent, print one value per line
* and then unindent and line break and print '}'.
* - Array value:
* - if empty then print [] without indent and line break
* - if the array contains no object value, empty array or some other value types,
* and all the values fit on one lines, then print the array on a single line.
* - otherwise, it the values do not fit on one line, or the array contains
* object or non empty array, then print one value per line.
*
* If the Value have comments then they are outputed according to their #CommentPlacement.
*
* \param indentation Each level will be indented by this amount extra.
* \sa Reader, Value, Value::setComment()
*/
class JSON_API StyledStreamWriter
{
public:
StyledStreamWriter( std::string indentation="\t" );
~StyledStreamWriter(){}
public:
/** \brief Serialize a Value in <a HREF="http://www.json.org">JSON</a> format.
* \param out Stream to write to. (Can be ostringstream, e.g.)
* \param root Value to serialize.
* \note There is no point in deriving from Writer, since write() should not return a value.
*/
void write( std::ostream &out, const Value &root );
private:
void writeValue( const Value &value );
void writeArrayValue( const Value &value );
bool isMultineArray( const Value &value );
void pushValue( const std::string &value );
void writeIndent();
void writeWithIndent( const std::string &value );
void indent();
void unindent();
void writeCommentBeforeValue( const Value &root );
void writeCommentAfterValueOnSameLine( const Value &root );
bool hasCommentForValue( const Value &value );
static std::string normalizeEOL( const std::string &text );
typedef std::vector<std::string> ChildValues;
ChildValues childValues_;
std::ostream* document_;
std::string indentString_;
int rightMargin_;
std::string indentation_;
bool addChildValues_;
};
std::string JSON_API valueToString( Int value );
std::string JSON_API valueToString( UInt value );
std::string JSON_API valueToString( double value );
std::string JSON_API valueToString( bool value );
std::string JSON_API valueToQuotedString( const char *value );
/// \brief Output using the StyledStreamWriter.
/// \see Json::operator>>()
std::ostream& operator<<( std::ostream&, const Value &root );
} // namespace Json
#endif // JSON_WRITER_H_INCLUDED
#include <json/json.h>
#include <algorithm> // sort
#include <stdio.h>
#if defined(_MSC_VER) && _MSC_VER >= 1310
# pragma warning( disable: 4996 ) // disable fopen deprecation warning
#endif
static std::string
readInputTestFile( const char *path )
{
FILE *file = fopen( path, "rb" );
if ( !file )
return std::string("");
fseek( file, 0, SEEK_END );
long size = ftell( file );
fseek( file, 0, SEEK_SET );
std::string text;
char *buffer = new char[size+1];
buffer[size] = 0;
if ( fread( buffer, 1, size, file ) == (unsigned long)size )
text = buffer;
fclose( file );
delete[] buffer;
return text;
}
static void
printValueTree( FILE *fout, Json::Value &value, const std::string &path = "." )
{
switch ( value.type() )
{
case Json::nullValue:
fprintf( fout, "%s=null\n", path.c_str() );
break;
case Json::intValue:
fprintf( fout, "%s=%d\n", path.c_str(), value.asInt() );
break;
case Json::uintValue:
fprintf( fout, "%s=%u\n", path.c_str(), value.asUInt() );
break;
case Json::realValue:
fprintf( fout, "%s=%.16g\n", path.c_str(), value.asDouble() );
break;
case Json::stringValue:
fprintf( fout, "%s=\"%s\"\n", path.c_str(), value.asString().c_str() );
break;
case Json::booleanValue:
fprintf( fout, "%s=%s\n", path.c_str(), value.asBool() ? "true" : "false" );
break;
case Json::arrayValue:
{
fprintf( fout, "%s=[]\n", path.c_str() );
int size = value.size();
for ( int index =0; index < size; ++index )
{
static char buffer[16];
sprintf( buffer, "[%d]", index );
printValueTree( fout, value[index], path + buffer );
}
}
break;
case Json::objectValue:
{
fprintf( fout, "%s={}\n", path.c_str() );
Json::Value::Members members( value.getMemberNames() );
std::sort( members.begin(), members.end() );
std::string suffix = *(path.end()-1) == '.' ? "" : ".";
for ( Json::Value::Members::iterator it = members.begin();
it != members.end();
++it )
{
const std::string &name = *it;
printValueTree( fout, value[name], path + suffix + name );
}
}
break;
default:
break;
}
}
static int
parseAndSaveValueTree( const std::string &input,
const std::string &actual,
const std::string &kind,
Json::Value &root,
const Json::Features &features,
bool parseOnly )
{
Json::Reader reader( features );
bool parsingSuccessful = reader.parse( input, root );
if ( !parsingSuccessful )
{
printf( "Failed to parse %s file: \n%s\n",
kind.c_str(),
reader.getFormatedErrorMessages().c_str() );
return 1;
}
if ( !parseOnly )
{
FILE *factual = fopen( actual.c_str(), "wt" );
if ( !factual )
{
printf( "Failed to create %s actual file.\n", kind.c_str() );
return 2;
}
printValueTree( factual, root );
fclose( factual );
}
return 0;
}
static int
rewriteValueTree( const std::string &rewritePath,
const Json::Value &root,
std::string &rewrite )
{
//Json::FastWriter writer;
//writer.enableYAMLCompatibility();
Json::StyledWriter writer;
rewrite = writer.write( root );
FILE *fout = fopen( rewritePath.c_str(), "wt" );
if ( !fout )
{
printf( "Failed to create rewrite file: %s\n", rewritePath.c_str() );
return 2;
}
fprintf( fout, "%s\n", rewrite.c_str() );
fclose( fout );
return 0;
}
static std::string
removeSuffix( const std::string &path,
const std::string &extension )
{
if ( extension.length() >= path.length() )
return std::string("");
std::string suffix = path.substr( path.length() - extension.length() );
if ( suffix != extension )
return std::string("");
return path.substr( 0, path.length() - extension.length() );
}
static int
printUsage( const char *argv[] )
{
printf( "Usage: %s [--strict] input-json-file", argv[0] );
return 3;
}
int
parseCommandLine( int argc, const char *argv[],
Json::Features &features, std::string &path,
bool &parseOnly )
{
parseOnly = false;
if ( argc < 2 )
{
return printUsage( argv );
}
int index = 1;
if ( std::string(argv[1]) == "--json-checker" )
{
features = Json::Features::strictMode();
parseOnly = true;
++index;
}
if ( index == argc || index + 1 < argc )
{
return printUsage( argv );
}
path = argv[index];
return 0;
}
int main( int argc, const char *argv[] )
{
std::string path;
Json::Features features;
bool parseOnly;
int exitCode = parseCommandLine( argc, argv, features, path, parseOnly );
if ( exitCode != 0 )
{
return exitCode;
}
std::string input = readInputTestFile( path.c_str() );
if ( input.empty() )
{
printf( "Failed to read input or empty input: %s\n", path.c_str() );
return 3;
}
std::string basePath = removeSuffix( argv[1], ".json" );
if ( !parseOnly && basePath.empty() )
{
printf( "Bad input path. Path does not end with '.expected':\n%s\n", path.c_str() );
return 3;
}
std::string actualPath = basePath + ".actual";
std::string rewritePath = basePath + ".rewrite";
std::string rewriteActualPath = basePath + ".actual-rewrite";
Json::Value root;
exitCode = parseAndSaveValueTree( input, actualPath, "input", root, features, parseOnly );
if ( exitCode == 0 && !parseOnly )
{
std::string rewrite;
exitCode = rewriteValueTree( rewritePath, root, rewrite );
if ( exitCode == 0 )
{
Json::Value rewriteRoot;
exitCode = parseAndSaveValueTree( rewrite, rewriteActualPath,
"rewrite", rewriteRoot, features, parseOnly );
}
}
return exitCode;
}
Import( 'env_testing buildJSONTests' )
buildJSONTests( env_testing, Split( """
main.cpp
""" ),
'jsontestrunner' )
# For 'check' to work, 'libs' must be built first.
env_testing.Depends('jsontestrunner', '#libs')
#ifndef JSONCPP_BATCHALLOCATOR_H_INCLUDED
# define JSONCPP_BATCHALLOCATOR_H_INCLUDED
# include <stdlib.h>
# include <assert.h>
# ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
namespace Json {
/* Fast memory allocator.
*
* This memory allocator allocates memory for a batch of object (specified by
* the page size, the number of object in each page).
*
* It does not allow the destruction of a single object. All the allocated objects
* can be destroyed at once. The memory can be either released or reused for future
* allocation.
*
* The in-place new operator must be used to construct the object using the pointer
* returned by allocate.
*/
template<typename AllocatedType
,const unsigned int objectPerAllocation>
class BatchAllocator
{
public:
typedef AllocatedType Type;
BatchAllocator( unsigned int objectsPerPage = 255 )
: freeHead_( 0 )
, objectsPerPage_( objectsPerPage )
{
// printf( "Size: %d => %s\n", sizeof(AllocatedType), typeid(AllocatedType).name() );
assert( sizeof(AllocatedType) * objectPerAllocation >= sizeof(AllocatedType *) ); // We must be able to store a slist in the object free space.
assert( objectsPerPage >= 16 );
batches_ = allocateBatch( 0 ); // allocated a dummy page
currentBatch_ = batches_;
}
~BatchAllocator()
{
for ( BatchInfo *batch = batches_; batch; )
{
BatchInfo *nextBatch = batch->next_;
free( batch );
batch = nextBatch;
}
}
/// allocate space for an array of objectPerAllocation object.
/// @warning it is the responsability of the caller to call objects constructors.
AllocatedType *allocate()
{
if ( freeHead_ ) // returns node from free list.
{
AllocatedType *object = freeHead_;
freeHead_ = *(AllocatedType **)object;
return object;
}
if ( currentBatch_->used_ == currentBatch_->end_ )
{
currentBatch_ = currentBatch_->next_;
while ( currentBatch_ && currentBatch_->used_ == currentBatch_->end_ )
currentBatch_ = currentBatch_->next_;
if ( !currentBatch_ ) // no free batch found, allocate a new one
{
currentBatch_ = allocateBatch( objectsPerPage_ );
currentBatch_->next_ = batches_; // insert at the head of the list
batches_ = currentBatch_;
}
}
AllocatedType *allocated = currentBatch_->used_;
currentBatch_->used_ += objectPerAllocation;
return allocated;
}
/// Release the object.
/// @warning it is the responsability of the caller to actually destruct the object.
void release( AllocatedType *object )
{
assert( object != 0 );
*(AllocatedType **)object = freeHead_;
freeHead_ = object;
}
private:
struct BatchInfo
{
BatchInfo *next_;
AllocatedType *used_;
AllocatedType *end_;
AllocatedType buffer_[objectPerAllocation];
};
// disabled copy constructor and assignement operator.
BatchAllocator( const BatchAllocator & );
void operator =( const BatchAllocator &);
static BatchInfo *allocateBatch( unsigned int objectsPerPage )
{
const unsigned int mallocSize = sizeof(BatchInfo) - sizeof(AllocatedType)* objectPerAllocation
+ sizeof(AllocatedType) * objectPerAllocation * objectsPerPage;
BatchInfo *batch = static_cast<BatchInfo*>( malloc( mallocSize ) );
batch->next_ = 0;
batch->used_ = batch->buffer_;
batch->end_ = batch->buffer_ + objectsPerPage;
return batch;
}
BatchInfo *batches_;
BatchInfo *currentBatch_;
/// Head of a single linked list within the allocated space of freeed object
AllocatedType *freeHead_;
unsigned int objectsPerPage_;
};
} // namespace Json
# endif // ifndef JSONCPP_DOC_INCLUDE_IMPLEMENTATION
#endif // JSONCPP_BATCHALLOCATOR_H_INCLUDED
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Import( 'env buildLibrary' )
buildLibrary( env, Split( """
json_reader.cpp
json_value.cpp
json_writer.cpp
""" ),
'json' )
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Import( 'env_testing buildUnitTests' )
buildUnitTests( env_testing, Split( """
main.cpp
jsontest.cpp
""" ),
'test_lib_json' )
# For 'check' to work, 'libs' must be built first.
env_testing.Depends('test_lib_json', '#libs')
0.5.0
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Copyright (c) 2007-2011, Lloyd Hilaiel <lloyd@hilaiel.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
\ No newline at end of file
This diff is collapsed.
**********************************************************************
This is YAJL 2, for the legacy version of YAJL. see
https://github.com/lloyd/yajl/tree/1.x
**********************************************************************
Welcome to Yet Another JSON Library (YAJL)
## Why does the world need another C library for parsing JSON?
Good question. In a review of current C JSON parsing libraries I was
unable to find one that satisfies my requirements. Those are,
0. written in C
1. portable
2. robust -- as close to "crash proof" as possible
3. data representation independent
4. fast
5. generates verbose, useful error messages including context of where
the error occurs in the input text.
6. can parse JSON data off a stream, incrementally
7. simple to use
8. tiny
Numbers 3, 5, 6, and 7 where particularly hard to find, and were what
caused me to ultimately create YAJL. This document is a tour of some
of the more important aspects of YAJL.
## YAJL is Free.
Permissive licensing means you can use it in open source and
commercial products alike without any fees. My request beyond the
licensing is that if you find bugs drop me a email, or better yet,
fork and fix.
Porting YAJL should be trivial, the implementation is ANSI C. If you
port to new systems I'd love to hear of it and integrate your patches.
## YAJL is data representation independent.
BYODR! Many JSON libraries impose a structure based data representation
on you. This is a benefit in some cases and a drawback in others.
YAJL uses callbacks to remain agnostic of the in-memory representation.
So if you wish to build up an in-memory representation, you may do so
using YAJL, but you must bring the code that defines and populates the
in memory structure.
This also means that YAJL can be used by other (higher level) JSON
libraries if so desired.
## YAJL supports stream parsing
This means you do not need to hold the whole JSON representation in
textual form in memory. This makes YAJL ideal for filtering projects,
where you're converting YAJL from one form to another (i.e. XML). The
included JSON pretty printer is an example of such a filter program.
## YAJL is fast
Minimal memory copying is performed. YAJL, when possible, returns
pointers into the client provided text (i.e. for strings that have no
embedded escape chars, hopefully the common case). I've put a lot of
effort into profiling and tuning performance, but I have ignored a
couple possible performance improvements to keep the interface clean,
small, and flexible. My hope is that YAJL will perform comparably to
the fastest JSON parser out there.
YAJL should impose both minimal CPU and memory requirements on your
application.
## YAJL is tiny.
Fat free. No whip.
enjoy,
Lloyd - July, 2007
* add a test for 0x1F bug
* numeric overflow in integers and double
* line and char offsets in the lexer and in error messages
* testing:
a. the permuter
b. some performance comparison against json_checker.
* investigate pull instead of push parsing
* Handle memory allocation failures gracefully
* cygwin/msys support on win32
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
#ifndef YAJL_VERSION_H_
#define YAJL_VERSION_H_
#include <yajl/yajl_common.h>
#define YAJL_MAJOR 2
#define YAJL_MINOR 0
#define YAJL_MICRO 1
#define YAJL_VERSION ((YAJL_MAJOR * 10000) + (YAJL_MINOR * 100) + YAJL_MICRO)
#ifdef __cplusplus
extern "C" {
#endif
extern int YAJL_API yajl_version(void);
#ifdef __cplusplus
}
#endif
#endif /* YAJL_VERSION_H_ */
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
#ifndef YAJL_VERSION_H_
#define YAJL_VERSION_H_
#include <yajl/yajl_common.h>
#define YAJL_MAJOR ${YAJL_MAJOR}
#define YAJL_MINOR ${YAJL_MINOR}
#define YAJL_MICRO ${YAJL_MICRO}
#define YAJL_VERSION ((YAJL_MAJOR * 10000) + (YAJL_MINOR * 100) + YAJL_MICRO)
#ifdef __cplusplus
extern "C" {
#endif
extern int YAJL_API yajl_version(void);
#ifdef __cplusplus
}
#endif
#endif /* YAJL_VERSION_H_ */
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment