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

Conflicts:
	include/rapidjson/internal/dtoa.h
	test/unittest/readertest.cpp
This commit is contained in:
Milo Yip 2014-11-14 22:23:16 +08:00
commit 57b91300fb
108 changed files with 889 additions and 16742 deletions

1
.gitignore vendored
View File

@ -6,5 +6,6 @@
/build/gmake
/build/vs*/
/doc/html
/doc/doxygen_*.db
/thirdparty/lib
/intermediate

View File

@ -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

View File

@ -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"

View File

@ -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"

View File

@ -1 +0,0 @@
*.pdf

View File

@ -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

View File

@ -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); }

View File

@ -1,6 +1,6 @@
# 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]

View File

@ -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}

View File

@ -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() {

View File

@ -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 = ");

View File

@ -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_

View File

@ -62,7 +62,11 @@ RAPIDJSON_DIAG_OFF(effc++)
#include <iterator> // std::iterator, std::random_access_iterator_tag
#endif
namespace rapidjson {
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
#include <utility> // std::move
#endif
RAPIDJSON_NAMESPACE_BEGIN
// Forward declaration.
template <typename Encoding, typename Allocator>
@ -413,7 +417,6 @@ template <typename T> struct IsGenericValue : IsGenericValueImpl<T>::Type {};
\tparam Encoding Encoding of the value. (Even non-string values need to have the same encoding in a document)
\tparam Allocator Allocator type for allocating memory of object, array and string.
*/
#pragma pack (push, 4)
template <typename Encoding, typename Allocator = MemoryPoolAllocator<> >
class GenericValue {
public:
@ -452,7 +455,7 @@ public:
\param type Type of the value.
\note Default content for number is zero.
*/
GenericValue(Type type) RAPIDJSON_NOEXCEPT : data_(), flags_() {
explicit GenericValue(Type type) RAPIDJSON_NOEXCEPT : data_(), flags_() {
static const unsigned defaultFlags[7] = {
kNullFlag, kFalseFlag, kTrueFlag, kObjectFlag, kArrayFlag, kConstStringFlag,
kNumberAnyFlag
@ -796,22 +799,32 @@ public:
//! Check whether the object is empty.
bool ObjectEmpty() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.size == 0; }
//! Get the value associated with the name.
/*!
//! Get a value from an object associated with the name.
/*! \pre IsObject() == true
\tparam T Either \c Ch or \c const \c Ch (template used for disambiguation with \ref operator[](SizeType))
\note In version 0.1x, if the member is not found, this function returns a null value. This makes issue 7.
Since 0.2, if the name is not correct, it will assert.
If user is unsure whether a member exists, user should use HasMember() first.
A better approach is to use FindMember().
\note Linear time complexity.
*/
GenericValue& operator[](const Ch* name) {
template <typename T>
RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr<internal::IsSame<typename internal::RemoveConst<T>::Type, Ch> >),(GenericValue&)) operator[](T* name) {
GenericValue n(StringRef(name));
return (*this)[n];
}
const GenericValue& operator[](const Ch* name) const { return const_cast<GenericValue&>(*this)[name]; }
template <typename T>
RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr<internal::IsSame<typename internal::RemoveConst<T>::Type, Ch> >),(const GenericValue&)) operator[](T* name) const { return const_cast<GenericValue&>(*this)[name]; }
// This version is faster because it does not need a StrLen().
// It can also handle string with null character.
//! Get a value from an object associated with the name.
/*! \pre IsObject() == true
\tparam SourceAllocator Allocator of the \c name value
\note Compared to \ref operator[](T*), this version is faster because it does not need a StrLen().
And it can also handle strings with embedded null characters.
\note Linear time complexity.
*/
template <typename SourceAllocator>
GenericValue& operator[](const GenericValue<Encoding, SourceAllocator>& name) {
MemberIterator member = FindMember(name);
@ -1022,7 +1035,9 @@ public:
//! Remove a member in object by its name.
/*! \param name Name of member to be removed.
\return Whether the member existed.
\note Removing member is implemented by moving the last member. So the ordering of members is changed.
\note This function may reorder the object members. Use \ref
EraseMember(ConstMemberIterator) if you need to preserve the
relative order of the remaining members.
\note Linear time complexity.
*/
bool RemoveMember(const Ch* name) {
@ -1044,8 +1059,9 @@ public:
//! Remove a member in object by iterator.
/*! \param m member iterator (obtained by FindMember() or MemberBegin()).
\return the new iterator after removal.
\note Removing member is implemented by moving the last member. So the ordering of members is changed.
\note Use \ref EraseMember(ConstMemberIterator) instead, if you need to rely on a stable member ordering.
\note This function may reorder the object members. Use \ref
EraseMember(ConstMemberIterator) if you need to preserve the
relative order of the remaining members.
\note Constant time complexity.
*/
MemberIterator RemoveMember(MemberIterator m) {
@ -1072,7 +1088,8 @@ public:
\pre IsObject() == true && \ref MemberBegin() <= \c pos < \ref MemberEnd()
\return Iterator following the removed element.
If the iterator \c pos refers to the last element, the \ref MemberEnd() iterator is returned.
\note Other than \ref RemoveMember(MemberIterator), this function preserves the ordering of the members.
\note This function preserves the relative order of the remaining object
members. If you do not need this, use the more efficient \ref RemoveMember(MemberIterator).
\note Linear time complexity.
*/
MemberIterator EraseMember(ConstMemberIterator pos) {
@ -1084,7 +1101,8 @@ public:
\param last iterator following the last member to remove
\pre IsObject() == true && \ref MemberBegin() <= \c first <= \c last <= \ref MemberEnd()
\return Iterator following the last removed element.
\note Other than \ref RemoveMember(MemberIterator), this function preserves the ordering of the members.
\note This function preserves the relative order of the remaining object
members.
\note Linear time complexity.
*/
MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) {
@ -1098,7 +1116,7 @@ public:
MemberIterator pos = MemberBegin() + (first - MemberBegin());
for (MemberIterator itr = pos; itr != last; ++itr)
itr->~Member();
memmove(&*pos, &*last, (MemberEnd() - last) * sizeof(Member));
std::memmove(&*pos, &*last, (MemberEnd() - last) * sizeof(Member));
data_.o.size -= (last - first);
return pos;
}
@ -1133,14 +1151,9 @@ public:
}
//! Get an element from array by index.
/*! \param index Zero-based index of element.
\code
Value a(kArrayType);
a.PushBack(123);
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.
\endcode
/*! \pre IsArray() == true
\param index Zero-based index of element.
\see operator[](T*)
*/
GenericValue& operator[](SizeType index) {
RAPIDJSON_ASSERT(IsArray());
@ -1278,7 +1291,7 @@ int z = a[0u].GetInt(); // This works too.
ValueIterator pos = Begin() + (first - Begin());
for (ValueIterator itr = pos; itr != last; ++itr)
itr->~GenericValue();
memmove(pos, last, (End() - last) * sizeof(GenericValue));
std::memmove(pos, last, (End() - last) * sizeof(GenericValue));
data_.a.size -= (last - first);
return pos;
}
@ -1528,7 +1541,7 @@ private:
void SetArrayRaw(GenericValue* values, SizeType count, Allocator& allocator) {
flags_ = kArrayFlag;
data_.a.elements = (GenericValue*)allocator.Malloc(count * sizeof(GenericValue));
memcpy(data_.a.elements, values, count * sizeof(GenericValue));
std::memcpy(data_.a.elements, values, count * sizeof(GenericValue));
data_.a.size = data_.a.capacity = count;
}
@ -1536,7 +1549,7 @@ private:
void SetObjectRaw(Member* members, SizeType count, Allocator& allocator) {
flags_ = kObjectFlag;
data_.o.members = (Member*)allocator.Malloc(count * sizeof(Member));
memcpy(data_.o.members, members, count * sizeof(Member));
std::memcpy(data_.o.members, members, count * sizeof(Member));
data_.o.size = data_.o.capacity = count;
}
@ -1560,7 +1573,7 @@ private:
str = (Ch *)allocator.Malloc((s.length + 1) * sizeof(Ch));
data_.s.str = str;
}
memcpy(str, s, s.length * sizeof(Ch));
std::memcpy(str, s, s.length * sizeof(Ch));
str[s.length] = '\0';
}
@ -1584,13 +1597,12 @@ private:
const Ch* const str2 = rhs.GetString();
if(str1 == str2) { return true; } // fast path for constant string
return (memcmp(str1, str2, sizeof(Ch) * len1) == 0);
return (std::memcmp(str1, str2, sizeof(Ch) * len1) == 0);
}
Data data_;
unsigned flags_;
};
#pragma pack (pop)
//! GenericValue with UTF8 encoding
typedef GenericValue<UTF8<> > Value;
@ -1622,12 +1634,51 @@ public:
allocator_(allocator), ownAllocator_(0), stack_(stackAllocator, stackCapacity), parseResult_()
{
if (!allocator_)
ownAllocator_ = allocator_ = new Allocator();
ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator());
}
~GenericDocument() {
delete ownAllocator_;
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
//! Move constructor in C++11
GenericDocument(GenericDocument&& rhs) RAPIDJSON_NOEXCEPT
: ValueType(std::move(rhs)),
allocator_(rhs.allocator_),
ownAllocator_(rhs.ownAllocator_),
stack_(std::move(rhs.stack_)),
parseResult_(rhs.parseResult_)
{
rhs.allocator_ = 0;
rhs.ownAllocator_ = 0;
rhs.parseResult_ = ParseResult();
}
#endif
~GenericDocument() {
Destroy();
}
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
//! Move assignment in C++11
GenericDocument& operator=(GenericDocument&& rhs) RAPIDJSON_NOEXCEPT
{
// The cast to ValueType is necessary here, because otherwise it would
// attempt to call GenericValue's templated assignment operator.
ValueType::operator=(std::forward<ValueType>(rhs));
// Calling the destructor here would prematurely call stack_'s destructor
Destroy();
allocator_ = rhs.allocator_;
ownAllocator_ = rhs.ownAllocator_;
stack_ = std::move(rhs.stack_);
parseResult_ = rhs.parseResult_;
rhs.allocator_ = 0;
rhs.ownAllocator_ = 0;
rhs.parseResult_ = ParseResult();
return *this;
}
#endif
//!@name Parse from stream
//!@{
@ -1823,6 +1874,10 @@ private:
stack_.ShrinkToFit();
}
void Destroy() {
RAPIDJSON_DELETE(ownAllocator_);
}
static const size_t kDefaultStackCapacity = 1024;
Allocator* allocator_;
Allocator* ownAllocator_;
@ -1844,7 +1899,7 @@ GenericValue<Encoding,Allocator>::GenericValue(const GenericValue<Encoding,Sourc
RawAssign(*d.stack_.template Pop<GenericValue>(1));
}
} // namespace rapidjson
RAPIDJSON_NAMESPACE_END
#if defined(_MSC_VER) || defined(__GNUC__)
RAPIDJSON_DIAG_POP

View File

@ -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

View File

@ -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

View File

@ -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__

View File

@ -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__

View File

@ -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_

View File

@ -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_

View File

@ -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_

View File

@ -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_

View File

@ -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_

View File

@ -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)

View File

@ -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_

View File

@ -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();
}
~Stack() {
Allocator::Free(stack_);
delete ownAllocator; // Only delete if it is owned by the stack
#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() {
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_

View File

@ -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_

View File

@ -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_

View File

@ -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_

View File

@ -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

View File

@ -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_

View File

@ -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

View File

@ -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_

View File

@ -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

View File

@ -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:

View File

@ -1,77 +0,0 @@
// 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_JSONCPP
#include "jsoncpp/src/lib_json/json_reader.cpp"
#include "jsoncpp/src/lib_json/json_value.cpp"
#include "jsoncpp/src/lib_json/json_writer.cpp"
using namespace Json;
class JsonCpp : public PerfTest {
public:
virtual void SetUp() {
PerfTest::SetUp();
Reader reader;
ASSERT_TRUE(reader.parse(json_, root_));
}
protected:
Value root_;
};
TEST_F(JsonCpp, ReaderParse) {
for (int i = 0; i < kTrialCount; i++) {
Value root;
Reader reader;
ASSERT_TRUE(reader.parse(json_, root));
}
}
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;
}
}
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_F(JsonCpp, Whitespace) {
for (int i = 0; i < kTrialCount; i++) {
Value root;
Reader reader;
ASSERT_TRUE(reader.parse(whitespace_, root));
}
}
#endif // TEST_JSONCPP

View File

@ -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
////////////////////////////////////////////////////////////////////////////////

View File

@ -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");

View File

@ -1,84 +0,0 @@
// 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

View File

@ -1,22 +0,0 @@
#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

View File

@ -1,208 +0,0 @@
// 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

View File

@ -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) {

View File

@ -60,7 +60,7 @@ protected:
size_t length_;
};
// Depreciated
// Deprecated
//TEST_F(FileStreamTest, FileStream_Read) {
// FILE *fp = fopen(filename_, "rb");
// ASSERT_TRUE(fp != 0);

View File

@ -0,0 +1,76 @@
// 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"
// test another instantiation of RapidJSON in a different namespace
#define RAPIDJSON_NAMESPACE my::rapid::json
#define RAPIDJSON_NAMESPACE_BEGIN namespace my { namespace rapid { namespace json {
#define RAPIDJSON_NAMESPACE_END } } }
// include lots of RapidJSON files
#include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include "rapidjson/filereadstream.h"
#include "rapidjson/filewritestream.h"
#include "rapidjson/encodedstream.h"
#include "rapidjson/stringbuffer.h"
static const char json[] = "{\"hello\":\"world\",\"t\":true,\"f\":false,\"n\":null,\"i\":123,\"pi\":3.1416,\"a\":[1,2,3,4]}";
TEST(NamespaceTest,Using) {
using namespace RAPIDJSON_NAMESPACE;
typedef GenericDocument<UTF8<>, CrtAllocator> DocumentType;
DocumentType doc;
doc.Parse(json);
EXPECT_TRUE(!doc.HasParseError());
}
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;
StringStream s(json);
StringBuffer buffer;
WriterType writer(buffer);
buffer.ShrinkToFit();
Reader reader;
reader.Parse(s, writer);
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());
}

View File

@ -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

View File

@ -0,0 +1,140 @@
// 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

View File

@ -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());
}
}

View File

@ -1 +0,0 @@
Baptiste Lepilleur <blep@users.sourceforge.net>

View File

@ -1 +0,0 @@
The json-cpp library and this documentation are in Public Domain.

View File

@ -1,117 +0,0 @@
* 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.

View File

@ -1,19 +0,0 @@
#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

View File

@ -1,43 +0,0 @@
#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

View File

@ -1,42 +0,0 @@
#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

View File

@ -1,39 +0,0 @@
#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

View File

@ -1,10 +0,0 @@
#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

View File

@ -1,196 +0,0 @@
#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

File diff suppressed because it is too large Load Diff

View File

@ -1,174 +0,0 @@
#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

View File

@ -1,233 +0,0 @@
#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;
}

View File

@ -1,9 +0,0 @@
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')

View File

@ -1,125 +0,0 @@
#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

View File

@ -1,448 +0,0 @@
// included by json_value.cpp
// everything is within Json namespace
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// class ValueInternalArray
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
ValueArrayAllocator::~ValueArrayAllocator()
{
}
// //////////////////////////////////////////////////////////////////
// class DefaultValueArrayAllocator
// //////////////////////////////////////////////////////////////////
#ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR
class DefaultValueArrayAllocator : public ValueArrayAllocator
{
public: // overridden from ValueArrayAllocator
virtual ~DefaultValueArrayAllocator()
{
}
virtual ValueInternalArray *newArray()
{
return new ValueInternalArray();
}
virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other )
{
return new ValueInternalArray( other );
}
virtual void destructArray( ValueInternalArray *array )
{
delete array;
}
virtual void reallocateArrayPageIndex( Value **&indexes,
ValueInternalArray::PageIndex &indexCount,
ValueInternalArray::PageIndex minNewIndexCount )
{
ValueInternalArray::PageIndex newIndexCount = (indexCount*3)/2 + 1;
if ( minNewIndexCount > newIndexCount )
newIndexCount = minNewIndexCount;
void *newIndexes = realloc( indexes, sizeof(Value*) * newIndexCount );
if ( !newIndexes )
throw std::bad_alloc();
indexCount = newIndexCount;
indexes = static_cast<Value **>( newIndexes );
}
virtual void releaseArrayPageIndex( Value **indexes,
ValueInternalArray::PageIndex indexCount )
{
if ( indexes )
free( indexes );
}
virtual Value *allocateArrayPage()
{
return static_cast<Value *>( malloc( sizeof(Value) * ValueInternalArray::itemsPerPage ) );
}
virtual void releaseArrayPage( Value *value )
{
if ( value )
free( value );
}
};
#else // #ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR
/// @todo make this thread-safe (lock when accessign batch allocator)
class DefaultValueArrayAllocator : public ValueArrayAllocator
{
public: // overridden from ValueArrayAllocator
virtual ~DefaultValueArrayAllocator()
{
}
virtual ValueInternalArray *newArray()
{
ValueInternalArray *array = arraysAllocator_.allocate();
new (array) ValueInternalArray(); // placement new
return array;
}
virtual ValueInternalArray *newArrayCopy( const ValueInternalArray &other )
{
ValueInternalArray *array = arraysAllocator_.allocate();
new (array) ValueInternalArray( other ); // placement new
return array;
}
virtual void destructArray( ValueInternalArray *array )
{
if ( array )
{
array->~ValueInternalArray();
arraysAllocator_.release( array );
}
}
virtual void reallocateArrayPageIndex( Value **&indexes,
ValueInternalArray::PageIndex &indexCount,
ValueInternalArray::PageIndex minNewIndexCount )
{
ValueInternalArray::PageIndex newIndexCount = (indexCount*3)/2 + 1;
if ( minNewIndexCount > newIndexCount )
newIndexCount = minNewIndexCount;
void *newIndexes = realloc( indexes, sizeof(Value*) * newIndexCount );
if ( !newIndexes )
throw std::bad_alloc();
indexCount = newIndexCount;
indexes = static_cast<Value **>( newIndexes );
}
virtual void releaseArrayPageIndex( Value **indexes,
ValueInternalArray::PageIndex indexCount )
{
if ( indexes )
free( indexes );
}
virtual Value *allocateArrayPage()
{
return static_cast<Value *>( pagesAllocator_.allocate() );
}
virtual void releaseArrayPage( Value *value )
{
if ( value )
pagesAllocator_.release( value );
}
private:
BatchAllocator<ValueInternalArray,1> arraysAllocator_;
BatchAllocator<Value,ValueInternalArray::itemsPerPage> pagesAllocator_;
};
#endif // #ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR
static ValueArrayAllocator *&arrayAllocator()
{
static DefaultValueArrayAllocator defaultAllocator;
static ValueArrayAllocator *arrayAllocator = &defaultAllocator;
return arrayAllocator;
}
static struct DummyArrayAllocatorInitializer {
DummyArrayAllocatorInitializer()
{
arrayAllocator(); // ensure arrayAllocator() statics are initialized before main().
}
} dummyArrayAllocatorInitializer;
// //////////////////////////////////////////////////////////////////
// class ValueInternalArray
// //////////////////////////////////////////////////////////////////
bool
ValueInternalArray::equals( const IteratorState &x,
const IteratorState &other )
{
return x.array_ == other.array_
&& x.currentItemIndex_ == other.currentItemIndex_
&& x.currentPageIndex_ == other.currentPageIndex_;
}
void
ValueInternalArray::increment( IteratorState &it )
{
JSON_ASSERT_MESSAGE( it.array_ &&
(it.currentPageIndex_ - it.array_->pages_)*itemsPerPage + it.currentItemIndex_
!= it.array_->size_,
"ValueInternalArray::increment(): moving iterator beyond end" );
++(it.currentItemIndex_);
if ( it.currentItemIndex_ == itemsPerPage )
{
it.currentItemIndex_ = 0;
++(it.currentPageIndex_);
}
}
void
ValueInternalArray::decrement( IteratorState &it )
{
JSON_ASSERT_MESSAGE( it.array_ && it.currentPageIndex_ == it.array_->pages_
&& it.currentItemIndex_ == 0,
"ValueInternalArray::decrement(): moving iterator beyond end" );
if ( it.currentItemIndex_ == 0 )
{
it.currentItemIndex_ = itemsPerPage-1;
--(it.currentPageIndex_);
}
else
{
--(it.currentItemIndex_);
}
}
Value &
ValueInternalArray::unsafeDereference( const IteratorState &it )
{
return (*(it.currentPageIndex_))[it.currentItemIndex_];
}
Value &
ValueInternalArray::dereference( const IteratorState &it )
{
JSON_ASSERT_MESSAGE( it.array_ &&
(it.currentPageIndex_ - it.array_->pages_)*itemsPerPage + it.currentItemIndex_
< it.array_->size_,
"ValueInternalArray::dereference(): dereferencing invalid iterator" );
return unsafeDereference( it );
}
void
ValueInternalArray::makeBeginIterator( IteratorState &it ) const
{
it.array_ = const_cast<ValueInternalArray *>( this );
it.currentItemIndex_ = 0;
it.currentPageIndex_ = pages_;
}
void
ValueInternalArray::makeIterator( IteratorState &it, ArrayIndex index ) const
{
it.array_ = const_cast<ValueInternalArray *>( this );
it.currentItemIndex_ = index % itemsPerPage;
it.currentPageIndex_ = pages_ + index / itemsPerPage;
}
void
ValueInternalArray::makeEndIterator( IteratorState &it ) const
{
makeIterator( it, size_ );
}
ValueInternalArray::ValueInternalArray()
: pages_( 0 )
, size_( 0 )
, pageCount_( 0 )
{
}
ValueInternalArray::ValueInternalArray( const ValueInternalArray &other )
: pages_( 0 )
, pageCount_( 0 )
, size_( other.size_ )
{
PageIndex minNewPages = other.size_ / itemsPerPage;
arrayAllocator()->reallocateArrayPageIndex( pages_, pageCount_, minNewPages );
JSON_ASSERT_MESSAGE( pageCount_ >= minNewPages,
"ValueInternalArray::reserve(): bad reallocation" );
IteratorState itOther;
other.makeBeginIterator( itOther );
Value *value;
for ( ArrayIndex index = 0; index < size_; ++index, increment(itOther) )
{
if ( index % itemsPerPage == 0 )
{
PageIndex pageIndex = index / itemsPerPage;
value = arrayAllocator()->allocateArrayPage();
pages_[pageIndex] = value;
}
new (value) Value( dereference( itOther ) );
}
}
ValueInternalArray &
ValueInternalArray::operator =( const ValueInternalArray &other )
{
ValueInternalArray temp( other );
swap( temp );
return *this;
}
ValueInternalArray::~ValueInternalArray()
{
// destroy all constructed items
IteratorState it;
IteratorState itEnd;
makeBeginIterator( it);
makeEndIterator( itEnd );
for ( ; !equals(it,itEnd); increment(it) )
{
Value *value = &dereference(it);
value->~Value();
}
// release all pages
PageIndex lastPageIndex = size_ / itemsPerPage;
for ( PageIndex pageIndex = 0; pageIndex < lastPageIndex; ++pageIndex )
arrayAllocator()->releaseArrayPage( pages_[pageIndex] );
// release pages index
arrayAllocator()->releaseArrayPageIndex( pages_, pageCount_ );
}
void
ValueInternalArray::swap( ValueInternalArray &other )
{
Value **tempPages = pages_;
pages_ = other.pages_;
other.pages_ = tempPages;
ArrayIndex tempSize = size_;
size_ = other.size_;
other.size_ = tempSize;
PageIndex tempPageCount = pageCount_;
pageCount_ = other.pageCount_;
other.pageCount_ = tempPageCount;
}
void
ValueInternalArray::clear()
{
ValueInternalArray dummy;
swap( dummy );
}
void
ValueInternalArray::resize( ArrayIndex newSize )
{
if ( newSize == 0 )
clear();
else if ( newSize < size_ )
{
IteratorState it;
IteratorState itEnd;
makeIterator( it, newSize );
makeIterator( itEnd, size_ );
for ( ; !equals(it,itEnd); increment(it) )
{
Value *value = &dereference(it);
value->~Value();
}
PageIndex pageIndex = (newSize + itemsPerPage - 1) / itemsPerPage;
PageIndex lastPageIndex = size_ / itemsPerPage;
for ( ; pageIndex < lastPageIndex; ++pageIndex )
arrayAllocator()->releaseArrayPage( pages_[pageIndex] );
size_ = newSize;
}
else if ( newSize > size_ )
resolveReference( newSize );
}
void
ValueInternalArray::makeIndexValid( ArrayIndex index )
{
// Need to enlarge page index ?
if ( index >= pageCount_ * itemsPerPage )
{
PageIndex minNewPages = (index + 1) / itemsPerPage;
arrayAllocator()->reallocateArrayPageIndex( pages_, pageCount_, minNewPages );
JSON_ASSERT_MESSAGE( pageCount_ >= minNewPages, "ValueInternalArray::reserve(): bad reallocation" );
}
// Need to allocate new pages ?
ArrayIndex nextPageIndex =
(size_ % itemsPerPage) != 0 ? size_ - (size_%itemsPerPage) + itemsPerPage
: size_;
if ( nextPageIndex <= index )
{
PageIndex pageIndex = nextPageIndex / itemsPerPage;
PageIndex pageToAllocate = (index - nextPageIndex) / itemsPerPage + 1;
for ( ; pageToAllocate-- > 0; ++pageIndex )
pages_[pageIndex] = arrayAllocator()->allocateArrayPage();
}
// Initialize all new entries
IteratorState it;
IteratorState itEnd;
makeIterator( it, size_ );
size_ = index + 1;
makeIterator( itEnd, size_ );
for ( ; !equals(it,itEnd); increment(it) )
{
Value *value = &dereference(it);
new (value) Value(); // Construct a default value using placement new
}
}
Value &
ValueInternalArray::resolveReference( ArrayIndex index )
{
if ( index >= size_ )
makeIndexValid( index );
return pages_[index/itemsPerPage][index%itemsPerPage];
}
Value *
ValueInternalArray::find( ArrayIndex index ) const
{
if ( index >= size_ )
return 0;
return &(pages_[index/itemsPerPage][index%itemsPerPage]);
}
ValueInternalArray::ArrayIndex
ValueInternalArray::size() const
{
return size_;
}
int
ValueInternalArray::distance( const IteratorState &x, const IteratorState &y )
{
return indexOf(y) - indexOf(x);
}
ValueInternalArray::ArrayIndex
ValueInternalArray::indexOf( const IteratorState &iterator )
{
if ( !iterator.array_ )
return ArrayIndex(-1);
return ArrayIndex(
(iterator.currentPageIndex_ - iterator.array_->pages_) * itemsPerPage
+ iterator.currentItemIndex_ );
}
int
ValueInternalArray::compare( const ValueInternalArray &other ) const
{
int sizeDiff( size_ - other.size_ );
if ( sizeDiff != 0 )
return sizeDiff;
for ( ArrayIndex index =0; index < size_; ++index )
{
int diff = pages_[index/itemsPerPage][index%itemsPerPage].compare(
other.pages_[index/itemsPerPage][index%itemsPerPage] );
if ( diff != 0 )
return diff;
}
return 0;
}

View File

@ -1,607 +0,0 @@
// included by json_value.cpp
// everything is within Json namespace
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// class ValueInternalMap
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
/** \internal MUST be safely initialized using memset( this, 0, sizeof(ValueInternalLink) );
* This optimization is used by the fast allocator.
*/
ValueInternalLink::ValueInternalLink()
: previous_( 0 )
, next_( 0 )
{
}
ValueInternalLink::~ValueInternalLink()
{
for ( int index =0; index < itemPerLink; ++index )
{
if ( !items_[index].isItemAvailable() )
{
if ( !items_[index].isMemberNameStatic() )
free( keys_[index] );
}
else
break;
}
}
ValueMapAllocator::~ValueMapAllocator()
{
}
#ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR
class DefaultValueMapAllocator : public ValueMapAllocator
{
public: // overridden from ValueMapAllocator
virtual ValueInternalMap *newMap()
{
return new ValueInternalMap();
}
virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other )
{
return new ValueInternalMap( other );
}
virtual void destructMap( ValueInternalMap *map )
{
delete map;
}
virtual ValueInternalLink *allocateMapBuckets( unsigned int size )
{
return new ValueInternalLink[size];
}
virtual void releaseMapBuckets( ValueInternalLink *links )
{
delete [] links;
}
virtual ValueInternalLink *allocateMapLink()
{
return new ValueInternalLink();
}
virtual void releaseMapLink( ValueInternalLink *link )
{
delete link;
}
};
#else
/// @todo make this thread-safe (lock when accessign batch allocator)
class DefaultValueMapAllocator : public ValueMapAllocator
{
public: // overridden from ValueMapAllocator
virtual ValueInternalMap *newMap()
{
ValueInternalMap *map = mapsAllocator_.allocate();
new (map) ValueInternalMap(); // placement new
return map;
}
virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other )
{
ValueInternalMap *map = mapsAllocator_.allocate();
new (map) ValueInternalMap( other ); // placement new
return map;
}
virtual void destructMap( ValueInternalMap *map )
{
if ( map )
{
map->~ValueInternalMap();
mapsAllocator_.release( map );
}
}
virtual ValueInternalLink *allocateMapBuckets( unsigned int size )
{
return new ValueInternalLink[size];
}
virtual void releaseMapBuckets( ValueInternalLink *links )
{
delete [] links;
}
virtual ValueInternalLink *allocateMapLink()
{
ValueInternalLink *link = linksAllocator_.allocate();
memset( link, 0, sizeof(ValueInternalLink) );
return link;
}
virtual void releaseMapLink( ValueInternalLink *link )
{
link->~ValueInternalLink();
linksAllocator_.release( link );
}
private:
BatchAllocator<ValueInternalMap,1> mapsAllocator_;
BatchAllocator<ValueInternalLink,1> linksAllocator_;
};
#endif
static ValueMapAllocator *&mapAllocator()
{
static DefaultValueMapAllocator defaultAllocator;
static ValueMapAllocator *mapAllocator = &defaultAllocator;
return mapAllocator;
}
static struct DummyMapAllocatorInitializer {
DummyMapAllocatorInitializer()
{
mapAllocator(); // ensure mapAllocator() statics are initialized before main().
}
} dummyMapAllocatorInitializer;
// h(K) = value * K >> w ; with w = 32 & K prime w.r.t. 2^32.
/*
use linked list hash map.
buckets array is a container.
linked list element contains 6 key/values. (memory = (16+4) * 6 + 4 = 124)
value have extra state: valid, available, deleted
*/
ValueInternalMap::ValueInternalMap()
: buckets_( 0 )
, tailLink_( 0 )
, bucketsSize_( 0 )
, itemCount_( 0 )
{
}
ValueInternalMap::ValueInternalMap( const ValueInternalMap &other )
: buckets_( 0 )
, tailLink_( 0 )
, bucketsSize_( 0 )
, itemCount_( 0 )
{
reserve( other.itemCount_ );
IteratorState it;
IteratorState itEnd;
other.makeBeginIterator( it );
other.makeEndIterator( itEnd );
for ( ; !equals(it,itEnd); increment(it) )
{
bool isStatic;
const char *memberName = key( it, isStatic );
const Value &aValue = value( it );
resolveReference(memberName, isStatic) = aValue;
}
}
ValueInternalMap &
ValueInternalMap::operator =( const ValueInternalMap &other )
{
ValueInternalMap dummy( other );
swap( dummy );
return *this;
}
ValueInternalMap::~ValueInternalMap()
{
if ( buckets_ )
{
for ( BucketIndex bucketIndex =0; bucketIndex < bucketsSize_; ++bucketIndex )
{
ValueInternalLink *link = buckets_[bucketIndex].next_;
while ( link )
{
ValueInternalLink *linkToRelease = link;
link = link->next_;
mapAllocator()->releaseMapLink( linkToRelease );
}
}
mapAllocator()->releaseMapBuckets( buckets_ );
}
}
void
ValueInternalMap::swap( ValueInternalMap &other )
{
ValueInternalLink *tempBuckets = buckets_;
buckets_ = other.buckets_;
other.buckets_ = tempBuckets;
ValueInternalLink *tempTailLink = tailLink_;
tailLink_ = other.tailLink_;
other.tailLink_ = tempTailLink;
BucketIndex tempBucketsSize = bucketsSize_;
bucketsSize_ = other.bucketsSize_;
other.bucketsSize_ = tempBucketsSize;
BucketIndex tempItemCount = itemCount_;
itemCount_ = other.itemCount_;
other.itemCount_ = tempItemCount;
}
void
ValueInternalMap::clear()
{
ValueInternalMap dummy;
swap( dummy );
}
ValueInternalMap::BucketIndex
ValueInternalMap::size() const
{
return itemCount_;
}
bool
ValueInternalMap::reserveDelta( BucketIndex growth )
{
return reserve( itemCount_ + growth );
}
bool
ValueInternalMap::reserve( BucketIndex newItemCount )
{
if ( !buckets_ && newItemCount > 0 )
{
buckets_ = mapAllocator()->allocateMapBuckets( 1 );
bucketsSize_ = 1;
tailLink_ = &buckets_[0];
}
// BucketIndex idealBucketCount = (newItemCount + ValueInternalLink::itemPerLink) / ValueInternalLink::itemPerLink;
return true;
}
const Value *
ValueInternalMap::find( const char *key ) const
{
if ( !bucketsSize_ )
return 0;
HashKey hashedKey = hash( key );
BucketIndex bucketIndex = hashedKey % bucketsSize_;
for ( const ValueInternalLink *current = &buckets_[bucketIndex];
current != 0;
current = current->next_ )
{
for ( BucketIndex index=0; index < ValueInternalLink::itemPerLink; ++index )
{
if ( current->items_[index].isItemAvailable() )
return 0;
if ( strcmp( key, current->keys_[index] ) == 0 )
return &current->items_[index];
}
}
return 0;
}
Value *
ValueInternalMap::find( const char *key )
{
const ValueInternalMap *constThis = this;
return const_cast<Value *>( constThis->find( key ) );
}
Value &
ValueInternalMap::resolveReference( const char *key,
bool isStatic )
{
HashKey hashedKey = hash( key );
if ( bucketsSize_ )
{
BucketIndex bucketIndex = hashedKey % bucketsSize_;
ValueInternalLink **previous = 0;
BucketIndex index;
for ( ValueInternalLink *current = &buckets_[bucketIndex];
current != 0;
previous = &current->next_, current = current->next_ )
{
for ( index=0; index < ValueInternalLink::itemPerLink; ++index )
{
if ( current->items_[index].isItemAvailable() )
return setNewItem( key, isStatic, current, index );
if ( strcmp( key, current->keys_[index] ) == 0 )
return current->items_[index];
}
}
}
reserveDelta( 1 );
return unsafeAdd( key, isStatic, hashedKey );
}
void
ValueInternalMap::remove( const char *key )
{
HashKey hashedKey = hash( key );
if ( !bucketsSize_ )
return;
BucketIndex bucketIndex = hashedKey % bucketsSize_;
for ( ValueInternalLink *link = &buckets_[bucketIndex];
link != 0;
link = link->next_ )
{
BucketIndex index;
for ( index =0; index < ValueInternalLink::itemPerLink; ++index )
{
if ( link->items_[index].isItemAvailable() )
return;
if ( strcmp( key, link->keys_[index] ) == 0 )
{
doActualRemove( link, index, bucketIndex );
return;
}
}
}
}
void
ValueInternalMap::doActualRemove( ValueInternalLink *link,
BucketIndex index,
BucketIndex bucketIndex )
{
// find last item of the bucket and swap it with the 'removed' one.
// set removed items flags to 'available'.
// if last page only contains 'available' items, then desallocate it (it's empty)
ValueInternalLink *&lastLink = getLastLinkInBucket( index );
BucketIndex lastItemIndex = 1; // a link can never be empty, so start at 1
for ( ;
lastItemIndex < ValueInternalLink::itemPerLink;
++lastItemIndex ) // may be optimized with dicotomic search
{
if ( lastLink->items_[lastItemIndex].isItemAvailable() )
break;
}
BucketIndex lastUsedIndex = lastItemIndex - 1;
Value *valueToDelete = &link->items_[index];
Value *valueToPreserve = &lastLink->items_[lastUsedIndex];
if ( valueToDelete != valueToPreserve )
valueToDelete->swap( *valueToPreserve );
if ( lastUsedIndex == 0 ) // page is now empty
{ // remove it from bucket linked list and delete it.
ValueInternalLink *linkPreviousToLast = lastLink->previous_;
if ( linkPreviousToLast != 0 ) // can not deleted bucket link.
{
mapAllocator()->releaseMapLink( lastLink );
linkPreviousToLast->next_ = 0;
lastLink = linkPreviousToLast;
}
}
else
{
Value dummy;
valueToPreserve->swap( dummy ); // restore deleted to default Value.
valueToPreserve->setItemUsed( false );
}
--itemCount_;
}
ValueInternalLink *&
ValueInternalMap::getLastLinkInBucket( BucketIndex bucketIndex )
{
if ( bucketIndex == bucketsSize_ - 1 )
return tailLink_;
ValueInternalLink *&previous = buckets_[bucketIndex+1].previous_;
if ( !previous )
previous = &buckets_[bucketIndex];
return previous;
}
Value &
ValueInternalMap::setNewItem( const char *key,
bool isStatic,
ValueInternalLink *link,
BucketIndex index )
{
char *duplicatedKey = valueAllocator()->makeMemberName( key );
++itemCount_;
link->keys_[index] = duplicatedKey;
link->items_[index].setItemUsed();
link->items_[index].setMemberNameIsStatic( isStatic );
return link->items_[index]; // items already default constructed.
}
Value &
ValueInternalMap::unsafeAdd( const char *key,
bool isStatic,
HashKey hashedKey )
{
JSON_ASSERT_MESSAGE( bucketsSize_ > 0, "ValueInternalMap::unsafeAdd(): internal logic error." );
BucketIndex bucketIndex = hashedKey % bucketsSize_;
ValueInternalLink *&previousLink = getLastLinkInBucket( bucketIndex );
ValueInternalLink *link = previousLink;
BucketIndex index;
for ( index =0; index < ValueInternalLink::itemPerLink; ++index )
{
if ( link->items_[index].isItemAvailable() )
break;
}
if ( index == ValueInternalLink::itemPerLink ) // need to add a new page
{
ValueInternalLink *newLink = mapAllocator()->allocateMapLink();
index = 0;
link->next_ = newLink;
previousLink = newLink;
link = newLink;
}
return setNewItem( key, isStatic, link, index );
}
ValueInternalMap::HashKey
ValueInternalMap::hash( const char *key ) const
{
HashKey hash = 0;
while ( *key )
hash += *key++ * 37;
return hash;
}
int
ValueInternalMap::compare( const ValueInternalMap &other ) const
{
int sizeDiff( itemCount_ - other.itemCount_ );
if ( sizeDiff != 0 )
return sizeDiff;
// Strict order guaranty is required. Compare all keys FIRST, then compare values.
IteratorState it;
IteratorState itEnd;
makeBeginIterator( it );
makeEndIterator( itEnd );
for ( ; !equals(it,itEnd); increment(it) )
{
if ( !other.find( key( it ) ) )
return 1;
}
// All keys are equals, let's compare values
makeBeginIterator( it );
for ( ; !equals(it,itEnd); increment(it) )
{
const Value *otherValue = other.find( key( it ) );
int valueDiff = value(it).compare( *otherValue );
if ( valueDiff != 0 )
return valueDiff;
}
return 0;
}
void
ValueInternalMap::makeBeginIterator( IteratorState &it ) const
{
it.map_ = const_cast<ValueInternalMap *>( this );
it.bucketIndex_ = 0;
it.itemIndex_ = 0;
it.link_ = buckets_;
}
void
ValueInternalMap::makeEndIterator( IteratorState &it ) const
{
it.map_ = const_cast<ValueInternalMap *>( this );
it.bucketIndex_ = bucketsSize_;
it.itemIndex_ = 0;
it.link_ = 0;
}
bool
ValueInternalMap::equals( const IteratorState &x, const IteratorState &other )
{
return x.map_ == other.map_
&& x.bucketIndex_ == other.bucketIndex_
&& x.link_ == other.link_
&& x.itemIndex_ == other.itemIndex_;
}
void
ValueInternalMap::incrementBucket( IteratorState &iterator )
{
++iterator.bucketIndex_;
JSON_ASSERT_MESSAGE( iterator.bucketIndex_ <= iterator.map_->bucketsSize_,
"ValueInternalMap::increment(): attempting to iterate beyond end." );
if ( iterator.bucketIndex_ == iterator.map_->bucketsSize_ )
iterator.link_ = 0;
else
iterator.link_ = &(iterator.map_->buckets_[iterator.bucketIndex_]);
iterator.itemIndex_ = 0;
}
void
ValueInternalMap::increment( IteratorState &iterator )
{
JSON_ASSERT_MESSAGE( iterator.map_, "Attempting to iterator using invalid iterator." );
++iterator.itemIndex_;
if ( iterator.itemIndex_ == ValueInternalLink::itemPerLink )
{
JSON_ASSERT_MESSAGE( iterator.link_ != 0,
"ValueInternalMap::increment(): attempting to iterate beyond end." );
iterator.link_ = iterator.link_->next_;
if ( iterator.link_ == 0 )
incrementBucket( iterator );
}
else if ( iterator.link_->items_[iterator.itemIndex_].isItemAvailable() )
{
incrementBucket( iterator );
}
}
void
ValueInternalMap::decrement( IteratorState &iterator )
{
if ( iterator.itemIndex_ == 0 )
{
JSON_ASSERT_MESSAGE( iterator.map_, "Attempting to iterate using invalid iterator." );
if ( iterator.link_ == &iterator.map_->buckets_[iterator.bucketIndex_] )
{
JSON_ASSERT_MESSAGE( iterator.bucketIndex_ > 0, "Attempting to iterate beyond beginning." );
--(iterator.bucketIndex_);
}
iterator.link_ = iterator.link_->previous_;
iterator.itemIndex_ = ValueInternalLink::itemPerLink - 1;
}
}
const char *
ValueInternalMap::key( const IteratorState &iterator )
{
JSON_ASSERT_MESSAGE( iterator.link_, "Attempting to iterate using invalid iterator." );
return iterator.link_->keys_[iterator.itemIndex_];
}
const char *
ValueInternalMap::key( const IteratorState &iterator, bool &isStatic )
{
JSON_ASSERT_MESSAGE( iterator.link_, "Attempting to iterate using invalid iterator." );
isStatic = iterator.link_->items_[iterator.itemIndex_].isMemberNameStatic();
return iterator.link_->keys_[iterator.itemIndex_];
}
Value &
ValueInternalMap::value( const IteratorState &iterator )
{
JSON_ASSERT_MESSAGE( iterator.link_, "Attempting to iterate using invalid iterator." );
return iterator.link_->items_[iterator.itemIndex_];
}
int
ValueInternalMap::distance( const IteratorState &x, const IteratorState &y )
{
int offset = 0;
IteratorState it = x;
while ( !equals( it, y ) )
increment( it );
return offset;
}

View File

@ -1,885 +0,0 @@
#include <json/reader.h>
#include <json/value.h>
#include <utility>
#include <cstdio>
#include <cassert>
#include <cstring>
#include <iostream>
#include <stdexcept>
#if _MSC_VER >= 1400 // VC++ 8.0
#pragma warning( disable : 4996 ) // disable warning about strdup being deprecated.
#endif
namespace Json {
// Implementation of class Features
// ////////////////////////////////
Features::Features()
: allowComments_( true )
, strictRoot_( false )
{
}
Features
Features::all()
{
return Features();
}
Features
Features::strictMode()
{
Features features;
features.allowComments_ = false;
features.strictRoot_ = true;
return features;
}
// Implementation of class Reader
// ////////////////////////////////
static inline bool
in( Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::Char c4 )
{
return c == c1 || c == c2 || c == c3 || c == c4;
}
static inline bool
in( Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::Char c4, Reader::Char c5 )
{
return c == c1 || c == c2 || c == c3 || c == c4 || c == c5;
}
static bool
containsNewLine( Reader::Location begin,
Reader::Location end )
{
for ( ;begin < end; ++begin )
if ( *begin == '\n' || *begin == '\r' )
return true;
return false;
}
static std::string codePointToUTF8(unsigned int cp)
{
std::string result;
// based on description from http://en.wikipedia.org/wiki/UTF-8
if (cp <= 0x7f)
{
result.resize(1);
result[0] = static_cast<char>(cp);
}
else if (cp <= 0x7FF)
{
result.resize(2);
result[1] = static_cast<char>(0x80 | (0x3f & cp));
result[0] = static_cast<char>(0xC0 | (0x1f & (cp >> 6)));
}
else if (cp <= 0xFFFF)
{
result.resize(3);
result[2] = static_cast<char>(0x80 | (0x3f & cp));
result[1] = 0x80 | static_cast<char>((0x3f & (cp >> 6)));
result[0] = 0xE0 | static_cast<char>((0xf & (cp >> 12)));
}
else if (cp <= 0x10FFFF)
{
result.resize(4);
result[3] = static_cast<char>(0x80 | (0x3f & cp));
result[2] = static_cast<char>(0x80 | (0x3f & (cp >> 6)));
result[1] = static_cast<char>(0x80 | (0x3f & (cp >> 12)));
result[0] = static_cast<char>(0xF0 | (0x7 & (cp >> 18)));
}
return result;
}
// Class Reader
// //////////////////////////////////////////////////////////////////
Reader::Reader()
: features_( Features::all() )
{
}
Reader::Reader( const Features &features )
: features_( features )
{
}
bool
Reader::parse( const std::string &document,
Value &root,
bool collectComments )
{
document_ = document;
const char *begin = document_.c_str();
const char *end = begin + document_.length();
return parse( begin, end, root, collectComments );
}
bool
Reader::parse( std::istream& sin,
Value &root,
bool collectComments )
{
//std::istream_iterator<char> begin(sin);
//std::istream_iterator<char> end;
// Those would allow streamed input from a file, if parse() were a
// template function.
// Since std::string is reference-counted, this at least does not
// create an extra copy.
std::string doc;
std::getline(sin, doc, (char)EOF);
return parse( doc, root, collectComments );
}
bool
Reader::parse( const char *beginDoc, const char *endDoc,
Value &root,
bool collectComments )
{
if ( !features_.allowComments_ )
{
collectComments = false;
}
begin_ = beginDoc;
end_ = endDoc;
collectComments_ = collectComments;
current_ = begin_;
lastValueEnd_ = 0;
lastValue_ = 0;
commentsBefore_ = "";
errors_.clear();
while ( !nodes_.empty() )
nodes_.pop();
nodes_.push( &root );
bool successful = readValue();
Token token;
skipCommentTokens( token );
if ( collectComments_ && !commentsBefore_.empty() )
root.setComment( commentsBefore_, commentAfter );
if ( features_.strictRoot_ )
{
if ( !root.isArray() && !root.isObject() )
{
// Set error location to start of doc, ideally should be first token found in doc
token.type_ = tokenError;
token.start_ = beginDoc;
token.end_ = endDoc;
addError( "A valid JSON document must be either an array or an object value.",
token );
return false;
}
}
return successful;
}
bool
Reader::readValue()
{
Token token;
skipCommentTokens( token );
bool successful = true;
if ( collectComments_ && !commentsBefore_.empty() )
{
currentValue().setComment( commentsBefore_, commentBefore );
commentsBefore_ = "";
}
switch ( token.type_ )
{
case tokenObjectBegin:
successful = readObject( token );
break;
case tokenArrayBegin:
successful = readArray( token );
break;
case tokenNumber:
successful = decodeNumber( token );
break;
case tokenString:
successful = decodeString( token );
break;
case tokenTrue:
currentValue() = true;
break;
case tokenFalse:
currentValue() = false;
break;
case tokenNull:
currentValue() = Value();
break;
default:
return addError( "Syntax error: value, object or array expected.", token );
}
if ( collectComments_ )
{
lastValueEnd_ = current_;
lastValue_ = &currentValue();
}
return successful;
}
void
Reader::skipCommentTokens( Token &token )
{
if ( features_.allowComments_ )
{
do
{
readToken( token );
}
while ( token.type_ == tokenComment );
}
else
{
readToken( token );
}
}
bool
Reader::expectToken( TokenType type, Token &token, const char *message )
{
readToken( token );
if ( token.type_ != type )
return addError( message, token );
return true;
}
bool
Reader::readToken( Token &token )
{
skipSpaces();
token.start_ = current_;
Char c = getNextChar();
bool ok = true;
switch ( c )
{
case '{':
token.type_ = tokenObjectBegin;
break;
case '}':
token.type_ = tokenObjectEnd;
break;
case '[':
token.type_ = tokenArrayBegin;
break;
case ']':
token.type_ = tokenArrayEnd;
break;
case '"':
token.type_ = tokenString;
ok = readString();
break;
case '/':
token.type_ = tokenComment;
ok = readComment();
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '-':
token.type_ = tokenNumber;
readNumber();
break;
case 't':
token.type_ = tokenTrue;
ok = match( "rue", 3 );
break;
case 'f':
token.type_ = tokenFalse;
ok = match( "alse", 4 );
break;
case 'n':
token.type_ = tokenNull;
ok = match( "ull", 3 );
break;
case ',':
token.type_ = tokenArraySeparator;
break;
case ':':
token.type_ = tokenMemberSeparator;
break;
case 0:
token.type_ = tokenEndOfStream;
break;
default:
ok = false;
break;
}
if ( !ok )
token.type_ = tokenError;
token.end_ = current_;
return true;
}
void
Reader::skipSpaces()
{
while ( current_ != end_ )
{
Char c = *current_;
if ( c == ' ' || c == '\t' || c == '\r' || c == '\n' )
++current_;
else
break;
}
}
bool
Reader::match( Location pattern,
int patternLength )
{
if ( end_ - current_ < patternLength )
return false;
int index = patternLength;
while ( index-- )
if ( current_[index] != pattern[index] )
return false;
current_ += patternLength;
return true;
}
bool
Reader::readComment()
{
Location commentBegin = current_ - 1;
Char c = getNextChar();
bool successful = false;
if ( c == '*' )
successful = readCStyleComment();
else if ( c == '/' )
successful = readCppStyleComment();
if ( !successful )
return false;
if ( collectComments_ )
{
CommentPlacement placement = commentBefore;
if ( lastValueEnd_ && !containsNewLine( lastValueEnd_, commentBegin ) )
{
if ( c != '*' || !containsNewLine( commentBegin, current_ ) )
placement = commentAfterOnSameLine;
}
addComment( commentBegin, current_, placement );
}
return true;
}
void
Reader::addComment( Location begin,
Location end,
CommentPlacement placement )
{
assert( collectComments_ );
if ( placement == commentAfterOnSameLine )
{
assert( lastValue_ != 0 );
lastValue_->setComment( std::string( begin, end ), placement );
}
else
{
if ( !commentsBefore_.empty() )
commentsBefore_ += "\n";
commentsBefore_ += std::string( begin, end );
}
}
bool
Reader::readCStyleComment()
{
while ( current_ != end_ )
{
Char c = getNextChar();
if ( c == '*' && *current_ == '/' )
break;
}
return getNextChar() == '/';
}
bool
Reader::readCppStyleComment()
{
while ( current_ != end_ )
{
Char c = getNextChar();
if ( c == '\r' || c == '\n' )
break;
}
return true;
}
void
Reader::readNumber()
{
while ( current_ != end_ )
{
if ( !(*current_ >= '0' && *current_ <= '9') &&
!in( *current_, '.', 'e', 'E', '+', '-' ) )
break;
++current_;
}
}
bool
Reader::readString()
{
Char c = 0;
while ( current_ != end_ )
{
c = getNextChar();
if ( c == '\\' )
getNextChar();
else if ( c == '"' )
break;
}
return c == '"';
}
bool
Reader::readObject( Token &tokenStart )
{
Token tokenName;
std::string name;
currentValue() = Value( objectValue );
while ( readToken( tokenName ) )
{
bool initialTokenOk = true;
while ( tokenName.type_ == tokenComment && initialTokenOk )
initialTokenOk = readToken( tokenName );
if ( !initialTokenOk )
break;
if ( tokenName.type_ == tokenObjectEnd && name.empty() ) // empty object
return true;
if ( tokenName.type_ != tokenString )
break;
name = "";
if ( !decodeString( tokenName, name ) )
return recoverFromError( tokenObjectEnd );
Token colon;
if ( !readToken( colon ) || colon.type_ != tokenMemberSeparator )
{
return addErrorAndRecover( "Missing ':' after object member name",
colon,
tokenObjectEnd );
}
Value &value = currentValue()[ name ];
nodes_.push( &value );
bool ok = readValue();
nodes_.pop();
if ( !ok ) // error already set
return recoverFromError( tokenObjectEnd );
Token comma;
if ( !readToken( comma )
|| ( comma.type_ != tokenObjectEnd &&
comma.type_ != tokenArraySeparator &&
comma.type_ != tokenComment ) )
{
return addErrorAndRecover( "Missing ',' or '}' in object declaration",
comma,
tokenObjectEnd );
}
bool finalizeTokenOk = true;
while ( comma.type_ == tokenComment &&
finalizeTokenOk )
finalizeTokenOk = readToken( comma );
if ( comma.type_ == tokenObjectEnd )
return true;
}
return addErrorAndRecover( "Missing '}' or object member name",
tokenName,
tokenObjectEnd );
}
bool
Reader::readArray( Token &tokenStart )
{
currentValue() = Value( arrayValue );
skipSpaces();
if ( *current_ == ']' ) // empty array
{
Token endArray;
readToken( endArray );
return true;
}
int index = 0;
while ( true )
{
Value &value = currentValue()[ index++ ];
nodes_.push( &value );
bool ok = readValue();
nodes_.pop();
if ( !ok ) // error already set
return recoverFromError( tokenArrayEnd );
Token token;
// Accept Comment after last item in the array.
ok = readToken( token );
while ( token.type_ == tokenComment && ok )
{
ok = readToken( token );
}
bool badTokenType = ( token.type_ == tokenArraySeparator &&
token.type_ == tokenArrayEnd );
if ( !ok || badTokenType )
{
return addErrorAndRecover( "Missing ',' or ']' in array declaration",
token,
tokenArrayEnd );
}
if ( token.type_ == tokenArrayEnd )
break;
}
return true;
}
bool
Reader::decodeNumber( Token &token )
{
bool isDouble = false;
for ( Location inspect = token.start_; inspect != token.end_; ++inspect )
{
isDouble = isDouble
|| in( *inspect, '.', 'e', 'E', '+' )
|| ( *inspect == '-' && inspect != token.start_ );
}
if ( isDouble )
return decodeDouble( token );
Location current = token.start_;
bool isNegative = *current == '-';
if ( isNegative )
++current;
Value::UInt threshold = (isNegative ? Value::UInt(-Value::minInt)
: Value::maxUInt) / 10;
Value::UInt value = 0;
while ( current < token.end_ )
{
Char c = *current++;
if ( c < '0' || c > '9' )
return addError( "'" + std::string( token.start_, token.end_ ) + "' is not a number.", token );
if ( value >= threshold )
return decodeDouble( token );
value = value * 10 + Value::UInt(c - '0');
}
if ( isNegative )
currentValue() = -Value::Int( value );
else if ( value <= Value::UInt(Value::maxInt) )
currentValue() = Value::Int( value );
else
currentValue() = value;
return true;
}
bool
Reader::decodeDouble( Token &token )
{
double value = 0;
const int bufferSize = 32;
int count;
int length = int(token.end_ - token.start_);
if ( length <= bufferSize )
{
Char buffer[bufferSize];
memcpy( buffer, token.start_, length );
buffer[length] = 0;
count = sscanf( buffer, "%lf", &value );
}
else
{
std::string buffer( token.start_, token.end_ );
count = sscanf( buffer.c_str(), "%lf", &value );
}
if ( count != 1 )
return addError( "'" + std::string( token.start_, token.end_ ) + "' is not a number.", token );
currentValue() = value;
return true;
}
bool
Reader::decodeString( Token &token )
{
std::string decoded;
if ( !decodeString( token, decoded ) )
return false;
currentValue() = decoded;
return true;
}
bool
Reader::decodeString( Token &token, std::string &decoded )
{
decoded.reserve( token.end_ - token.start_ - 2 );
Location current = token.start_ + 1; // skip '"'
Location end = token.end_ - 1; // do not include '"'
while ( current != end )
{
Char c = *current++;
if ( c == '"' )
break;
else if ( c == '\\' )
{
if ( current == end )
return addError( "Empty escape sequence in string", token, current );
Char escape = *current++;
switch ( escape )
{
case '"': decoded += '"'; break;
case '/': decoded += '/'; break;
case '\\': decoded += '\\'; break;
case 'b': decoded += '\b'; break;
case 'f': decoded += '\f'; break;
case 'n': decoded += '\n'; break;
case 'r': decoded += '\r'; break;
case 't': decoded += '\t'; break;
case 'u':
{
unsigned int unicode;
if ( !decodeUnicodeCodePoint( token, current, end, unicode ) )
return false;
decoded += codePointToUTF8(unicode);
}
break;
default:
return addError( "Bad escape sequence in string", token, current );
}
}
else
{
decoded += c;
}
}
return true;
}
bool
Reader::decodeUnicodeCodePoint( Token &token,
Location &current,
Location end,
unsigned int &unicode )
{
if ( !decodeUnicodeEscapeSequence( token, current, end, unicode ) )
return false;
if (unicode >= 0xD800 && unicode <= 0xDBFF)
{
// surrogate pairs
if (end - current < 6)
return addError( "additional six characters expected to parse unicode surrogate pair.", token, current );
unsigned int surrogatePair;
if (*(current++) == '\\' && *(current++)== 'u')
{
if (decodeUnicodeEscapeSequence( token, current, end, surrogatePair ))
{
unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
}
else
return false;
}
else
return addError( "expecting another \\u token to begin the second half of a unicode surrogate pair", token, current );
}
return true;
}
bool
Reader::decodeUnicodeEscapeSequence( Token &token,
Location &current,
Location end,
unsigned int &unicode )
{
if ( end - current < 4 )
return addError( "Bad unicode escape sequence in string: four digits expected.", token, current );
unicode = 0;
for ( int index =0; index < 4; ++index )
{
Char c = *current++;
unicode *= 16;
if ( c >= '0' && c <= '9' )
unicode += c - '0';
else if ( c >= 'a' && c <= 'f' )
unicode += c - 'a' + 10;
else if ( c >= 'A' && c <= 'F' )
unicode += c - 'A' + 10;
else
return addError( "Bad unicode escape sequence in string: hexadecimal digit expected.", token, current );
}
return true;
}
bool
Reader::addError( const std::string &message,
Token &token,
Location extra )
{
ErrorInfo info;
info.token_ = token;
info.message_ = message;
info.extra_ = extra;
errors_.push_back( info );
return false;
}
bool
Reader::recoverFromError( TokenType skipUntilToken )
{
int errorCount = int(errors_.size());
Token skip;
while ( true )
{
if ( !readToken(skip) )
errors_.resize( errorCount ); // discard errors caused by recovery
if ( skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream )
break;
}
errors_.resize( errorCount );
return false;
}
bool
Reader::addErrorAndRecover( const std::string &message,
Token &token,
TokenType skipUntilToken )
{
addError( message, token );
return recoverFromError( skipUntilToken );
}
Value &
Reader::currentValue()
{
return *(nodes_.top());
}
Reader::Char
Reader::getNextChar()
{
if ( current_ == end_ )
return 0;
return *current_++;
}
void
Reader::getLocationLineAndColumn( Location location,
int &line,
int &column ) const
{
Location current = begin_;
Location lastLineStart = current;
line = 0;
while ( current < location && current != end_ )
{
Char c = *current++;
if ( c == '\r' )
{
if ( *current == '\n' )
++current;
lastLineStart = current;
++line;
}
else if ( c == '\n' )
{
lastLineStart = current;
++line;
}
}
// column & line start at 1
column = int(location - lastLineStart) + 1;
++line;
}
std::string
Reader::getLocationLineAndColumn( Location location ) const
{
int line, column;
getLocationLineAndColumn( location, line, column );
char buffer[18+16+16+1];
sprintf( buffer, "Line %d, Column %d", line, column );
return buffer;
}
std::string
Reader::getFormatedErrorMessages() const
{
std::string formattedMessage;
for ( Errors::const_iterator itError = errors_.begin();
itError != errors_.end();
++itError )
{
const ErrorInfo &error = *itError;
formattedMessage += "* " + getLocationLineAndColumn( error.token_.start_ ) + "\n";
formattedMessage += " " + error.message_ + "\n";
if ( error.extra_ )
formattedMessage += "See " + getLocationLineAndColumn( error.extra_ ) + " for detail.\n";
}
return formattedMessage;
}
std::istream& operator>>( std::istream &sin, Value &root )
{
Json::Reader reader;
bool ok = reader.parse(sin, root, true);
//JSON_ASSERT( ok );
if (!ok) throw std::runtime_error(reader.getFormatedErrorMessages());
return sin;
}
} // namespace Json

File diff suppressed because it is too large Load Diff

View File

@ -1,292 +0,0 @@
// included by json_value.cpp
// everything is within Json namespace
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// class ValueIteratorBase
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
ValueIteratorBase::ValueIteratorBase()
#ifndef JSON_VALUE_USE_INTERNAL_MAP
: current_()
, isNull_( true )
{
}
#else
: isArray_( true )
, isNull_( true )
{
iterator_.array_ = ValueInternalArray::IteratorState();
}
#endif
#ifndef JSON_VALUE_USE_INTERNAL_MAP
ValueIteratorBase::ValueIteratorBase( const Value::ObjectValues::iterator &current )
: current_( current )
, isNull_( false )
{
}
#else
ValueIteratorBase::ValueIteratorBase( const ValueInternalArray::IteratorState &state )
: isArray_( true )
{
iterator_.array_ = state;
}
ValueIteratorBase::ValueIteratorBase( const ValueInternalMap::IteratorState &state )
: isArray_( false )
{
iterator_.map_ = state;
}
#endif
Value &
ValueIteratorBase::deref() const
{
#ifndef JSON_VALUE_USE_INTERNAL_MAP
return current_->second;
#else
if ( isArray_ )
return ValueInternalArray::dereference( iterator_.array_ );
return ValueInternalMap::value( iterator_.map_ );
#endif
}
void
ValueIteratorBase::increment()
{
#ifndef JSON_VALUE_USE_INTERNAL_MAP
++current_;
#else
if ( isArray_ )
ValueInternalArray::increment( iterator_.array_ );
ValueInternalMap::increment( iterator_.map_ );
#endif
}
void
ValueIteratorBase::decrement()
{
#ifndef JSON_VALUE_USE_INTERNAL_MAP
--current_;
#else
if ( isArray_ )
ValueInternalArray::decrement( iterator_.array_ );
ValueInternalMap::decrement( iterator_.map_ );
#endif
}
ValueIteratorBase::difference_type
ValueIteratorBase::computeDistance( const SelfType &other ) const
{
#ifndef JSON_VALUE_USE_INTERNAL_MAP
# ifdef JSON_USE_CPPTL_SMALLMAP
return current_ - other.current_;
# else
// Iterator for null value are initialized using the default
// constructor, which initialize current_ to the default
// std::map::iterator. As begin() and end() are two instance
// of the default std::map::iterator, they can not be compared.
// To allow this, we handle this comparison specifically.
if ( isNull_ && other.isNull_ )
{
return 0;
}
// Usage of std::distance is not portable (does not compile with Sun Studio 12 RogueWave STL,
// which is the one used by default).
// Using a portable hand-made version for non random iterator instead:
// return difference_type( std::distance( current_, other.current_ ) );
difference_type myDistance = 0;
for ( Value::ObjectValues::iterator it = current_; it != other.current_; ++it )
{
++myDistance;
}
return myDistance;
# endif
#else
if ( isArray_ )
return ValueInternalArray::distance( iterator_.array_, other.iterator_.array_ );
return ValueInternalMap::distance( iterator_.map_, other.iterator_.map_ );
#endif
}
bool
ValueIteratorBase::isEqual( const SelfType &other ) const
{
#ifndef JSON_VALUE_USE_INTERNAL_MAP
if ( isNull_ )
{
return other.isNull_;
}
return current_ == other.current_;
#else
if ( isArray_ )
return ValueInternalArray::equals( iterator_.array_, other.iterator_.array_ );
return ValueInternalMap::equals( iterator_.map_, other.iterator_.map_ );
#endif
}
void
ValueIteratorBase::copy( const SelfType &other )
{
#ifndef JSON_VALUE_USE_INTERNAL_MAP
current_ = other.current_;
#else
if ( isArray_ )
iterator_.array_ = other.iterator_.array_;
iterator_.map_ = other.iterator_.map_;
#endif
}
Value
ValueIteratorBase::key() const
{
#ifndef JSON_VALUE_USE_INTERNAL_MAP
const Value::CZString czstring = (*current_).first;
if ( czstring.c_str() )
{
if ( czstring.isStaticString() )
return Value( StaticString( czstring.c_str() ) );
return Value( czstring.c_str() );
}
return Value( czstring.index() );
#else
if ( isArray_ )
return Value( ValueInternalArray::indexOf( iterator_.array_ ) );
bool isStatic;
const char *memberName = ValueInternalMap::key( iterator_.map_, isStatic );
if ( isStatic )
return Value( StaticString( memberName ) );
return Value( memberName );
#endif
}
UInt
ValueIteratorBase::index() const
{
#ifndef JSON_VALUE_USE_INTERNAL_MAP
const Value::CZString czstring = (*current_).first;
if ( !czstring.c_str() )
return czstring.index();
return Value::UInt( -1 );
#else
if ( isArray_ )
return Value::UInt( ValueInternalArray::indexOf( iterator_.array_ ) );
return Value::UInt( -1 );
#endif
}
const char *
ValueIteratorBase::memberName() const
{
#ifndef JSON_VALUE_USE_INTERNAL_MAP
const char *name = (*current_).first.c_str();
return name ? name : "";
#else
if ( !isArray_ )
return ValueInternalMap::key( iterator_.map_ );
return "";
#endif
}
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// class ValueConstIterator
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
ValueConstIterator::ValueConstIterator()
{
}
#ifndef JSON_VALUE_USE_INTERNAL_MAP
ValueConstIterator::ValueConstIterator( const Value::ObjectValues::iterator &current )
: ValueIteratorBase( current )
{
}
#else
ValueConstIterator::ValueConstIterator( const ValueInternalArray::IteratorState &state )
: ValueIteratorBase( state )
{
}
ValueConstIterator::ValueConstIterator( const ValueInternalMap::IteratorState &state )
: ValueIteratorBase( state )
{
}
#endif
ValueConstIterator &
ValueConstIterator::operator =( const ValueIteratorBase &other )
{
copy( other );
return *this;
}
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// class ValueIterator
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
ValueIterator::ValueIterator()
{
}
#ifndef JSON_VALUE_USE_INTERNAL_MAP
ValueIterator::ValueIterator( const Value::ObjectValues::iterator &current )
: ValueIteratorBase( current )
{
}
#else
ValueIterator::ValueIterator( const ValueInternalArray::IteratorState &state )
: ValueIteratorBase( state )
{
}
ValueIterator::ValueIterator( const ValueInternalMap::IteratorState &state )
: ValueIteratorBase( state )
{
}
#endif
ValueIterator::ValueIterator( const ValueConstIterator &other )
: ValueIteratorBase( other )
{
}
ValueIterator::ValueIterator( const ValueIterator &other )
: ValueIteratorBase( other )
{
}
ValueIterator &
ValueIterator::operator =( const SelfType &other )
{
copy( other );
return *this;
}

View File

@ -1,829 +0,0 @@
#include <json/writer.h>
#include <utility>
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <sstream>
#include <iomanip>
#if _MSC_VER >= 1400 // VC++ 8.0
#pragma warning( disable : 4996 ) // disable warning about strdup being deprecated.
#endif
namespace Json {
static bool isControlCharacter(char ch)
{
return ch > 0 && ch <= 0x1F;
}
static bool containsControlCharacter( const char* str )
{
while ( *str )
{
if ( isControlCharacter( *(str++) ) )
return true;
}
return false;
}
static void uintToString( unsigned int value,
char *&current )
{
*--current = 0;
do
{
*--current = (value % 10) + '0';
value /= 10;
}
while ( value != 0 );
}
std::string valueToString( Int value )
{
char buffer[32];
char *current = buffer + sizeof(buffer);
bool isNegative = value < 0;
if ( isNegative )
value = -value;
uintToString( UInt(value), current );
if ( isNegative )
*--current = '-';
assert( current >= buffer );
return current;
}
std::string valueToString( UInt value )
{
char buffer[32];
char *current = buffer + sizeof(buffer);
uintToString( value, current );
assert( current >= buffer );
return current;
}
std::string valueToString( double value )
{
char buffer[32];
#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) // Use secure version with visual studio 2005 to avoid warning.
sprintf_s(buffer, sizeof(buffer), "%#.16g", value);
#else
sprintf(buffer, "%#.16g", value);
#endif
char* ch = buffer + strlen(buffer) - 1;
if (*ch != '0') return buffer; // nothing to truncate, so save time
while(ch > buffer && *ch == '0'){
--ch;
}
char* last_nonzero = ch;
while(ch >= buffer){
switch(*ch){
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
--ch;
continue;
case '.':
// Truncate zeroes to save bytes in output, but keep one.
*(last_nonzero+2) = '\0';
return buffer;
default:
return buffer;
}
}
return buffer;
}
std::string valueToString( bool value )
{
return value ? "true" : "false";
}
std::string valueToQuotedString( const char *value )
{
// Not sure how to handle unicode...
if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL && !containsControlCharacter( value ))
return std::string("\"") + value + "\"";
// We have to walk value and escape any special characters.
// Appending to std::string is not efficient, but this should be rare.
// (Note: forward slashes are *not* rare, but I am not escaping them.)
unsigned maxsize = strlen(value)*2 + 3; // allescaped+quotes+NULL
std::string result;
result.reserve(maxsize); // to avoid lots of mallocs
result += "\"";
for (const char* c=value; *c != 0; ++c)
{
switch(*c)
{
case '\"':
result += "\\\"";
break;
case '\\':
result += "\\\\";
break;
case '\b':
result += "\\b";
break;
case '\f':
result += "\\f";
break;
case '\n':
result += "\\n";
break;
case '\r':
result += "\\r";
break;
case '\t':
result += "\\t";
break;
//case '/':
// Even though \/ is considered a legal escape in JSON, a bare
// slash is also legal, so I see no reason to escape it.
// (I hope I am not misunderstanding something.
// blep notes: actually escaping \/ may be useful in javascript to avoid </
// sequence.
// Should add a flag to allow this compatibility mode and prevent this
// sequence from occurring.
default:
if ( isControlCharacter( *c ) )
{
std::ostringstream oss;
oss << "\\u" << std::hex << std::uppercase << std::setfill('0') << std::setw(4) << static_cast<int>(*c);
result += oss.str();
}
else
{
result += *c;
}
break;
}
}
result += "\"";
return result;
}
// Class Writer
// //////////////////////////////////////////////////////////////////
Writer::~Writer()
{
}
// Class FastWriter
// //////////////////////////////////////////////////////////////////
FastWriter::FastWriter()
: yamlCompatiblityEnabled_( false )
{
}
void
FastWriter::enableYAMLCompatibility()
{
yamlCompatiblityEnabled_ = true;
}
std::string
FastWriter::write( const Value &root )
{
document_ = "";
writeValue( root );
document_ += "\n";
return document_;
}
void
FastWriter::writeValue( const Value &value )
{
switch ( value.type() )
{
case nullValue:
document_ += "null";
break;
case intValue:
document_ += valueToString( value.asInt() );
break;
case uintValue:
document_ += valueToString( value.asUInt() );
break;
case realValue:
document_ += valueToString( value.asDouble() );
break;
case stringValue:
document_ += valueToQuotedString( value.asCString() );
break;
case booleanValue:
document_ += valueToString( value.asBool() );
break;
case arrayValue:
{
document_ += "[";
int size = value.size();
for ( int index =0; index < size; ++index )
{
if ( index > 0 )
document_ += ",";
writeValue( value[index] );
}
document_ += "]";
}
break;
case objectValue:
{
Value::Members members( value.getMemberNames() );
document_ += "{";
for ( Value::Members::iterator it = members.begin();
it != members.end();
++it )
{
const std::string &name = *it;
if ( it != members.begin() )
document_ += ",";
document_ += valueToQuotedString( name.c_str() );
document_ += yamlCompatiblityEnabled_ ? ": "
: ":";
writeValue( value[name] );
}
document_ += "}";
}
break;
}
}
// Class StyledWriter
// //////////////////////////////////////////////////////////////////
StyledWriter::StyledWriter()
: rightMargin_( 74 )
, indentSize_( 3 )
{
}
std::string
StyledWriter::write( const Value &root )
{
document_ = "";
addChildValues_ = false;
indentString_ = "";
writeCommentBeforeValue( root );
writeValue( root );
writeCommentAfterValueOnSameLine( root );
document_ += "\n";
return document_;
}
void
StyledWriter::writeValue( const Value &value )
{
switch ( value.type() )
{
case nullValue:
pushValue( "null" );
break;
case intValue:
pushValue( valueToString( value.asInt() ) );
break;
case uintValue:
pushValue( valueToString( value.asUInt() ) );
break;
case realValue:
pushValue( valueToString( value.asDouble() ) );
break;
case stringValue:
pushValue( valueToQuotedString( value.asCString() ) );
break;
case booleanValue:
pushValue( valueToString( value.asBool() ) );
break;
case arrayValue:
writeArrayValue( value);
break;
case objectValue:
{
Value::Members members( value.getMemberNames() );
if ( members.empty() )
pushValue( "{}" );
else
{
writeWithIndent( "{" );
indent();
Value::Members::iterator it = members.begin();
while ( true )
{
const std::string &name = *it;
const Value &childValue = value[name];
writeCommentBeforeValue( childValue );
writeWithIndent( valueToQuotedString( name.c_str() ) );
document_ += " : ";
writeValue( childValue );
if ( ++it == members.end() )
{
writeCommentAfterValueOnSameLine( childValue );
break;
}
document_ += ",";
writeCommentAfterValueOnSameLine( childValue );
}
unindent();
writeWithIndent( "}" );
}
}
break;
}
}
void
StyledWriter::writeArrayValue( const Value &value )
{
unsigned size = value.size();
if ( size == 0 )
pushValue( "[]" );
else
{
bool isArrayMultiLine = isMultineArray( value );
if ( isArrayMultiLine )
{
writeWithIndent( "[" );
indent();
bool hasChildValue = !childValues_.empty();
unsigned index =0;
while ( true )
{
const Value &childValue = value[index];
writeCommentBeforeValue( childValue );
if ( hasChildValue )
writeWithIndent( childValues_[index] );
else
{
writeIndent();
writeValue( childValue );
}
if ( ++index == size )
{
writeCommentAfterValueOnSameLine( childValue );
break;
}
document_ += ",";
writeCommentAfterValueOnSameLine( childValue );
}
unindent();
writeWithIndent( "]" );
}
else // output on a single line
{
assert( childValues_.size() == size );
document_ += "[ ";
for ( unsigned index =0; index < size; ++index )
{
if ( index > 0 )
document_ += ", ";
document_ += childValues_[index];
}
document_ += " ]";
}
}
}
bool
StyledWriter::isMultineArray( const Value &value )
{
int size = value.size();
bool isMultiLine = size*3 >= rightMargin_ ;
childValues_.clear();
for ( int index =0; index < size && !isMultiLine; ++index )
{
const Value &childValue = value[index];
isMultiLine = isMultiLine ||
( (childValue.isArray() || childValue.isObject()) &&
childValue.size() > 0 );
}
if ( !isMultiLine ) // check if line length > max line length
{
childValues_.reserve( size );
addChildValues_ = true;
int lineLength = 4 + (size-1)*2; // '[ ' + ', '*n + ' ]'
for ( int index =0; index < size && !isMultiLine; ++index )
{
writeValue( value[index] );
lineLength += int( childValues_[index].length() );
isMultiLine = isMultiLine && hasCommentForValue( value[index] );
}
addChildValues_ = false;
isMultiLine = isMultiLine || lineLength >= rightMargin_;
}
return isMultiLine;
}
void
StyledWriter::pushValue( const std::string &value )
{
if ( addChildValues_ )
childValues_.push_back( value );
else
document_ += value;
}
void
StyledWriter::writeIndent()
{
if ( !document_.empty() )
{
char last = document_[document_.length()-1];
if ( last == ' ' ) // already indented
return;
if ( last != '\n' ) // Comments may add new-line
document_ += '\n';
}
document_ += indentString_;
}
void
StyledWriter::writeWithIndent( const std::string &value )
{
writeIndent();
document_ += value;
}
void
StyledWriter::indent()
{
indentString_ += std::string( indentSize_, ' ' );
}
void
StyledWriter::unindent()
{
assert( int(indentString_.size()) >= indentSize_ );
indentString_.resize( indentString_.size() - indentSize_ );
}
void
StyledWriter::writeCommentBeforeValue( const Value &root )
{
if ( !root.hasComment( commentBefore ) )
return;
document_ += normalizeEOL( root.getComment( commentBefore ) );
document_ += "\n";
}
void
StyledWriter::writeCommentAfterValueOnSameLine( const Value &root )
{
if ( root.hasComment( commentAfterOnSameLine ) )
document_ += " " + normalizeEOL( root.getComment( commentAfterOnSameLine ) );
if ( root.hasComment( commentAfter ) )
{
document_ += "\n";
document_ += normalizeEOL( root.getComment( commentAfter ) );
document_ += "\n";
}
}
bool
StyledWriter::hasCommentForValue( const Value &value )
{
return value.hasComment( commentBefore )
|| value.hasComment( commentAfterOnSameLine )
|| value.hasComment( commentAfter );
}
std::string
StyledWriter::normalizeEOL( const std::string &text )
{
std::string normalized;
normalized.reserve( text.length() );
const char *begin = text.c_str();
const char *end = begin + text.length();
const char *current = begin;
while ( current != end )
{
char c = *current++;
if ( c == '\r' ) // mac or dos EOL
{
if ( *current == '\n' ) // convert dos EOL
++current;
normalized += '\n';
}
else // handle unix EOL & other char
normalized += c;
}
return normalized;
}
// Class StyledStreamWriter
// //////////////////////////////////////////////////////////////////
StyledStreamWriter::StyledStreamWriter( std::string indentation )
: document_(NULL)
, rightMargin_( 74 )
, indentation_( indentation )
{
}
void
StyledStreamWriter::write( std::ostream &out, const Value &root )
{
document_ = &out;
addChildValues_ = false;
indentString_ = "";
writeCommentBeforeValue( root );
writeValue( root );
writeCommentAfterValueOnSameLine( root );
*document_ << "\n";
document_ = NULL; // Forget the stream, for safety.
}
void
StyledStreamWriter::writeValue( const Value &value )
{
switch ( value.type() )
{
case nullValue:
pushValue( "null" );
break;
case intValue:
pushValue( valueToString( value.asInt() ) );
break;
case uintValue:
pushValue( valueToString( value.asUInt() ) );
break;
case realValue:
pushValue( valueToString( value.asDouble() ) );
break;
case stringValue:
pushValue( valueToQuotedString( value.asCString() ) );
break;
case booleanValue:
pushValue( valueToString( value.asBool() ) );
break;
case arrayValue:
writeArrayValue( value);
break;
case objectValue:
{
Value::Members members( value.getMemberNames() );
if ( members.empty() )
pushValue( "{}" );
else
{
writeWithIndent( "{" );
indent();
Value::Members::iterator it = members.begin();
while ( true )
{
const std::string &name = *it;
const Value &childValue = value[name];
writeCommentBeforeValue( childValue );
writeWithIndent( valueToQuotedString( name.c_str() ) );
*document_ << " : ";
writeValue( childValue );
if ( ++it == members.end() )
{
writeCommentAfterValueOnSameLine( childValue );
break;
}
*document_ << ",";
writeCommentAfterValueOnSameLine( childValue );
}
unindent();
writeWithIndent( "}" );
}
}
break;
}
}
void
StyledStreamWriter::writeArrayValue( const Value &value )
{
unsigned size = value.size();
if ( size == 0 )
pushValue( "[]" );
else
{
bool isArrayMultiLine = isMultineArray( value );
if ( isArrayMultiLine )
{
writeWithIndent( "[" );
indent();
bool hasChildValue = !childValues_.empty();
unsigned index =0;
while ( true )
{
const Value &childValue = value[index];
writeCommentBeforeValue( childValue );
if ( hasChildValue )
writeWithIndent( childValues_[index] );
else
{
writeIndent();
writeValue( childValue );
}
if ( ++index == size )
{
writeCommentAfterValueOnSameLine( childValue );
break;
}
*document_ << ",";
writeCommentAfterValueOnSameLine( childValue );
}
unindent();
writeWithIndent( "]" );
}
else // output on a single line
{
assert( childValues_.size() == size );
*document_ << "[ ";
for ( unsigned index =0; index < size; ++index )
{
if ( index > 0 )
*document_ << ", ";
*document_ << childValues_[index];
}
*document_ << " ]";
}
}
}
bool
StyledStreamWriter::isMultineArray( const Value &value )
{
int size = value.size();
bool isMultiLine = size*3 >= rightMargin_ ;
childValues_.clear();
for ( int index =0; index < size && !isMultiLine; ++index )
{
const Value &childValue = value[index];
isMultiLine = isMultiLine ||
( (childValue.isArray() || childValue.isObject()) &&
childValue.size() > 0 );
}
if ( !isMultiLine ) // check if line length > max line length
{
childValues_.reserve( size );
addChildValues_ = true;
int lineLength = 4 + (size-1)*2; // '[ ' + ', '*n + ' ]'
for ( int index =0; index < size && !isMultiLine; ++index )
{
writeValue( value[index] );
lineLength += int( childValues_[index].length() );
isMultiLine = isMultiLine && hasCommentForValue( value[index] );
}
addChildValues_ = false;
isMultiLine = isMultiLine || lineLength >= rightMargin_;
}
return isMultiLine;
}
void
StyledStreamWriter::pushValue( const std::string &value )
{
if ( addChildValues_ )
childValues_.push_back( value );
else
*document_ << value;
}
void
StyledStreamWriter::writeIndent()
{
/*
Some comments in this method would have been nice. ;-)
if ( !document_.empty() )
{
char last = document_[document_.length()-1];
if ( last == ' ' ) // already indented
return;
if ( last != '\n' ) // Comments may add new-line
*document_ << '\n';
}
*/
*document_ << '\n' << indentString_;
}
void
StyledStreamWriter::writeWithIndent( const std::string &value )
{
writeIndent();
*document_ << value;
}
void
StyledStreamWriter::indent()
{
indentString_ += indentation_;
}
void
StyledStreamWriter::unindent()
{
assert( indentString_.size() >= indentation_.size() );
indentString_.resize( indentString_.size() - indentation_.size() );
}
void
StyledStreamWriter::writeCommentBeforeValue( const Value &root )
{
if ( !root.hasComment( commentBefore ) )
return;
*document_ << normalizeEOL( root.getComment( commentBefore ) );
*document_ << "\n";
}
void
StyledStreamWriter::writeCommentAfterValueOnSameLine( const Value &root )
{
if ( root.hasComment( commentAfterOnSameLine ) )
*document_ << " " + normalizeEOL( root.getComment( commentAfterOnSameLine ) );
if ( root.hasComment( commentAfter ) )
{
*document_ << "\n";
*document_ << normalizeEOL( root.getComment( commentAfter ) );
*document_ << "\n";
}
}
bool
StyledStreamWriter::hasCommentForValue( const Value &value )
{
return value.hasComment( commentBefore )
|| value.hasComment( commentAfterOnSameLine )
|| value.hasComment( commentAfter );
}
std::string
StyledStreamWriter::normalizeEOL( const std::string &text )
{
std::string normalized;
normalized.reserve( text.length() );
const char *begin = text.c_str();
const char *end = begin + text.length();
const char *current = begin;
while ( current != end )
{
char c = *current++;
if ( c == '\r' ) // mac or dos EOL
{
if ( *current == '\n' ) // convert dos EOL
++current;
normalized += '\n';
}
else // handle unix EOL & other char
normalized += c;
}
return normalized;
}
std::ostream& operator<<( std::ostream &sout, const Value &root )
{
Json::StyledStreamWriter writer;
writer.write(sout, root);
return sout;
}
} // namespace Json

View File

@ -1,8 +0,0 @@
Import( 'env buildLibrary' )
buildLibrary( env, Split( """
json_reader.cpp
json_value.cpp
json_writer.cpp
""" ),
'json' )

View File

@ -1,603 +0,0 @@
#define _CRT_SECURE_NO_WARNINGS 1 // Prevents deprecation warning with MSVC
#include "jsontest.h"
#include <stdio.h>
#include <string>
#if defined(_MSC_VER)
// Used to install a report hook that prevent dialog on assertion and error.
# include <crtdbg.h>
#endif // if defined(_MSC_VER)
#if defined(_WIN32)
// Used to prevent dialog on memory fault.
// Limits headers included by Windows.h
# define WIN32_LEAN_AND_MEAN
# define NOSERVICE
# define NOMCX
# define NOIME
# define NOSOUND
# define NOCOMM
# define NORPC
# define NOGDI
# define NOUSER
# define NODRIVERS
# define NOLOGERROR
# define NOPROFILER
# define NOMEMMGR
# define NOLFILEIO
# define NOOPENFILE
# define NORESOURCE
# define NOATOM
# define NOLANGUAGE
# define NOLSTRING
# define NODBCS
# define NOKEYBOARDINFO
# define NOGDICAPMASKS
# define NOCOLOR
# define NOGDIOBJ
# define NODRAWTEXT
# define NOTEXTMETRIC
# define NOSCALABLEFONT
# define NOBITMAP
# define NORASTEROPS
# define NOMETAFILE
# define NOSYSMETRICS
# define NOSYSTEMPARAMSINFO
# define NOMSG
# define NOWINSTYLES
# define NOWINOFFSETS
# define NOSHOWWINDOW
# define NODEFERWINDOWPOS
# define NOVIRTUALKEYCODES
# define NOKEYSTATES
# define NOWH
# define NOMENUS
# define NOSCROLL
# define NOCLIPBOARD
# define NOICONS
# define NOMB
# define NOSYSCOMMANDS
# define NOMDI
# define NOCTLMGR
# define NOWINMESSAGES
# include <windows.h>
#endif // if defined(_WIN32)
namespace JsonTest {
// class TestResult
// //////////////////////////////////////////////////////////////////
TestResult::TestResult()
: predicateId_( 1 )
, lastUsedPredicateId_( 0 )
, messageTarget_( 0 )
{
// The root predicate has id 0
rootPredicateNode_.id_ = 0;
rootPredicateNode_.next_ = 0;
predicateStackTail_ = &rootPredicateNode_;
}
void
TestResult::setTestName( const std::string &name )
{
name_ = name;
}
TestResult &
TestResult::addFailure( const char *file, unsigned int line,
const char *expr )
{
/// Walks the PredicateContext stack adding them to failures_ if not already added.
unsigned int nestingLevel = 0;
PredicateContext *lastNode = rootPredicateNode_.next_;
for ( ; lastNode != 0; lastNode = lastNode->next_ )
{
if ( lastNode->id_ > lastUsedPredicateId_ ) // new PredicateContext
{
lastUsedPredicateId_ = lastNode->id_;
addFailureInfo( lastNode->file_, lastNode->line_, lastNode->expr_,
nestingLevel );
// Link the PredicateContext to the failure for message target when
// popping the PredicateContext.
lastNode->failure_ = &( failures_.back() );
}
++nestingLevel;
}
// Adds the failed assertion
addFailureInfo( file, line, expr, nestingLevel );
messageTarget_ = &( failures_.back() );
return *this;
}
void
TestResult::addFailureInfo( const char *file, unsigned int line,
const char *expr, unsigned int nestingLevel )
{
Failure failure;
failure.file_ = file;
failure.line_ = line;
if ( expr )
{
failure.expr_ = expr;
}
failure.nestingLevel_ = nestingLevel;
failures_.push_back( failure );
}
TestResult &
TestResult::popPredicateContext()
{
PredicateContext *lastNode = &rootPredicateNode_;
while ( lastNode->next_ != 0 && lastNode->next_->next_ != 0 )
{
lastNode = lastNode->next_;
}
// Set message target to popped failure
PredicateContext *tail = lastNode->next_;
if ( tail != 0 && tail->failure_ != 0 )
{
messageTarget_ = tail->failure_;
}
// Remove tail from list
predicateStackTail_ = lastNode;
lastNode->next_ = 0;
return *this;
}
bool
TestResult::failed() const
{
return !failures_.empty();
}
unsigned int
TestResult::getAssertionNestingLevel() const
{
unsigned int level = 0;
const PredicateContext *lastNode = &rootPredicateNode_;
while ( lastNode->next_ != 0 )
{
lastNode = lastNode->next_;
++level;
}
return level;
}
void
TestResult::printFailure( bool printTestName ) const
{
if ( failures_.empty() )
{
return;
}
if ( printTestName )
{
printf( "* Detail of %s test failure:\n", name_.c_str() );
}
// Print in reverse to display the callstack in the right order
Failures::const_iterator itEnd = failures_.end();
for ( Failures::const_iterator it = failures_.begin(); it != itEnd; ++it )
{
const Failure &failure = *it;
std::string indent( failure.nestingLevel_ * 2, ' ' );
if ( failure.file_ )
{
printf( "%s%s(%d): ", indent.c_str(), failure.file_, failure.line_ );
}
if ( !failure.expr_.empty() )
{
printf( "%s\n", failure.expr_.c_str() );
}
else if ( failure.file_ )
{
printf( "\n" );
}
if ( !failure.message_.empty() )
{
std::string reindented = indentText( failure.message_, indent + " " );
printf( "%s\n", reindented.c_str() );
}
}
}
std::string
TestResult::indentText( const std::string &text,
const std::string &indent )
{
std::string reindented;
std::string::size_type lastIndex = 0;
while ( lastIndex < text.size() )
{
std::string::size_type nextIndex = text.find( '\n', lastIndex );
if ( nextIndex == std::string::npos )
{
nextIndex = text.size() - 1;
}
reindented += indent;
reindented += text.substr( lastIndex, nextIndex - lastIndex + 1 );
lastIndex = nextIndex + 1;
}
return reindented;
}
TestResult &
TestResult::addToLastFailure( const std::string &message )
{
if ( messageTarget_ != 0 )
{
messageTarget_->message_ += message;
}
return *this;
}
TestResult &
TestResult::operator << ( bool value )
{
return addToLastFailure( value ? "true" : "false" );
}
TestResult &
TestResult::operator << ( int value )
{
char buffer[32];
sprintf( buffer, "%d", value );
return addToLastFailure( buffer );
}
TestResult &
TestResult::operator << ( unsigned int value )
{
char buffer[32];
sprintf( buffer, "%u", value );
return addToLastFailure( buffer );
}
TestResult &
TestResult::operator << ( double value )
{
char buffer[32];
sprintf( buffer, "%16g", value );
return addToLastFailure( buffer );
}
TestResult &
TestResult::operator << ( const char *value )
{
return addToLastFailure( value ? value
: "<NULL>" );
}
TestResult &
TestResult::operator << ( const std::string &value )
{
return addToLastFailure( value );
}
// class TestCase
// //////////////////////////////////////////////////////////////////
TestCase::TestCase()
: result_( 0 )
{
}
TestCase::~TestCase()
{
}
void
TestCase::run( TestResult &result )
{
result_ = &result;
runTestCase();
}
// class Runner
// //////////////////////////////////////////////////////////////////
Runner::Runner()
{
}
Runner &
Runner::add( TestCaseFactory factory )
{
tests_.push_back( factory );
return *this;
}
unsigned int
Runner::testCount() const
{
return static_cast<unsigned int>( tests_.size() );
}
std::string
Runner::testNameAt( unsigned int index ) const
{
TestCase *test = tests_[index]();
std::string name = test->testName();
delete test;
return name;
}
void
Runner::runTestAt( unsigned int index, TestResult &result ) const
{
TestCase *test = tests_[index]();
result.setTestName( test->testName() );
printf( "Testing %s: ", test->testName() );
fflush( stdout );
#if JSON_USE_EXCEPTION
try
{
#endif // if JSON_USE_EXCEPTION
test->run( result );
#if JSON_USE_EXCEPTION
}
catch ( const std::exception &e )
{
result.addFailure( __FILE__, __LINE__,
"Unexpected exception caugth:" ) << e.what();
}
#endif // if JSON_USE_EXCEPTION
delete test;
const char *status = result.failed() ? "FAILED"
: "OK";
printf( "%s\n", status );
fflush( stdout );
}
bool
Runner::runAllTest( bool printSummary ) const
{
unsigned int count = testCount();
std::deque<TestResult> failures;
for ( unsigned int index = 0; index < count; ++index )
{
TestResult result;
runTestAt( index, result );
if ( result.failed() )
{
failures.push_back( result );
}
}
if ( failures.empty() )
{
if ( printSummary )
{
printf( "All %d tests passed\n", count );
}
return true;
}
else
{
for ( unsigned int index = 0; index < failures.size(); ++index )
{
TestResult &result = failures[index];
result.printFailure( count > 1 );
}
if ( printSummary )
{
unsigned int failedCount = static_cast<unsigned int>( failures.size() );
unsigned int passedCount = count - failedCount;
printf( "%d/%d tests passed (%d failure(s))\n", passedCount, count, failedCount );
}
return false;
}
}
bool
Runner::testIndex( const std::string &testName,
unsigned int &indexOut ) const
{
unsigned int count = testCount();
for ( unsigned int index = 0; index < count; ++index )
{
if ( testNameAt(index) == testName )
{
indexOut = index;
return true;
}
}
return false;
}
void
Runner::listTests() const
{
unsigned int count = testCount();
for ( unsigned int index = 0; index < count; ++index )
{
printf( "%s\n", testNameAt( index ).c_str() );
}
}
int
Runner::runCommandLine( int argc, const char *argv[] ) const
{
typedef std::deque<std::string> TestNames;
Runner subrunner;
for ( int index = 1; index < argc; ++index )
{
std::string opt = argv[index];
if ( opt == "--list-tests" )
{
listTests();
return 0;
}
else if ( opt == "--test-auto" )
{
preventDialogOnCrash();
}
else if ( opt == "--test" )
{
++index;
if ( index < argc )
{
unsigned int testNameIndex;
if ( testIndex( argv[index], testNameIndex ) )
{
subrunner.add( tests_[testNameIndex] );
}
else
{
fprintf( stderr, "Test '%s' does not exist!\n", argv[index] );
return 2;
}
}
else
{
printUsage( argv[0] );
return 2;
}
}
else
{
printUsage( argv[0] );
return 2;
}
}
bool succeeded;
if ( subrunner.testCount() > 0 )
{
succeeded = subrunner.runAllTest( subrunner.testCount() > 1 );
}
else
{
succeeded = runAllTest( true );
}
return succeeded ? 0
: 1;
}
#if defined(_MSC_VER)
// Hook MSVCRT assertions to prevent dialog from appearing
static int
msvcrtSilentReportHook( int reportType, char *message, int *returnValue )
{
// The default CRT handling of error and assertion is to display
// an error dialog to the user.
// Instead, when an error or an assertion occurs, we force the
// application to terminate using abort() after display
// the message on stderr.
if ( reportType == _CRT_ERROR ||
reportType == _CRT_ASSERT )
{
// calling abort() cause the ReportHook to be called
// The following is used to detect this case and let's the
// error handler fallback on its default behaviour (
// display a warning message)
static volatile bool isAborting = false;
if ( isAborting )
{
return TRUE;
}
isAborting = true;
fprintf( stderr, "CRT Error/Assert:\n%s\n", message );
fflush( stderr );
abort();
}
// Let's other reportType (_CRT_WARNING) be handled as they would by default
return FALSE;
}
#endif // if defined(_MSC_VER)
void
Runner::preventDialogOnCrash()
{
#if defined(_MSC_VER)
// Install a hook to prevent MSVCRT error and assertion from
// popping a dialog.
_CrtSetReportHook( &msvcrtSilentReportHook );
#endif // if defined(_MSC_VER)
// @todo investiguate this handler (for buffer overflow)
// _set_security_error_handler
#if defined(_WIN32)
// Prevents the system from popping a dialog for debugging if the
// application fails due to invalid memory access.
SetErrorMode( SEM_FAILCRITICALERRORS
| SEM_NOGPFAULTERRORBOX
| SEM_NOOPENFILEERRORBOX );
#endif // if defined(_WIN32)
}
void
Runner::printUsage( const char *appName )
{
printf(
"Usage: %s [options]\n"
"\n"
"If --test is not specified, then all the test cases be run.\n"
"\n"
"Valid options:\n"
"--list-tests: print the name of all test cases on the standard\n"
" output and exit.\n"
"--test TESTNAME: executes the test case with the specified name.\n"
" May be repeated.\n"
"--test-auto: prevent dialog prompting for debugging on crash.\n"
, appName );
}
// Assertion functions
// //////////////////////////////////////////////////////////////////
TestResult &
checkStringEqual( TestResult &result,
const std::string &expected, const std::string &actual,
const char *file, unsigned int line, const char *expr )
{
if ( expected != actual )
{
result.addFailure( file, line, expr );
result << "Expected: '" << expected << "'\n";
result << "Actual : '" << actual << "'";
}
return result;
}
} // namespace JsonTest

View File

@ -1,254 +0,0 @@
#ifndef JSONTEST_H_INCLUDED
# define JSONTEST_H_INCLUDED
# include <json/config.h>
# include <stdio.h>
# include <deque>
# include <string>
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// Mini Unit Testing framework
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
/** \brief Unit testing framework.
* \warning: all assertions are non-aborting, test case execution will continue
* even if an assertion namespace.
* This constraint is for portability: the framework needs to compile
* on Visual Studio 6 and must not require exception usage.
*/
namespace JsonTest {
class Failure
{
public:
const char *file_;
unsigned int line_;
std::string expr_;
std::string message_;
unsigned int nestingLevel_;
};
/// Context used to create the assertion callstack on failure.
/// Must be a POD to allow inline initialisation without stepping
/// into the debugger.
struct PredicateContext
{
typedef unsigned int Id;
Id id_;
const char *file_;
unsigned int line_;
const char *expr_;
PredicateContext *next_;
/// Related Failure, set when the PredicateContext is converted
/// into a Failure.
Failure *failure_;
};
class TestResult
{
public:
TestResult();
/// \internal Implementation detail for assertion macros
/// Not encapsulated to prevent step into when debugging failed assertions
/// Incremented by one on assertion predicate entry, decreased by one
/// by addPredicateContext().
PredicateContext::Id predicateId_;
/// \internal Implementation detail for predicate macros
PredicateContext *predicateStackTail_;
void setTestName( const std::string &name );
/// Adds an assertion failure.
TestResult &addFailure( const char *file, unsigned int line,
const char *expr = 0 );
/// Removes the last PredicateContext added to the predicate stack
/// chained list.
/// Next messages will be targed at the PredicateContext that was removed.
TestResult &popPredicateContext();
bool failed() const;
void printFailure( bool printTestName ) const;
TestResult &operator << ( bool value );
TestResult &operator << ( int value );
TestResult &operator << ( unsigned int value );
TestResult &operator << ( double value );
TestResult &operator << ( const char *value );
TestResult &operator << ( const std::string &value );
private:
TestResult &addToLastFailure( const std::string &message );
unsigned int getAssertionNestingLevel() const;
/// Adds a failure or a predicate context
void addFailureInfo( const char *file, unsigned int line,
const char *expr, unsigned int nestingLevel );
static std::string indentText( const std::string &text,
const std::string &indent );
typedef std::deque<Failure> Failures;
Failures failures_;
std::string name_;
PredicateContext rootPredicateNode_;
PredicateContext::Id lastUsedPredicateId_;
/// Failure which is the target of the messages added using operator <<
Failure *messageTarget_;
};
class TestCase
{
public:
TestCase();
virtual ~TestCase();
void run( TestResult &result );
virtual const char *testName() const = 0;
protected:
TestResult *result_;
private:
virtual void runTestCase() = 0;
};
/// Function pointer type for TestCase factory
typedef TestCase *(*TestCaseFactory)();
class Runner
{
public:
Runner();
/// Adds a test to the suite
Runner &add( TestCaseFactory factory );
/// Runs test as specified on the command-line
/// If no command-line arguments are provided, run all tests.
/// If --list-tests is provided, then print the list of all test cases
/// If --test <testname> is provided, then run test testname.
int runCommandLine( int argc, const char *argv[] ) const;
/// Runs all the test cases
bool runAllTest( bool printSummary ) const;
/// Returns the number of test case in the suite
unsigned int testCount() const;
/// Returns the name of the test case at the specified index
std::string testNameAt( unsigned int index ) const;
/// Runs the test case at the specified index using the specified TestResult
void runTestAt( unsigned int index, TestResult &result ) const;
static void printUsage( const char *appName );
private: // prevents copy construction and assignment
Runner( const Runner &other );
Runner &operator =( const Runner &other );
private:
void listTests() const;
bool testIndex( const std::string &testName, unsigned int &index ) const;
static void preventDialogOnCrash();
private:
typedef std::deque<TestCaseFactory> Factories;
Factories tests_;
};
template<typename T>
TestResult &
checkEqual( TestResult &result, const T &expected, const T &actual,
const char *file, unsigned int line, const char *expr )
{
if ( expected != actual )
{
result.addFailure( file, line, expr );
result << "Expected: " << expected << "\n";
result << "Actual : " << actual;
}
return result;
}
TestResult &
checkStringEqual( TestResult &result,
const std::string &expected, const std::string &actual,
const char *file, unsigned int line, const char *expr );
} // namespace JsonTest
/// \brief Asserts that the given expression is true.
/// JSONTEST_ASSERT( x == y ) << "x=" << x << ", y=" << y;
/// JSONTEST_ASSERT( x == y );
#define JSONTEST_ASSERT( expr ) \
if ( condition ) \
{ \
} \
else \
result_->addFailure( __FILE__, __LINE__, #expr )
/// \brief Asserts that the given predicate is true.
/// The predicate may do other assertions and be a member function of the fixture.
#define JSONTEST_ASSERT_PRED( expr ) \
{ \
JsonTest::PredicateContext _minitest_Context = { \
result_->predicateId_, __FILE__, __LINE__, #expr }; \
result_->predicateStackTail_->next_ = &_minitest_Context; \
result_->predicateId_ += 1; \
result_->predicateStackTail_ = &_minitest_Context; \
(expr); \
result_->popPredicateContext(); \
} \
*result_
/// \brief Asserts that two values are equals.
#define JSONTEST_ASSERT_EQUAL( expected, actual ) \
JsonTest::checkEqual( *result_, expected, actual, \
__FILE__, __LINE__, \
#expected " == " #actual )
/// \brief Asserts that two values are equals.
#define JSONTEST_ASSERT_STRING_EQUAL( expected, actual ) \
JsonTest::checkStringEqual( *result_, \
std::string(expected), std::string(actual), \
#expected " == " #actual )
/// \brief Begin a fixture test case.
#define JSONTEST_FIXTURE( FixtureType, name ) \
class Test##FixtureType##name : public FixtureType \
{ \
public: \
static JsonTest::TestCase *factory() \
{ \
return new Test##FixtureType##name(); \
} \
public: /* overidden from TestCase */ \
virtual const char *testName() const \
{ \
return #FixtureType "/" #name; \
} \
virtual void runTestCase(); \
}; \
\
void Test##FixtureType##name::runTestCase()
#define JSONTEST_FIXTURE_FACTORY( FixtureType, name ) \
&Test##FixtureType##name::factory
#define JSONTEST_REGISTER_FIXTURE( runner, FixtureType, name ) \
(runner).add( JSONTEST_FIXTURE_FACTORY( FixtureType, name ) )
#endif // ifndef JSONTEST_H_INCLUDED

View File

@ -1,244 +0,0 @@
#include <json/json.h>
#include "jsontest.h"
// TODO:
// - boolean value returns that they are integral. Should not be.
// - unsigned integer in integer range are not considered to be valid integer. Should check range.
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// Json Library test cases
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
struct ValueTest : JsonTest::TestCase
{
Json::Value null_;
Json::Value emptyArray_;
Json::Value emptyObject_;
Json::Value integer_;
Json::Value unsignedInteger_;
Json::Value smallUnsignedInteger_;
Json::Value real_;
Json::Value array1_;
Json::Value object1_;
Json::Value emptyString_;
Json::Value string1_;
Json::Value string_;
Json::Value true_;
Json::Value false_;
ValueTest()
: emptyArray_( Json::arrayValue )
, emptyObject_( Json::objectValue )
, integer_( 123456789 )
, smallUnsignedInteger_( Json::Value::UInt( Json::Value::maxInt ) )
, unsignedInteger_( 34567890u )
, real_( 1234.56789 )
, emptyString_( "" )
, string1_( "a" )
, string_( "sometext with space" )
, true_( true )
, false_( false )
{
array1_.append( 1234 );
object1_["id"] = 1234;
}
struct IsCheck
{
/// Initialize all checks to \c false by default.
IsCheck();
bool isObject_;
bool isArray_;
bool isBool_;
bool isDouble_;
bool isInt_;
bool isUInt_;
bool isIntegral_;
bool isNumeric_;
bool isString_;
bool isNull_;
};
void checkConstMemberCount( const Json::Value &value, unsigned int expectedCount );
void checkMemberCount( Json::Value &value, unsigned int expectedCount );
void checkIs( const Json::Value &value, const IsCheck &check );
};
JSONTEST_FIXTURE( ValueTest, size )
{
JSONTEST_ASSERT_PRED( checkMemberCount(emptyArray_, 0) );
JSONTEST_ASSERT_PRED( checkMemberCount(emptyObject_, 0) );
JSONTEST_ASSERT_PRED( checkMemberCount(array1_, 1) );
JSONTEST_ASSERT_PRED( checkMemberCount(object1_, 1) );
JSONTEST_ASSERT_PRED( checkMemberCount(null_, 0) );
JSONTEST_ASSERT_PRED( checkMemberCount(integer_, 0) );
JSONTEST_ASSERT_PRED( checkMemberCount(real_, 0) );
JSONTEST_ASSERT_PRED( checkMemberCount(emptyString_, 0) );
JSONTEST_ASSERT_PRED( checkMemberCount(string_, 0) );
JSONTEST_ASSERT_PRED( checkMemberCount(true_, 0) );
}
JSONTEST_FIXTURE( ValueTest, isObject )
{
IsCheck checks;
checks.isObject_ = true;
JSONTEST_ASSERT_PRED( checkIs( emptyObject_, checks ) );
JSONTEST_ASSERT_PRED( checkIs( object1_, checks ) );
}
JSONTEST_FIXTURE( ValueTest, isArray )
{
IsCheck checks;
checks.isArray_ = true;
JSONTEST_ASSERT_PRED( checkIs( emptyArray_, checks ) );
JSONTEST_ASSERT_PRED( checkIs( array1_, checks ) );
}
JSONTEST_FIXTURE( ValueTest, isNull )
{
IsCheck checks;
checks.isNull_ = true;
checks.isObject_ = true;
checks.isArray_ = true;
JSONTEST_ASSERT_PRED( checkIs( null_, checks ) );
}
JSONTEST_FIXTURE( ValueTest, isString )
{
IsCheck checks;
checks.isString_ = true;
JSONTEST_ASSERT_PRED( checkIs( emptyString_, checks ) );
JSONTEST_ASSERT_PRED( checkIs( string_, checks ) );
JSONTEST_ASSERT_PRED( checkIs( string1_, checks ) );
}
JSONTEST_FIXTURE( ValueTest, isBool )
{
IsCheck checks;
checks.isBool_ = true;
checks.isIntegral_ = true;
checks.isNumeric_ = true;
JSONTEST_ASSERT_PRED( checkIs( false_, checks ) );
JSONTEST_ASSERT_PRED( checkIs( true_, checks ) );
}
JSONTEST_FIXTURE( ValueTest, isDouble )
{
IsCheck checks;
checks.isDouble_ = true;
checks.isNumeric_ = true;
JSONTEST_ASSERT_PRED( checkIs( real_, checks ) );
}
JSONTEST_FIXTURE( ValueTest, isInt )
{
IsCheck checks;
checks.isInt_ = true;
checks.isNumeric_ = true;
checks.isIntegral_ = true;
JSONTEST_ASSERT_PRED( checkIs( integer_, checks ) );
}
JSONTEST_FIXTURE( ValueTest, isUInt )
{
IsCheck checks;
checks.isUInt_ = true;
checks.isNumeric_ = true;
checks.isIntegral_ = true;
JSONTEST_ASSERT_PRED( checkIs( unsignedInteger_, checks ) );
JSONTEST_ASSERT_PRED( checkIs( smallUnsignedInteger_, checks ) );
}
void
ValueTest::checkConstMemberCount( const Json::Value &value, unsigned int expectedCount )
{
unsigned int count = 0;
Json::Value::const_iterator itEnd = value.end();
for ( Json::Value::const_iterator it = value.begin(); it != itEnd; ++it )
{
++count;
}
JSONTEST_ASSERT_EQUAL( expectedCount, count ) << "Json::Value::const_iterator";
}
void
ValueTest::checkMemberCount( Json::Value &value, unsigned int expectedCount )
{
JSONTEST_ASSERT_EQUAL( expectedCount, value.size() );
unsigned int count = 0;
Json::Value::iterator itEnd = value.end();
for ( Json::Value::iterator it = value.begin(); it != itEnd; ++it )
{
++count;
}
JSONTEST_ASSERT_EQUAL( expectedCount, count ) << "Json::Value::iterator";
JSONTEST_ASSERT_PRED( checkConstMemberCount(value, expectedCount) );
}
ValueTest::IsCheck::IsCheck()
: isObject_( false )
, isArray_( false )
, isBool_( false )
, isDouble_( false )
, isInt_( false )
, isUInt_( false )
, isIntegral_( false )
, isNumeric_( false )
, isString_( false )
, isNull_( false )
{
}
void
ValueTest::checkIs( const Json::Value &value, const IsCheck &check )
{
JSONTEST_ASSERT_EQUAL( check.isObject_, value.isObject() );
JSONTEST_ASSERT_EQUAL( check.isArray_, value.isArray() );
JSONTEST_ASSERT_EQUAL( check.isBool_, value.isBool() );
JSONTEST_ASSERT_EQUAL( check.isDouble_, value.isDouble() );
JSONTEST_ASSERT_EQUAL( check.isInt_, value.isInt() );
JSONTEST_ASSERT_EQUAL( check.isUInt_, value.isUInt() );
JSONTEST_ASSERT_EQUAL( check.isIntegral_, value.isIntegral() );
JSONTEST_ASSERT_EQUAL( check.isNumeric_, value.isNumeric() );
JSONTEST_ASSERT_EQUAL( check.isString_, value.isString() );
JSONTEST_ASSERT_EQUAL( check.isNull_, value.isNull() );
}
int main( int argc, const char *argv[] )
{
JsonTest::Runner runner;
JSONTEST_REGISTER_FIXTURE( runner, ValueTest, size );
JSONTEST_REGISTER_FIXTURE( runner, ValueTest, isObject );
JSONTEST_REGISTER_FIXTURE( runner, ValueTest, isArray );
JSONTEST_REGISTER_FIXTURE( runner, ValueTest, isBool );
JSONTEST_REGISTER_FIXTURE( runner, ValueTest, isInt );
JSONTEST_REGISTER_FIXTURE( runner, ValueTest, isUInt );
JSONTEST_REGISTER_FIXTURE( runner, ValueTest, isDouble );
JSONTEST_REGISTER_FIXTURE( runner, ValueTest, isString );
JSONTEST_REGISTER_FIXTURE( runner, ValueTest, isNull );
return runner.runCommandLine( argc, argv );
}

View File

@ -1,10 +0,0 @@
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')

View File

@ -1 +0,0 @@
0.5.0

View File

@ -1,221 +0,0 @@
UltraJSON is a fast and extendable JSON encoder and decoder written in pure C
Python bindings are available as the module ujson (through easy_install / pypi):
http://pypi.python.org/pypi/ujson/
Installation instructions:
1. Build and install ujson Python extension (requires root)
Go to <root>/python
Type: python setup.py build install
2. Run tests (as needed)
Type: python tests.py
Same instructions applies for Windows except that step 1) isn't necessary since
a prebuilt static library is included.
Preliminary benchmarks:
64-bit benchmarks Linux
Python 2.6.6 (r266:84292, Sep 15 2010, 16:22:56)
OS Version: Ubuntu 10.10
System Type: x64-based PC
Processor: Intel(R) Core(TM) i5-2300 CPU @ 2.80GHz
Total Physical Memory: 4096 MB
Array with 256 utf-8 strings:
ujson encode : 2714.66499 calls/sec
simplejson encode : 1542.63718 calls/sec
cjson encode : 132.23604 calls/sec
ujson decode : 2079.17287 calls/sec
cjson decode : 992.21602 calls/sec
simplejson decode : 278.92061 calls/sec
Medium complex object:
ujson encode : 17849.80356 calls/sec
simplejson encode : 3524.32372 calls/sec
cjson encode : 2967.34656 calls/sec
ujson decode : 11685.87610 calls/sec
cjson decode : 8206.67906 calls/sec
simplejson decode : 6549.99750 calls/sec
Array with 256 strings:
ujson encode : 38543.50303 calls/sec
simplejson encode : 19436.45772 calls/sec
cjson encode : 12392.55614 calls/sec
ujson decode : 27207.33157 calls/sec
cjson decode : 30237.60827 calls/sec
simplejson decode : 25271.93073 calls/sec
Array with 256 doubles:
ujson encode : 6027.45931 calls/sec
simplejson encode : 2915.54871 calls/sec
cjson encode : 3546.88804 calls/sec
ujson decode : 28045.13375 calls/sec
cjson decode : 15066.73209 calls/sec
simplejson decode : 15604.98222 calls/sec
Array with 256 True values:
ujson encode : 187342.39634 calls/sec
simplejson encode : 48972.93887 calls/sec
cjson encode : 67274.93082 calls/sec
ujson decode : 158103.79663 calls/sec
cjson decode : 83237.88990 calls/sec
simplejson decode : 115645.98241 calls/sec
Array with 256 dict{string, int} pairs:
ujson encode : 25301.85690 calls/sec
simplejson encode : 5734.29472 calls/sec
cjson encode : 4447.73411 calls/sec
ujson decode : 16290.72288 calls/sec
cjson decode : 12528.56060 calls/sec
simplejson decode : 10394.23358 calls/sec
Dict with 256 arrays with 256 dict{string, int} pairs:
ujson encode : 87.40865 calls/sec
simplejson encode : 17.07889 calls/sec
cjson encode : 17.25164 calls/sec
ujson decode : 45.94026 calls/sec
cjson decode : 34.60225 calls/sec
simplejson decode : 26.92238 calls/sec
32-bit benchmarks Windows
Python 2.6.6 (r266:84297, Aug 24 2010, 18:46:32) [MSC v.1500 32 bit (Intel)]
OS Version: 6.1.7601 Service Pack 1 Build 7601
System Type: x64-based PC
Processor: Intel(R) Core(TM)2 Quad CPU Q9550 @ 2.83GHz 2.83 GHz
Total Physical Memory: 8191 MB
Array with 256 utf-8 strings:
ujson encode : 1191.98175 calls/sec
simplejson encode : 1013.98279 calls/sec
cjson encode : 1040.66063 calls/sec
ujson decode : 1215.66875 calls/sec
cjson decode : 493.30484 calls/sec
simplejson decode : 269.85512 calls/sec
Medium complex object:
ujson encode : 10307.63723 calls/sec
simplejson encode : 2534.94769 calls/sec
cjson encode : 2047.95118 calls/sec
ujson decode : 7274.10026 calls/sec
cjson decode : 3575.39307 calls/sec
simplejson decode : 3565.51252 calls/sec
Array with 256 strings:
ujson encode : 21348.25210 calls/sec
simplejson encode : 15736.74638 calls/sec
cjson encode : 6371.26334 calls/sec
ujson decode : 26050.25316 calls/sec
cjson decode : 16468.88215 calls/sec
simplejson decode : 21115.75770 calls/sec
Array with 256 doubles:
ujson encode : 26975.49110 calls/sec
simplejson encode : 2046.29746 calls/sec
cjson encode : 2133.56594 calls/sec
ujson decode : 28430.33722 calls/sec
cjson decode : 4114.36400 calls/sec
simplejson decode : 4419.08507 calls/sec
Array with 256 True values:
ujson encode : 89846.12897 calls/sec
simplejson encode : 34288.36862 calls/sec
cjson encode : 47168.35849 calls/sec
ujson decode : 99423.47549 calls/sec
cjson decode : 58795.91460 calls/sec
simplejson decode : 76296.14699 calls/sec
Array with 256 dict{string, int} pairs:
ujson encode : 14776.41614 calls/sec
simplejson encode : 3876.86634 calls/sec
cjson encode : 3050.65343 calls/sec
ujson decode : 12934.39432 calls/sec
cjson decode : 7993.04345 calls/sec
simplejson decode : 7152.09475 calls/sec
Here is the benchmark run from a 32bit CentOS 5.6 (Python 2.4) machine:
Array with 256 utf-8 strings:
ujson encode : 1453.30891 calls/sec
simplejson encode : 658.31181 calls/sec
cjson encode : 62.18416 calls/sec
ujson decode : 1016.58767 calls/sec
cjson decode : 455.28550 calls/sec
simplejson decode : 124.20439 calls/sec
Medium complex object:
ujson encode : 6010.21634 calls/sec
simplejson encode : 1418.77823 calls/sec
cjson encode : 1252.92530 calls/sec
ujson decode : 4637.52630 calls/sec
cjson decode : 3444.13604 calls/sec
simplejson decode : 2166.18641 calls/sec
Array with 256 strings:
ujson encode : 12252.28889 calls/sec
simplejson encode : 9351.67532 calls/sec
cjson encode : 7786.13697 calls/sec
ujson decode : 10951.17394 calls/sec
cjson decode : 15971.02425 calls/sec
simplejson decode : 6796.77480 calls/sec
Array with 256 doubles:
ujson encode : 16300.61218 calls/sec
simplejson encode : 1613.39428 calls/sec
cjson encode : 2035.58937 calls/sec
ujson decode : 17301.00746 calls/sec
cjson decode : 5785.33627 calls/sec
simplejson decode : 6199.49364 calls/sec
Array with 256 True values:
ujson encode : 72618.15350 calls/sec
simplejson encode : 18707.57593 calls/sec
cjson encode : 24150.26201 calls/sec
ujson decode : 53650.94162 calls/sec
cjson decode : 48069.53050 calls/sec
simplejson decode : 47098.40293 calls/sec
Array with 256 dict{string, int} pairs:
ujson encode : 8811.85922 calls/sec
simplejson encode : 2756.91262 calls/sec
cjson encode : 1758.26962 calls/sec
ujson decode : 6490.36358 calls/sec
cjson decode : 6330.77263 calls/sec
simplejson decode : 4161.97048 calls/sec
Dict with 256 arrays with 256 dict{string, int} pairs:
ujson encode : 31.08834 calls/sec
simplejson encode : 10.41434 calls/sec
cjson encode : 6.93790 calls/sec
ujson decode : 19.81373 calls/sec
cjson decode : 20.31727 calls/sec
simplejson decode : 15.05690 calls/sec
See (python/benchmark.py) for further information.
NOTE: These benchmarks are preliminary!

View File

@ -1,298 +0,0 @@
/*
Copyright (c) 2011, Jonas Tarnstrom and ESN Social Software AB
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. All advertising materials mentioning features or use of this software
must display the following acknowledgement:
This product includes software developed by ESN Social Software AB (www.esn.me).
4. Neither the name of the ESN Social Software AB nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY ESN SOCIAL SOFTWARE AB ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL ESN SOCIAL SOFTWARE AB BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Portions of code from:
MODP_ASCII - Ascii transformations (upper/lower, etc)
http://code.google.com/p/stringencoders/
Copyright (c) 2007 Nick Galbreath -- nickg [at] modp [dot] com. All rights reserved.
*/
/*
Ultra fast JSON encoder and decoder
Developed by Jonas Tarnstrom (jonas@esn.me).
Encoder notes:
------------------
:: Cyclic references ::
Cyclic referenced objects are not detected.
Set JSONObjectEncoder.recursionMax to suitable value or make sure input object
tree doesn't have cyclic references.
*/
#ifndef __ULTRAJSON_H__
#define __ULTRAJSON_H__
#include <stdio.h>
#include <wchar.h>
//#define JSON_DECODE_NUMERIC_AS_DOUBLE
// Don't output any extra whitespaces when encoding
#define JSON_NO_EXTRA_WHITESPACE
// Max decimals to encode double floating point numbers with
#ifndef JSON_DOUBLE_MAX_DECIMALS
#define JSON_DOUBLE_MAX_DECIMALS 9
#endif
// Max recursion depth, default for encoder
#ifndef JSON_MAX_RECURSION_DEPTH
#define JSON_MAX_RECURSION_DEPTH 256
#endif
/*
Dictates and limits how much stack space for buffers UltraJSON will use before resorting to provided heap functions */
#ifndef JSON_MAX_STACK_BUFFER_SIZE
#define JSON_MAX_STACK_BUFFER_SIZE 131072
#endif
#ifdef _WIN32
typedef __int64 JSINT64;
typedef unsigned __int64 JSUINT64;
typedef unsigned __int32 uint32_t;
typedef __int32 JSINT32;
typedef uint32_t JSUINT32;
typedef unsigned __int8 JSUINT8;
typedef unsigned __int16 JSUTF16;
typedef unsigned __int32 JSUTF32;
typedef __int64 JSLONG;
#define EXPORTFUNCTION __declspec(dllexport)
#define FASTCALL_MSVC __fastcall
#define FASTCALL_ATTR
#define INLINE_PREFIX __inline
#else
#include <sys/types.h>
typedef int64_t JSINT64;
typedef u_int64_t JSUINT64;
typedef int32_t JSINT32;
typedef u_int32_t JSUINT32;
#define FASTCALL_MSVC
#define FASTCALL_ATTR __attribute__((fastcall))
#define INLINE_PREFIX inline
typedef u_int32_t uint32_t;
typedef u_int8_t JSUINT8;
typedef u_int16_t JSUTF16;
typedef u_int32_t JSUTF32;
typedef int64_t JSLONG;
#define EXPORTFUNCTION
#endif
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define __LITTLE_ENDIAN__
#else
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#define __BIG_ENDIAN__
#endif
#endif
#if !defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__)
#error "Endianess not supported"
#endif
enum JSTYPES
{
JT_NULL, // NULL
JT_TRUE, //boolean true
JT_FALSE, //boolean false
JT_INT, //(JSINT32 (signed 32-bit))
JT_LONG, //(JSINT64 (signed 64-bit))
JT_DOUBLE, //(double)
JT_UTF8, //(char 8-bit)
JT_ARRAY, // Array structure
JT_OBJECT, // Key/Value structure
JT_INVALID, // Internal, do not return nor expect
};
typedef void * JSOBJ;
typedef void * JSITER;
typedef struct __JSONTypeContext
{
int type;
void *prv[32];
} JSONTypeContext;
/*
Function pointer declarations, suitable for implementing UltraJSON */
typedef void (*JSPFN_ITERBEGIN)(JSOBJ obj, JSONTypeContext *tc);
typedef int (*JSPFN_ITERNEXT)(JSOBJ obj, JSONTypeContext *tc);
typedef void (*JSPFN_ITEREND)(JSOBJ obj, JSONTypeContext *tc);
typedef JSOBJ (*JSPFN_ITERGETVALUE)(JSOBJ obj, JSONTypeContext *tc);
typedef char *(*JSPFN_ITERGETNAME)(JSOBJ obj, JSONTypeContext *tc, size_t *outLen);
typedef void *(*JSPFN_MALLOC)(size_t size);
typedef void (*JSPFN_FREE)(void *pptr);
typedef void *(*JSPFN_REALLOC)(void *base, size_t size);
typedef struct __JSONObjectEncoder
{
void (*beginTypeContext)(JSOBJ obj, JSONTypeContext *tc);
void (*endTypeContext)(JSOBJ obj, JSONTypeContext *tc);
const char *(*getStringValue)(JSOBJ obj, JSONTypeContext *tc, size_t *_outLen);
JSINT64 (*getLongValue)(JSOBJ obj, JSONTypeContext *tc);
JSINT32 (*getIntValue)(JSOBJ obj, JSONTypeContext *tc);
double (*getDoubleValue)(JSOBJ obj, JSONTypeContext *tc);
/*
Begin iteration of an iteratable object (JS_ARRAY or JS_OBJECT)
Implementor should setup iteration state in ti->prv
*/
JSPFN_ITERBEGIN iterBegin;
/*
Retrieve next object in an iteration. Should return 0 to indicate iteration has reached end or 1 if there are more items.
Implementor is responsible for keeping state of the iteration. Use ti->prv fields for this
*/
JSPFN_ITERNEXT iterNext;
/*
Ends the iteration of an iteratable object.
Any iteration state stored in ti->prv can be freed here
*/
JSPFN_ITEREND iterEnd;
/*
Returns a reference to the value object of an iterator
The is responsible for the life-cycle of the returned string. Use iterNext/iterEnd and ti->prv to keep track of current object
*/
JSPFN_ITERGETVALUE iterGetValue;
/*
Return name of iterator.
The is responsible for the life-cycle of the returned string. Use iterNext/iterEnd and ti->prv to keep track of current object
*/
JSPFN_ITERGETNAME iterGetName;
/*
Release a value as indicated by setting ti->release = 1 in the previous getValue call.
The ti->prv array should contain the necessary context to release the value
*/
void (*releaseObject)(JSOBJ obj);
/* Library functions
Set to NULL to use STDLIB malloc,realloc,free */
JSPFN_MALLOC malloc;
JSPFN_REALLOC realloc;
JSPFN_FREE free;
/*
Configuration for max recursion, set to 0 to use default (see JSON_MAX_RECURSION_DEPTH)*/
int recursionMax;
/*
Configuration for max decimals of double floating poiunt numbers to encode (0-9) */
int doublePrecision;
/*
If true output will be ASCII with all characters above 127 encoded as \uXXXX. If false output will be UTF-8 or what ever charset strings are brought as */
int forceASCII;
/*
Set to an error message if error occured */
const char *errorMsg;
JSOBJ errorObj;
/* Buffer stuff */
char *start;
char *offset;
char *end;
int heap;
int level;
} JSONObjectEncoder;
/*
Encode an object structure into JSON.
Arguments:
obj - An anonymous type representing the object
enc - Function definitions for querying JSOBJ type
buffer - Preallocated buffer to store result in. If NULL function allocates own buffer
cbBuffer - Length of buffer (ignored if buffer is NULL)
Returns:
Encoded JSON object as a null terminated char string.
NOTE:
If the supplied buffer wasn't enough to hold the result the function will allocate a new buffer.
Life cycle of the provided buffer must still be handled by caller.
If the return value doesn't equal the specified buffer caller must release the memory using
JSONObjectEncoder.free or free() as specified when calling this function.
*/
EXPORTFUNCTION char *JSON_EncodeObject(JSOBJ obj, JSONObjectEncoder *enc, char *buffer, size_t cbBuffer);
typedef struct __JSONObjectDecoder
{
JSOBJ (*newString)(wchar_t *start, wchar_t *end);
void (*objectAddKey)(JSOBJ obj, JSOBJ name, JSOBJ value);
void (*arrayAddItem)(JSOBJ obj, JSOBJ value);
JSOBJ (*newTrue)(void);
JSOBJ (*newFalse)(void);
JSOBJ (*newNull)(void);
JSOBJ (*newObject)(void);
JSOBJ (*newArray)(void);
JSOBJ (*newInt)(JSINT32 value);
JSOBJ (*newLong)(JSINT64 value);
JSOBJ (*newDouble)(double value);
void (*releaseObject)(JSOBJ obj);
JSPFN_MALLOC malloc;
JSPFN_FREE free;
JSPFN_REALLOC realloc;
char *errorStr;
char *errorOffset;
} JSONObjectDecoder;
EXPORTFUNCTION JSOBJ JSON_DecodeObject(JSONObjectDecoder *dec, const char *buffer, size_t cbBuffer);
#endif

View File

@ -1,816 +0,0 @@
/*
Copyright (c) 2011, Jonas Tarnstrom and ESN Social Software AB
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. All advertising materials mentioning features or use of this software
must display the following acknowledgement:
This product includes software developed by ESN Social Software AB (www.esn.me).
4. Neither the name of the ESN Social Software AB nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY ESN SOCIAL SOFTWARE AB ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL ESN SOCIAL SOFTWARE AB BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Portions of code from:
MODP_ASCII - Ascii transformations (upper/lower, etc)
http://code.google.com/p/stringencoders/
Copyright (c) 2007 Nick Galbreath -- nickg [at] modp [dot] com. All rights reserved.
*/
#include "ultrajson.h"
#include <math.h>
#include <assert.h>
#include <string.h>
#include <limits.h>
#include <wchar.h>
struct DecoderState
{
char *start;
char *end;
wchar_t *escStart;
wchar_t *escEnd;
int escHeap;
int lastType;
JSONObjectDecoder *dec;
};
JSOBJ FASTCALL_MSVC decode_any( struct DecoderState *ds) FASTCALL_ATTR;
typedef JSOBJ (*PFN_DECODER)( struct DecoderState *ds);
#define RETURN_JSOBJ_NULLCHECK(_expr) return(_expr);
double createDouble(double intNeg, double intValue, double frcValue, int frcDecimalCount)
{
static const double g_pow10[] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000};
return (intValue + (frcValue / g_pow10[frcDecimalCount])) * intNeg;
}
static JSOBJ SetError( struct DecoderState *ds, int offset, const char *message)
{
ds->dec->errorOffset = ds->start + offset;
ds->dec->errorStr = (char *) message;
return NULL;
}
FASTCALL_ATTR JSOBJ FASTCALL_MSVC decode_numeric ( struct DecoderState *ds)
{
#ifdef JSON_DECODE_NUMERIC_AS_DOUBLE
double intNeg = 1;
double intValue;
#else
int intNeg = 1;
JSLONG intValue;
#endif
double expNeg;
int chr;
int decimalCount = 0;
double frcValue = 0.0;
double expValue;
if (*(ds->start) == '-')
{
ds->start ++;
intNeg = -1;
}
// Scan integer part
intValue = 0;
while (1)
{
chr = (int) (unsigned char) *(ds->start);
switch (chr)
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
//FIXME: Check for arithemtic overflow here
//PERF: Don't do 64-bit arithmetic here unless we know we have to
#ifdef JSON_DECODE_NUMERIC_AS_DOUBLE
intValue = intValue * 10.0 + (double) (chr - 48);
#else
intValue = intValue * 10LL + (JSLONG) (chr - 48);
#endif
ds->start ++;
break;
case '.':
ds->start ++;
goto DECODE_FRACTION;
break;
case 'e':
case 'E':
ds->start ++;
goto DECODE_EXPONENT;
break;
default:
goto BREAK_INT_LOOP;
break;
}
}
BREAK_INT_LOOP:
ds->lastType = JT_INT;
//If input string is LONGLONG_MIN here the value is already negative so we should not flip it
#ifdef JSON_DECODE_NUMERIC_AS_DOUBLE
#else
if (intValue < 0)
{
intNeg = 1;
}
#endif
//dbg1 = (intValue * intNeg);
//dbg2 = (JSLONG) dbg1;
#ifdef JSON_DECODE_NUMERIC_AS_DOUBLE
if (intValue > (double) INT_MAX || intValue < (double) INT_MIN)
#else
if ( (intValue >> 32))
#endif
{
RETURN_JSOBJ_NULLCHECK(ds->dec->newLong( (JSINT64) (intValue * (JSINT64) intNeg)));
}
else
{
RETURN_JSOBJ_NULLCHECK(ds->dec->newInt( (JSINT32) (intValue * intNeg)));
}
DECODE_FRACTION:
// Scan fraction part
frcValue = 0.0;
while (1)
{
chr = (int) (unsigned char) *(ds->start);
switch (chr)
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
if (decimalCount < JSON_DOUBLE_MAX_DECIMALS)
{
frcValue = frcValue * 10.0 + (double) (chr - 48);
decimalCount ++;
}
ds->start ++;
break;
case 'e':
case 'E':
ds->start ++;
goto DECODE_EXPONENT;
break;
default:
goto BREAK_FRC_LOOP;
}
}
BREAK_FRC_LOOP:
if (intValue < 0)
{
intNeg = 1;
}
//FIXME: Check for arithemtic overflow here
ds->lastType = JT_DOUBLE;
RETURN_JSOBJ_NULLCHECK(ds->dec->newDouble (createDouble( (double) intNeg, (double) intValue, frcValue, decimalCount)));
DECODE_EXPONENT:
expNeg = 1.0;
if (*(ds->start) == '-')
{
expNeg = -1.0;
ds->start ++;
}
else
if (*(ds->start) == '+')
{
expNeg = +1.0;
ds->start ++;
}
expValue = 0.0;
while (1)
{
chr = (int) (unsigned char) *(ds->start);
switch (chr)
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
expValue = expValue * 10.0 + (double) (chr - 48);
ds->start ++;
break;
default:
goto BREAK_EXP_LOOP;
}
}
BREAK_EXP_LOOP:
#ifdef JSON_DECODE_NUMERIC_AS_DOUBLE
#else
if (intValue < 0)
{
intNeg = 1;
}
#endif
//FIXME: Check for arithemtic overflow here
ds->lastType = JT_DOUBLE;
RETURN_JSOBJ_NULLCHECK(ds->dec->newDouble (createDouble( (double) intNeg, (double) intValue , frcValue, decimalCount) * pow(10.0, expValue * expNeg)));
}
FASTCALL_ATTR JSOBJ FASTCALL_MSVC decode_true ( struct DecoderState *ds)
{
ds->start ++;
if (*(ds->start++) != 'r')
goto SETERROR;
if (*(ds->start++) != 'u')
goto SETERROR;
if (*(ds->start++) != 'e')
goto SETERROR;
ds->lastType = JT_TRUE;
RETURN_JSOBJ_NULLCHECK(ds->dec->newTrue());
SETERROR:
return SetError(ds, -1, "Unexpected character found when decoding 'true'");
}
FASTCALL_ATTR JSOBJ FASTCALL_MSVC decode_false ( struct DecoderState *ds)
{
ds->start ++;
if (*(ds->start++) != 'a')
goto SETERROR;
if (*(ds->start++) != 'l')
goto SETERROR;
if (*(ds->start++) != 's')
goto SETERROR;
if (*(ds->start++) != 'e')
goto SETERROR;
ds->lastType = JT_FALSE;
RETURN_JSOBJ_NULLCHECK(ds->dec->newFalse());
SETERROR:
return SetError(ds, -1, "Unexpected character found when decoding 'false'");
}
FASTCALL_ATTR JSOBJ FASTCALL_MSVC decode_null ( struct DecoderState *ds)
{
ds->start ++;
if (*(ds->start++) != 'u')
goto SETERROR;
if (*(ds->start++) != 'l')
goto SETERROR;
if (*(ds->start++) != 'l')
goto SETERROR;
ds->lastType = JT_NULL;
RETURN_JSOBJ_NULLCHECK(ds->dec->newNull());
SETERROR:
return SetError(ds, -1, "Unexpected character found when decoding 'null'");
}
FASTCALL_ATTR void FASTCALL_MSVC SkipWhitespace(struct DecoderState *ds)
{
while (1)
{
switch (*ds->start)
{
case ' ':
case '\t':
case '\r':
case '\n':
ds->start ++;
break;
default:
return;
}
}
}
enum DECODESTRINGSTATE
{
DS_ISNULL = 0x32,
DS_ISQUOTE,
DS_ISESCAPE,
DS_UTFLENERROR,
};
static const JSUINT8 g_decoderLookup[256] =
{
/* 0x00 */ DS_ISNULL, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
/* 0x10 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
/* 0x20 */ 1, 1, DS_ISQUOTE, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
/* 0x30 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
/* 0x40 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
/* 0x50 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, DS_ISESCAPE, 1, 1, 1,
/* 0x60 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
/* 0x70 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
/* 0x80 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
/* 0x90 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
/* 0xa0 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
/* 0xb0 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
/* 0xc0 */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
/* 0xd0 */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
/* 0xe0 */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
/* 0xf0 */ 4, 4, 4, 4, 4, 4, 4, 4, DS_UTFLENERROR, DS_UTFLENERROR, DS_UTFLENERROR, DS_UTFLENERROR, DS_UTFLENERROR, DS_UTFLENERROR, DS_UTFLENERROR, DS_UTFLENERROR,
};
FASTCALL_ATTR JSOBJ FASTCALL_MSVC decode_string ( struct DecoderState *ds)
{
JSUTF16 sur[2] = { 0 };
int iSur = 0;
int index;
wchar_t *escOffset;
size_t escLen = (ds->escEnd - ds->escStart);
JSUINT8 *inputOffset;
JSUINT8 oct;
JSUTF32 ucs;
ds->lastType = JT_INVALID;
ds->start ++;
if ( (ds->end - ds->start) > escLen)
{
size_t newSize = (ds->end - ds->start);
if (ds->escHeap)
{
ds->escStart = (wchar_t *) ds->dec->realloc (ds->escStart, newSize * sizeof(wchar_t));
}
else
{
wchar_t *oldStart = ds->escStart;
ds->escHeap = 1;
ds->escStart = (wchar_t *) ds->dec->malloc (newSize * sizeof(wchar_t));
memcpy (ds->escStart, oldStart, escLen * sizeof(wchar_t));
}
ds->escEnd = ds->escStart + newSize;
}
escOffset = ds->escStart;
//inputOffset = ds->start;
inputOffset = (JSUINT8*)ds->start; // miloyip
while(1)
{
switch (g_decoderLookup[(JSUINT8)(*inputOffset)])
{
case DS_ISNULL:
return SetError(ds, -1, "Unmatched ''\"' when when decoding 'string'");
case DS_ISQUOTE:
ds->lastType = JT_UTF8;
inputOffset ++;
ds->start += ( (char *) inputOffset - (ds->start));
RETURN_JSOBJ_NULLCHECK(ds->dec->newString(ds->escStart, escOffset));
case DS_UTFLENERROR:
return SetError (ds, -1, "Invalid UTF-8 sequence length when decoding 'string'");
case DS_ISESCAPE:
inputOffset ++;
switch (*inputOffset)
{
case '\\': *(escOffset++) = L'\\'; inputOffset++; continue;
case '\"': *(escOffset++) = L'\"'; inputOffset++; continue;
case '/': *(escOffset++) = L'/'; inputOffset++; continue;
case 'b': *(escOffset++) = L'\b'; inputOffset++; continue;
case 'f': *(escOffset++) = L'\f'; inputOffset++; continue;
case 'n': *(escOffset++) = L'\n'; inputOffset++; continue;
case 'r': *(escOffset++) = L'\r'; inputOffset++; continue;
case 't': *(escOffset++) = L'\t'; inputOffset++; continue;
case 'u':
{
int index;
inputOffset ++;
for (index = 0; index < 4; index ++)
{
switch (*inputOffset)
{
case '\0': return SetError (ds, -1, "Unterminated unicode escape sequence when decoding 'string'");
default: return SetError (ds, -1, "Unexpected character in unicode escape sequence when decoding 'string'");
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
sur[iSur] = (sur[iSur] << 4) + (JSUTF16) (*inputOffset - '0');
break;
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
sur[iSur] = (sur[iSur] << 4) + 10 + (JSUTF16) (*inputOffset - 'a');
break;
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
sur[iSur] = (sur[iSur] << 4) + 10 + (JSUTF16) (*inputOffset - 'A');
break;
}
inputOffset ++;
}
if (iSur == 0)
{
if((sur[iSur] & 0xfc00) == 0xd800)
{
// First of a surrogate pair, continue parsing
iSur ++;
break;
}
(*escOffset++) = (wchar_t) sur[iSur];
iSur = 0;
}
else
{
// Decode pair
if ((sur[1] & 0xfc00) != 0xdc00)
{
return SetError (ds, -1, "Unpaired high surrogate when decoding 'string'");
}
#if WCHAR_MAX == 0xffff
(*escOffset++) = (wchar_t) sur[0];
(*escOffset++) = (wchar_t) sur[1];
#else
(*escOffset++) = (wchar_t) 0x10000 + (((sur[0] - 0xd800) << 10) | (sur[1] - 0xdc00));
#endif
iSur = 0;
}
break;
}
case '\0': return SetError(ds, -1, "Unterminated escape sequence when decoding 'string'");
default: return SetError(ds, -1, "Unrecognized escape sequence when decoding 'string'");
}
break;
case 1:
*(escOffset++) = (wchar_t) (*inputOffset++);
break;
case 2:
{
ucs = (*inputOffset++) & 0x1f;
ucs <<= 6;
if (((*inputOffset) & 0x80) != 0x80)
{
return SetError(ds, -1, "Invalid octet in UTF-8 sequence when decoding 'string'");
}
ucs |= (*inputOffset++) & 0x3f;
if (ucs < 0x80) return SetError (ds, -1, "Overlong 2 byte UTF-8 sequence detected when decoding 'string'");
*(escOffset++) = (wchar_t) ucs;
break;
}
case 3:
{
JSUTF32 ucs = 0;
ucs |= (*inputOffset++) & 0x0f;
for (index = 0; index < 2; index ++)
{
ucs <<= 6;
oct = (*inputOffset++);
if ((oct & 0x80) != 0x80)
{
return SetError(ds, -1, "Invalid octet in UTF-8 sequence when decoding 'string'");
}
ucs |= oct & 0x3f;
}
if (ucs < 0x800) return SetError (ds, -1, "Overlong 3 byte UTF-8 sequence detected when encoding string");
*(escOffset++) = (wchar_t) ucs;
break;
}
case 4:
{
JSUTF32 ucs = 0;
ucs |= (*inputOffset++) & 0x07;
for (index = 0; index < 3; index ++)
{
ucs <<= 6;
oct = (*inputOffset++);
if ((oct & 0x80) != 0x80)
{
return SetError(ds, -1, "Invalid octet in UTF-8 sequence when decoding 'string'");
}
ucs |= oct & 0x3f;
}
if (ucs < 0x10000) return SetError (ds, -1, "Overlong 4 byte UTF-8 sequence detected when decoding 'string'");
#if WCHAR_MAX == 0xffff
if (ucs >= 0x10000)
{
ucs -= 0x10000;
*(escOffset++) = (ucs >> 10) + 0xd800;
*(escOffset++) = (ucs & 0x3ff) + 0xdc00;
}
else
{
*(escOffset++) = (wchar_t) ucs;
}
#else
*(escOffset++) = (wchar_t) ucs;
#endif
break;
}
}
}
}
FASTCALL_ATTR JSOBJ FASTCALL_MSVC decode_array( struct DecoderState *ds)
{
JSOBJ itemValue;
JSOBJ newObj = ds->dec->newArray();
ds->lastType = JT_INVALID;
ds->start ++;
while (1)//(*ds->start) != '\0')
{
SkipWhitespace(ds);
if ((*ds->start) == ']')
{
*ds->start ++;
return newObj;
}
itemValue = decode_any(ds);
if (itemValue == NULL)
{
ds->dec->releaseObject(newObj);
return NULL;
}
ds->dec->arrayAddItem (newObj, itemValue);
SkipWhitespace(ds);
switch (*(ds->start++))
{
case ']':
return newObj;
case ',':
break;
default:
ds->dec->releaseObject(newObj);
return SetError(ds, -1, "Unexpected character in found when decoding array value");
}
}
ds->dec->releaseObject(newObj);
return SetError(ds, -1, "Unmatched ']' when decoding 'array'");
}
FASTCALL_ATTR JSOBJ FASTCALL_MSVC decode_object( struct DecoderState *ds)
{
JSOBJ itemName;
JSOBJ itemValue;
JSOBJ newObj = ds->dec->newObject();
ds->start ++;
while (1)
{
SkipWhitespace(ds);
if ((*ds->start) == '}')
{
ds->start ++;
return newObj;
}
ds->lastType = JT_INVALID;
itemName = decode_any(ds);
if (itemName == NULL)
{
ds->dec->releaseObject(newObj);
return NULL;
}
if (ds->lastType != JT_UTF8)
{
ds->dec->releaseObject(newObj);
ds->dec->releaseObject(itemName);
return SetError(ds, -1, "Key name of object must be 'string' when decoding 'object'");
}
SkipWhitespace(ds);
if (*(ds->start++) != ':')
{
ds->dec->releaseObject(newObj);
ds->dec->releaseObject(itemName);
return SetError(ds, -1, "No ':' found when decoding object value");
}
SkipWhitespace(ds);
itemValue = decode_any(ds);
if (itemValue == NULL)
{
ds->dec->releaseObject(newObj);
ds->dec->releaseObject(itemName);
return NULL;
}
ds->dec->objectAddKey (newObj, itemName, itemValue);
SkipWhitespace(ds);
switch (*(ds->start++))
{
case '}':
return newObj;
case ',':
break;
default:
ds->dec->releaseObject(newObj);
return SetError(ds, -1, "Unexpected character in found when decoding object value");
}
}
ds->dec->releaseObject(newObj);
return SetError(ds, -1, "Unmatched '}' when decoding object");
}
FASTCALL_ATTR JSOBJ FASTCALL_MSVC decode_any(struct DecoderState *ds)
{
while (1)
{
switch (*ds->start)
{
case '\"':
return decode_string (ds);
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '-':
return decode_numeric (ds);
case '[': return decode_array (ds);
case '{': return decode_object (ds);
case 't': return decode_true (ds);
case 'f': return decode_false (ds);
case 'n': return decode_null (ds);
case ' ':
case '\t':
case '\r':
case '\n':
// White space
ds->start ++;
break;
default:
return SetError(ds, -1, "Expected object or value");
}
}
}
JSOBJ JSON_DecodeObject(JSONObjectDecoder *dec, const char *buffer, size_t cbBuffer)
{
/*
FIXME: Base the size of escBuffer of that of cbBuffer so that the unicode escaping doesn't run into the wall each time */
struct DecoderState ds;
wchar_t escBuffer[(JSON_MAX_STACK_BUFFER_SIZE / sizeof(wchar_t))];
JSOBJ ret;
ds.start = (char *) buffer;
ds.end = ds.start + cbBuffer;
ds.escStart = escBuffer;
ds.escEnd = ds.escStart + (JSON_MAX_STACK_BUFFER_SIZE / sizeof(wchar_t));
ds.escHeap = 0;
ds.dec = dec;
ds.dec->errorStr = NULL;
ds.dec->errorOffset = NULL;
ds.dec = dec;
ret = decode_any (&ds);
if (ds.escHeap)
{
dec->free(ds.escStart);
}
return ret;
}

View File

@ -1,830 +0,0 @@
/*
Copyright (c) 2011, Jonas Tarnstrom and ESN Social Software AB
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. All advertising materials mentioning features or use of this software
must display the following acknowledgement:
This product includes software developed by ESN Social Software AB (www.esn.me).
4. Neither the name of the ESN Social Software AB nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY ESN SOCIAL SOFTWARE AB ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL ESN SOCIAL SOFTWARE AB BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Portions of code from:
MODP_ASCII - Ascii transformations (upper/lower, etc)
http://code.google.com/p/stringencoders/
Copyright (c) 2007 Nick Galbreath -- nickg [at] modp [dot] com. All rights reserved.
*/
#include "ultrajson.h"
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <float.h>
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
static const double g_pow10[] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000};
static const char g_hexChars[] = "0123456789abcdef";
static const char g_escapeChars[] = "0123456789\\b\\t\\n\\f\\r\\\"\\\\\\/";
/*
FIXME: While this is fine dandy and working it's a magic value mess which probably only the author understands.
Needs a cleanup and more documentation */
/*
Table for pure ascii output escaping all characters above 127 to \uXXXX */
static const JSUINT8 g_asciiOutputTable[256] =
{
/* 0x00 */ 0, 30, 30, 30, 30, 30, 30, 30, 10, 12, 14, 30, 16, 18, 30, 30,
/* 0x10 */ 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
/* 0x20 */ 1, 1, 20, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1/*24*/,
/* 0x30 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
/* 0x40 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
/* 0x50 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 22, 1, 1, 1,
/* 0x60 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
/* 0x70 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
/* 0x80 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
/* 0x90 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
/* 0xa0 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
/* 0xb0 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
/* 0xc0 */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
/* 0xd0 */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
/* 0xe0 */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
/* 0xf0 */ 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 1, 1
};
static void SetError (JSOBJ obj, JSONObjectEncoder *enc, const char *message)
{
enc->errorMsg = message;
enc->errorObj = obj;
}
/*
FIXME: Keep track of how big these get across several encoder calls and try to make an estimate
That way we won't run our head into the wall each call */
void Buffer_Realloc (JSONObjectEncoder *enc, size_t cbNeeded)
{
size_t curSize = enc->end - enc->start;
size_t newSize = curSize * 2;
size_t offset = enc->offset - enc->start;
while (newSize < curSize + cbNeeded)
{
newSize *= 2;
}
if (enc->heap)
{
enc->start = (char *) enc->realloc (enc->start, newSize);
}
else
{
char *oldStart = enc->start;
enc->heap = 1;
enc->start = (char *) enc->malloc (newSize);
memcpy (enc->start, oldStart, offset);
}
enc->offset = enc->start + offset;
enc->end = enc->start + newSize;
}
FASTCALL_ATTR INLINE_PREFIX void FASTCALL_MSVC Buffer_AppendShortHexUnchecked (char *outputOffset, unsigned short value)
{
*(outputOffset++) = g_hexChars[(value & 0xf000) >> 12];
*(outputOffset++) = g_hexChars[(value & 0x0f00) >> 8];
*(outputOffset++) = g_hexChars[(value & 0x00f0) >> 4];
*(outputOffset++) = g_hexChars[(value & 0x000f) >> 0];
}
int Buffer_EscapeStringUnvalidated (JSOBJ obj, JSONObjectEncoder *enc, const char *io, const char *end)
{
char *of = (char *) enc->offset;
while (1)
{
switch (*io)
{
case 0x00:
enc->offset += (of - enc->offset);
return TRUE;
case '\"': (*of++) = '\\'; (*of++) = '\"'; break;
case '\\': (*of++) = '\\'; (*of++) = '\\'; break;
//case '/': (*of++) = '\\'; (*of++) = '/'; break;
case '\b': (*of++) = '\\'; (*of++) = 'b'; break;
case '\f': (*of++) = '\\'; (*of++) = 'f'; break;
case '\n': (*of++) = '\\'; (*of++) = 'n'; break;
case '\r': (*of++) = '\\'; (*of++) = 'r'; break;
case '\t': (*of++) = '\\'; (*of++) = 't'; break;
case 0x01:
case 0x02:
case 0x03:
case 0x04:
case 0x05:
case 0x06:
case 0x07:
case 0x0b:
case 0x0e:
case 0x0f:
case 0x10:
case 0x11:
case 0x12:
case 0x13:
case 0x14:
case 0x15:
case 0x16:
case 0x17:
case 0x18:
case 0x19:
case 0x1a:
case 0x1b:
case 0x1c:
case 0x1d:
case 0x1e:
case 0x1f:
*(of++) = '\\';
*(of++) = 'u';
*(of++) = '0';
*(of++) = '0';
*(of++) = g_hexChars[ (unsigned char) (((*io) & 0xf0) >> 4)];
*(of++) = g_hexChars[ (unsigned char) ((*io) & 0x0f)];
break;
default: (*of++) = (*io); break;
}
*io++;
}
return FALSE;
}
/*
FIXME:
This code only works with Little and Big Endian
FIXME: The JSON spec says escape "/" but non of the others do and we don't
want to be left alone doing it so we don't :)
*/
int Buffer_EscapeStringValidated (JSOBJ obj, JSONObjectEncoder *enc, const char *io, const char *end)
{
JSUTF32 ucs;
char *of = (char *) enc->offset;
while (1)
{
//JSUINT8 chr = (unsigned char) *io;
JSUINT8 utflen = g_asciiOutputTable[(unsigned char) *io];
switch (utflen)
{
case 0:
{
enc->offset += (of - enc->offset);
return TRUE;
}
case 1:
{
*(of++)= (*io++);
continue;
}
case 2:
{
JSUTF32 in;
if (io + 1 > end)
{
enc->offset += (of - enc->offset);
SetError (obj, enc, "Unterminated UTF-8 sequence when encoding string");
return FALSE;
}
in = *((JSUTF16 *) io);
#ifdef __LITTLE_ENDIAN__
ucs = ((in & 0x1f) << 6) | ((in >> 8) & 0x3f);
#else
ucs = ((in & 0x1f00) >> 2) | (in & 0x3f);
#endif
if (ucs < 0x80)
{
enc->offset += (of - enc->offset);
SetError (obj, enc, "Overlong 2 byte UTF-8 sequence detected when encoding string");
return FALSE;
}
io += 2;
break;
}
case 3:
{
JSUTF32 in;
if (io + 2 > end)
{
enc->offset += (of - enc->offset);
SetError (obj, enc, "Unterminated UTF-8 sequence when encoding string");
return FALSE;
}
#ifdef __LITTLE_ENDIAN__
in = *((JSUTF16 *) io);
in |= *((JSUINT8 *) io + 2) << 16;
ucs = ((in & 0x0f) << 12) | ((in & 0x3f00) >> 2) | ((in & 0x3f0000) >> 16);
#else
in = *((JSUTF16 *) io) << 8;
in |= *((JSUINT8 *) io + 2);
ucs = ((in & 0x0f0000) >> 4) | ((in & 0x3f00) >> 2) | (in & 0x3f);
#endif
if (ucs < 0x800)
{
enc->offset += (of - enc->offset);
SetError (obj, enc, "Overlong 3 byte UTF-8 sequence detected when encoding string");
return FALSE;
}
io += 3;
break;
}
case 4:
{
JSUTF32 in;
if (io + 3 > end)
{
enc->offset += (of - enc->offset);
SetError (obj, enc, "Unterminated UTF-8 sequence when encoding string");
return FALSE;
}
#ifdef __LITTLE_ENDIAN__
in = *((JSUTF32 *) io);
ucs = ((in & 0x07) << 18) | ((in & 0x3f00) << 4) | ((in & 0x3f0000) >> 10) | ((in & 0x3f000000) >> 24);
#else
in = *((JSUTF32 *) io);
ucs = ((in & 0x07000000) >> 6) | ((in & 0x3f0000) >> 4) | ((in & 0x3f00) >> 2) | (in & 0x3f);
#endif
if (ucs < 0x10000)
{
enc->offset += (of - enc->offset);
SetError (obj, enc, "Overlong 4 byte UTF-8 sequence detected when encoding string");
return FALSE;
}
io += 4;
break;
}
case 5:
case 6:
enc->offset += (of - enc->offset);
SetError (obj, enc, "Unsupported UTF-8 sequence length when encoding string");
return FALSE;
case 30:
// \uXXXX encode
*(of++) = '\\';
*(of++) = 'u';
*(of++) = '0';
*(of++) = '0';
*(of++) = g_hexChars[ (unsigned char) (((*io) & 0xf0) >> 4)];
*(of++) = g_hexChars[ (unsigned char) ((*io) & 0x0f)];
io ++;
continue;
case 10:
case 12:
case 14:
case 16:
case 18:
case 20:
case 22:
//case 24: (enable for / escaping)
*(of++) = *( (char *) (g_escapeChars + utflen + 0));
*(of++) = *( (char *) (g_escapeChars + utflen + 1));
io ++;
continue;
}
/*
If the character is a UTF8 sequence of length > 1 we end up here */
if (ucs >= 0x10000)
{
ucs -= 0x10000;
*(of++) = '\\';
*(of++) = 'u';
Buffer_AppendShortHexUnchecked(of, (ucs >> 10) + 0xd800);
of += 4;
*(of++) = '\\';
*(of++) = 'u';
Buffer_AppendShortHexUnchecked(of, (ucs & 0x3ff) + 0xdc00);
of += 4;
}
else
{
*(of++) = '\\';
*(of++) = 'u';
Buffer_AppendShortHexUnchecked(of, ucs);
of += 4;
}
}
return FALSE;
}
#define Buffer_Reserve(__enc, __len) \
if ((__enc)->offset + (__len) > (__enc)->end) \
{ \
Buffer_Realloc((__enc), (__len));\
} \
#define Buffer_AppendCharUnchecked(__enc, __chr) \
*((__enc)->offset++) = __chr; \
FASTCALL_ATTR INLINE_PREFIX void FASTCALL_MSVC strreverse(char* begin, char* end)
{
char aux;
while (end > begin)
aux = *end, *end-- = *begin, *begin++ = aux;
}
void Buffer_AppendIntUnchecked(JSONObjectEncoder *enc, JSINT32 value)
{
char* wstr;
JSUINT32 uvalue = (value < 0) ? -value : value;
wstr = enc->offset;
// Conversion. Number is reversed.
do *wstr++ = (char)(48 + (uvalue % 10)); while(uvalue /= 10);
if (value < 0) *wstr++ = '-';
// Reverse string
strreverse(enc->offset,wstr - 1);
enc->offset += (wstr - (enc->offset));
}
void Buffer_AppendLongUnchecked(JSONObjectEncoder *enc, JSINT64 value)
{
char* wstr;
JSUINT64 uvalue = (value < 0) ? -value : value;
wstr = enc->offset;
// Conversion. Number is reversed.
do *wstr++ = (char)(48 + (uvalue % 10ULL)); while(uvalue /= 10ULL);
if (value < 0) *wstr++ = '-';
// Reverse string
strreverse(enc->offset,wstr - 1);
enc->offset += (wstr - (enc->offset));
}
int Buffer_AppendDoubleUnchecked(JSOBJ obj, JSONObjectEncoder *enc, double value)
{
/* if input is larger than thres_max, revert to exponential */
const double thres_max = (double)(0x7FFFFFFF);
int count;
double diff = 0.0;
char* str = enc->offset;
char* wstr = str;
int whole;
double tmp;
uint32_t frac;
int neg;
double pow10;
if (value == HUGE_VAL || value == -HUGE_VAL)
{
SetError (obj, enc, "Invalid Inf value when encoding double");
return FALSE;
}
if (! (value == value))
{
SetError (obj, enc, "Invalid Nan value when encoding double");
return FALSE;
}
/* we'll work in positive values and deal with the
negative sign issue later */
neg = 0;
if (value < 0)
{
neg = 1;
value = -value;
}
pow10 = g_pow10[enc->doublePrecision];
whole = (int) value;
tmp = (value - whole) * pow10;
frac = (uint32_t)(tmp);
diff = tmp - frac;
if (diff > 0.5)
{
++frac;
/* handle rollover, e.g. case 0.99 with prec 1 is 1.0 */
if (frac >= pow10)
{
frac = 0;
++whole;
}
}
else
if (diff == 0.5 && ((frac == 0) || (frac & 1)))
{
/* if halfway, round up if odd, OR
if last digit is 0. That last part is strange */
++frac;
}
/* for very large numbers switch back to native sprintf for exponentials.
anyone want to write code to replace this? */
/*
normal printf behavior is to print EVERY whole number digit
which can be 100s of characters overflowing your buffers == bad
*/
if (value > thres_max)
{
enc->offset += sprintf(str, "%e", neg ? -value : value);
return TRUE;
}
if (enc->doublePrecision == 0)
{
diff = value - whole;
if (diff > 0.5)
{
/* greater than 0.5, round up, e.g. 1.6 -> 2 */
++whole;
}
else
if (diff == 0.5 && (whole & 1))
{
/* exactly 0.5 and ODD, then round up */
/* 1.5 -> 2, but 2.5 -> 2 */
++whole;
}
//vvvvvvvvvvvvvvvvvvv Diff from modp_dto2
}
else
if (frac)
{
count = enc->doublePrecision;
// now do fractional part, as an unsigned number
// we know it is not 0 but we can have leading zeros, these
// should be removed
while (!(frac % 10))
{
--count;
frac /= 10;
}
//^^^^^^^^^^^^^^^^^^^ Diff from modp_dto2
// now do fractional part, as an unsigned number
do
{
--count;
*wstr++ = (char)(48 + (frac % 10));
} while (frac /= 10);
// add extra 0s
while (count-- > 0)
{
*wstr++ = '0';
}
// add decimal
*wstr++ = '.';
}
else
{
*wstr++ = '0';
*wstr++ = '.';
}
// do whole part
// Take care of sign
// Conversion. Number is reversed.
do *wstr++ = (char)(48 + (whole % 10)); while (whole /= 10);
if (neg)
{
*wstr++ = '-';
}
strreverse(str, wstr-1);
enc->offset += (wstr - (enc->offset));
return TRUE;
}
/*
FIXME:
Handle integration functions returning NULL here */
/*
FIXME:
Perhaps implement recursion detection */
void encode(JSOBJ obj, JSONObjectEncoder *enc, const char *name, size_t cbName)
{
JSONTypeContext tc;
size_t szlen;
if (enc->level > enc->recursionMax)
{
SetError (obj, enc, "Maximum recursion level reached");
return;
}
/*
This reservation must hold
length of _name as encoded worst case +
maxLength of double to string OR maxLength of JSLONG to string
Since input is assumed to be UTF-8 the worst character length is:
4 bytes (of UTF-8) => "\uXXXX\uXXXX" (12 bytes)
*/
Buffer_Reserve(enc, 256 + (((cbName / 4) + 1) * 12));
if (name)
{
Buffer_AppendCharUnchecked(enc, '\"');
if (enc->forceASCII)
{
if (!Buffer_EscapeStringValidated(obj, enc, name, name + cbName))
{
return;
}
}
else
{
if (!Buffer_EscapeStringUnvalidated(obj, enc, name, name + cbName))
{
return;
}
}
Buffer_AppendCharUnchecked(enc, '\"');
Buffer_AppendCharUnchecked (enc, ':');
#ifndef JSON_NO_EXTRA_WHITESPACE
Buffer_AppendCharUnchecked (enc, ' ');
#endif
}
enc->beginTypeContext(obj, &tc);
switch (tc.type)
{
case JT_INVALID:
return;
case JT_ARRAY:
{
int count = 0;
JSOBJ iterObj;
enc->iterBegin(obj, &tc);
Buffer_AppendCharUnchecked (enc, '[');
while (enc->iterNext(obj, &tc))
{
if (count > 0)
{
Buffer_AppendCharUnchecked (enc, ',');
#ifndef JSON_NO_EXTRA_WHITESPACE
Buffer_AppendCharUnchecked (buffer, ' ');
#endif
}
iterObj = enc->iterGetValue(obj, &tc);
enc->level ++;
encode (iterObj, enc, NULL, 0);
count ++;
}
enc->iterEnd(obj, &tc);
Buffer_AppendCharUnchecked (enc, ']');
break;
}
case JT_OBJECT:
{
int count = 0;
JSOBJ iterObj;
char *objName;
enc->iterBegin(obj, &tc);
Buffer_AppendCharUnchecked (enc, '{');
while (enc->iterNext(obj, &tc))
{
if (count > 0)
{
Buffer_AppendCharUnchecked (enc, ',');
#ifndef JSON_NO_EXTRA_WHITESPACE
Buffer_AppendCharUnchecked (enc, ' ');
#endif
}
iterObj = enc->iterGetValue(obj, &tc);
objName = enc->iterGetName(obj, &tc, &szlen);
enc->level ++;
encode (iterObj, enc, objName, szlen);
count ++;
}
enc->iterEnd(obj, &tc);
Buffer_AppendCharUnchecked (enc, '}');
break;
}
case JT_LONG:
{
Buffer_AppendLongUnchecked (enc, enc->getLongValue(obj, &tc));
break;
}
case JT_INT:
{
Buffer_AppendIntUnchecked (enc, enc->getIntValue(obj, &tc));
break;
}
case JT_TRUE:
{
Buffer_AppendCharUnchecked (enc, 't');
Buffer_AppendCharUnchecked (enc, 'r');
Buffer_AppendCharUnchecked (enc, 'u');
Buffer_AppendCharUnchecked (enc, 'e');
break;
}
case JT_FALSE:
{
Buffer_AppendCharUnchecked (enc, 'f');
Buffer_AppendCharUnchecked (enc, 'a');
Buffer_AppendCharUnchecked (enc, 'l');
Buffer_AppendCharUnchecked (enc, 's');
Buffer_AppendCharUnchecked (enc, 'e');
break;
}
case JT_NULL:
{
Buffer_AppendCharUnchecked (enc, 'n');
Buffer_AppendCharUnchecked (enc, 'u');
Buffer_AppendCharUnchecked (enc, 'l');
Buffer_AppendCharUnchecked (enc, 'l');
break;
}
case JT_DOUBLE:
{
if (!Buffer_AppendDoubleUnchecked (obj, enc, enc->getDoubleValue(obj, &tc)))
{
enc->endTypeContext(obj, &tc);
enc->level --;
return;
}
break;
}
case JT_UTF8:
{
const char *value = enc->getStringValue(obj, &tc, &szlen);
Buffer_Reserve(enc, ((szlen / 4) + 1) * 12);
Buffer_AppendCharUnchecked (enc, '\"');
if (enc->forceASCII)
{
if (!Buffer_EscapeStringValidated(obj, enc, value, value + szlen))
{
enc->endTypeContext(obj, &tc);
enc->level --;
return;
}
}
else
{
if (!Buffer_EscapeStringUnvalidated(obj, enc, value, value + szlen))
{
enc->endTypeContext(obj, &tc);
enc->level --;
return;
}
}
Buffer_AppendCharUnchecked (enc, '\"');
break;
}
}
enc->endTypeContext(obj, &tc);
enc->level --;
}
char *JSON_EncodeObject(JSOBJ obj, JSONObjectEncoder *enc, char *_buffer, size_t _cbBuffer)
{
enc->malloc = enc->malloc ? enc->malloc : malloc;
enc->free = enc->free ? enc->free : free;
enc->realloc = enc->realloc ? enc->realloc : realloc;
enc->errorMsg = NULL;
enc->errorObj = NULL;
enc->level = 0;
if (enc->recursionMax < 1)
{
enc->recursionMax = JSON_MAX_RECURSION_DEPTH;
}
if (enc->doublePrecision < 0 ||
enc->doublePrecision > JSON_DOUBLE_MAX_DECIMALS)
{
enc->doublePrecision = JSON_DOUBLE_MAX_DECIMALS;
}
if (_buffer == NULL)
{
_cbBuffer = 32768;
enc->start = (char *) enc->malloc (_cbBuffer);
enc->heap = 1;
}
else
{
enc->start = _buffer;
enc->heap = 0;
}
enc->end = enc->start + _cbBuffer;
enc->offset = enc->start;
encode (obj, enc, NULL, 0);
Buffer_Reserve(enc, 1);
Buffer_AppendCharUnchecked(enc, '\0');
return enc->start;
}

View File

@ -1,13 +0,0 @@
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.

View File

@ -1,157 +0,0 @@
2.0.1
* lth generator flag to allow client to specify they want
escaped solidi '/'. issue #28
* lth crash fix when yajl_parse() is never called. issue #27
2.0.0
* lth YAJL is now ISC licensed: http://en.wikipedia.org/wiki/ISC_license
* lth 20-35% (osx and linux respectively) parsing performance
improvement attained by tweaking string scanning (idea: @michaelrhanson).
* Florian Forster & lth - yajl_tree interface introduced as a higher level
interface to the parser (eats JSON, poops a memory representation)
* lth require a C99 compiler
* lth integers are now represented with long long (64bit+) on all platforms.
* lth size_t now used throughout to represent buffer lengths, so you can
safely manage buffers greater than 4GB.
* gno semantic improvements to yajl's API regarding partial value parsing and
trailing garbage
* lth new configuration mechanism for yajl, see yajl_config() and
yajl_gen_config()
* gno more allocation checking in more places
* gno remove usage of strtol, replace with custom implementation that cares
not about your locale.
* lth yajl_parse_complete renamed to yajl_complete_parse.
* lth add a switch to validate utf8 strings as they are generated.
* lth tests are a lot quieter in their output.
* lth addition of a little in tree performance benchmark, `perftest` in
perf/perftest.c
1.0.12
* Conrad Irwin - Parse null bytes correctly
* Mirek Rusin - fix LLVM warnings
* gno - Don't generate numbers for keys. closes #13
* lth - various win32 fixes, including build documentation improvements
* John Stamp - Don't export private symbols.
* John Stamp - Install yajl_version.h, not the template.
* John Stamp - Don't use -fPIC for static lib. Cmake will automatically add it for the shared.
* lth 0 fix paths embedded in dylib upon installation on osx. closes #11
1.0.11
* lth remove -Wno-missing-field-initializers for greater gcc compat (3.4.6)
1.0.10
* Brian Maher - yajl is now buildable without a c++ compiler present
* Brian Maher - fix header installation on OSX with cmake 2.8.0 installed
* lth & vitali - allow builder to specify alternate lib directory
for installation (i.e. lib64)
* Vitali Lovich - yajl version number now programatically accessible
* lth - prevent cmake from embedding rpaths in binaries. Static linking
makes this unneccesary.
1.0.9
* lth - fix inverted logic causing yajl_gen_double() to always fail on
win32 (thanks to Fredrik Kihlander for the report)
1.0.8
* Randall E. Barker - move dllexport defnitions so dlls with proper
exports can again be generated on windows
* lth - add yajl_get_bytes_consumed() which allows the client to
determine the offset as an error, as well as determine how
many bytes of an input buffer were consumed.
* lth - fixes to keep "error offset" up to date (like when the
client callback returns 0)
* Brian Maher - allow client to specify a printing function in
generation
1.0.7
* lth fix win32 build (isinf and isnan)
1.0.6
* lth fix several compiler warnings
* lth fix generation of invalid json from yajl_gen_double
(NaN is not JSON)
* jstamp support for combining short options in tools
* jstamp exit properly on errors from tools
* octo test success no longer depends on integer size
* max fix configure --prefix
1.0.5
* lth several performance improvements related to function
inlinin'
1.0.4
* lth fix broken utf8 validation for three & four byte represenations.
thanks to http://github.com/brianmario and
http://github.com/technoweenie
1.0.3
* lth fix syntax error in cplusplus extern "C" statements for wider
compiler support
1.0.2
* lth update doxygen documentation with new sample code, passing NULL
for allocation functions added in 1.0.0
1.0.1
* lth resolve crash in json_reformatter due to incorrectly ordered
parameters.
1.0.0
* lth add 'make install' rules, thaks to Andrei Soroker for the
contribution.
* lth client may override allocation routines at generator or parser
allocation time
* tjw add yajl_parse_complete routine to allow client to explicitly
specify end-of-input, solving the "lonely number" case, where
json text consists only of an element with no explicit syntactic
end.
* tjw many new test cases
* tjw cleanup of code for symmetry and ease of reading
* lth integration of patches from Robert Varga which cleanup
compilation warnings on 64 bit linux
0.4.0
* lth buffer overflow bug in yajl_gen_double s/%lf/%g/ - thanks to
Eric Bergstrome
* lth yajl_number callback to allow passthrough of arbitrary precision
numbers to client. Thanks to Hatem Nassrat.
* lth yajl_integer now deals in long, instead of long long. This
combined with yajl_number improves compiler compatibility while
maintaining precision.
* lth better ./configure && make experience (still requires cmake and
ruby)
* lth fix handling of special characters hex 0F and 1F in yajl_encode
(thanks to Robert Geiger)
* lth allow leading zeros in exponents (thanks to Hatem Nassrat)
0.3.0
* lth doxygen documentation (html & man) generated as part of the
build
* lth many documentation updates.
* lth fix to work with older versions of cmake (don't use LOOSE_LOOP
constructs)
* lth work around different behavior of freebsd 4 scanf. initialize
parameter to scanf to zero.
* lth all tests run 32x with ranging buffer sizes to stress stream
parsing
* lth yajl_test accepts -b option to allow read buffer size to be
set
* lth option to validate UTF8 added to parser (argument in
yajl_parser_cfg)
* lth fix buffer overrun when chunk ends inside \u escaped text
* lth support client cancelation
0.2.2
* lth on windows build debug with C7 symbols and no pdb files.
0.2.1
* fix yajl_reformat and yajl_verify to work on arbitrarily sized
inputs.
* fix win32 build break, clean up all errors and warnings.
* fix optimized build flags.
0.2.0
* optionally support comments in input text
0.1.0
* Initial release

View File

@ -1,74 +0,0 @@
**********************************************************************
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

View File

@ -1,9 +0,0 @@
* 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

View File

@ -1,75 +0,0 @@
/*
* 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.
*/
#ifndef __YAJL_COMMON_H__
#define __YAJL_COMMON_H__
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
#define YAJL_MAX_DEPTH 128
/* msft dll export gunk. To build a DLL on windows, you
* must define WIN32, YAJL_SHARED, and YAJL_BUILD. To use a shared
* DLL, you must define YAJL_SHARED and WIN32 */
#if defined(WIN32) && defined(YAJL_SHARED)
# ifdef YAJL_BUILD
# define YAJL_API __declspec(dllexport)
# else
# define YAJL_API __declspec(dllimport)
# endif
#else
# if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 303
# define YAJL_API __attribute__ ((visibility("default")))
# else
# define YAJL_API
# endif
#endif
/** pointer to a malloc function, supporting client overriding memory
* allocation routines */
typedef void * (*yajl_malloc_func)(void *ctx, size_t sz);
/** pointer to a free function, supporting client overriding memory
* allocation routines */
typedef void (*yajl_free_func)(void *ctx, void * ptr);
/** pointer to a realloc function which can resize an allocation. */
typedef void * (*yajl_realloc_func)(void *ctx, void * ptr, size_t sz);
/** A structure which can be passed to yajl_*_alloc routines to allow the
* client to specify memory allocation functions to be used. */
typedef struct
{
/** pointer to a function that can allocate uninitialized memory */
yajl_malloc_func malloc;
/** pointer to a function that can resize memory allocations */
yajl_realloc_func realloc;
/** pointer to a function that can free memory allocated using
* reallocFunction or mallocFunction */
yajl_free_func free;
/** a context pointer that will be passed to above allocation routines */
void * ctx;
} yajl_alloc_funcs;
#ifdef __cplusplus
}
#endif
#endif

View File

@ -1,157 +0,0 @@
/*
* 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.
*/
/**
* \file yajl_gen.h
* Interface to YAJL's JSON generation facilities.
*/
#include <yajl/yajl_common.h>
#ifndef __YAJL_GEN_H__
#define __YAJL_GEN_H__
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
/** generator status codes */
typedef enum {
/** no error */
yajl_gen_status_ok = 0,
/** at a point where a map key is generated, a function other than
* yajl_gen_string was called */
yajl_gen_keys_must_be_strings,
/** YAJL's maximum generation depth was exceeded. see
* YAJL_MAX_DEPTH */
yajl_max_depth_exceeded,
/** A generator function (yajl_gen_XXX) was called while in an error
* state */
yajl_gen_in_error_state,
/** A complete JSON document has been generated */
yajl_gen_generation_complete,
/** yajl_gen_double was passed an invalid floating point value
* (infinity or NaN). */
yajl_gen_invalid_number,
/** A print callback was passed in, so there is no internal
* buffer to get from */
yajl_gen_no_buf,
/** returned from yajl_gen_string() when the yajl_gen_validate_utf8
* option is enabled and an invalid was passed by client code.
*/
yajl_gen_invalid_string
} yajl_gen_status;
/** an opaque handle to a generator */
typedef struct yajl_gen_t * yajl_gen;
/** a callback used for "printing" the results. */
typedef void (*yajl_print_t)(void * ctx,
const char * str,
size_t len);
/** configuration parameters for the parser, these may be passed to
* yajl_gen_config() along with option specific argument(s). In general,
* all configuration parameters default to *off*. */
typedef enum {
/** generate indented (beautiful) output */
yajl_gen_beautify = 0x01,
/**
* Set an indent string which is used when yajl_gen_beautify
* is enabled. Maybe something like \\t or some number of
* spaces. The default is four spaces ' '.
*/
yajl_gen_indent_string = 0x02,
/**
* Set a function and context argument that should be used to
* output generated json. the function should conform to the
* yajl_print_t prototype while the context argument is a
* void * of your choosing.
*
* example:
* yajl_gen_config(g, yajl_gen_print_callback, myFunc, myVoidPtr);
*/
yajl_gen_print_callback = 0x04,
/**
* Normally the generator does not validate that strings you
* pass to it via yajl_gen_string() are valid UTF8. Enabling
* this option will cause it to do so.
*/
yajl_gen_validate_utf8 = 0x08,
/**
* the forward solidus (slash or '/' in human) is not required to be
* escaped in json text. By default, YAJL will not escape it in the
* iterest of saving bytes. Setting this flag will cause YAJL to
* always escape '/' in generated JSON strings.
*/
yajl_gen_escape_solidus = 0x10
} yajl_gen_option;
/** allow the modification of generator options subsequent to handle
* allocation (via yajl_alloc)
* \returns zero in case of errors, non-zero otherwise
*/
YAJL_API int yajl_gen_config(yajl_gen g, yajl_gen_option opt, ...);
/** allocate a generator handle
* \param allocFuncs an optional pointer to a structure which allows
* the client to overide the memory allocation
* used by yajl. May be NULL, in which case
* malloc/free/realloc will be used.
*
* \returns an allocated handle on success, NULL on failure (bad params)
*/
YAJL_API yajl_gen yajl_gen_alloc(const yajl_alloc_funcs * allocFuncs);
/** free a generator handle */
YAJL_API void yajl_gen_free(yajl_gen handle);
YAJL_API yajl_gen_status yajl_gen_integer(yajl_gen hand, long long int number);
/** generate a floating point number. number may not be infinity or
* NaN, as these have no representation in JSON. In these cases the
* generator will return 'yajl_gen_invalid_number' */
YAJL_API yajl_gen_status yajl_gen_double(yajl_gen hand, double number);
YAJL_API yajl_gen_status yajl_gen_number(yajl_gen hand,
const char * num,
size_t len);
YAJL_API yajl_gen_status yajl_gen_string(yajl_gen hand,
const unsigned char * str,
size_t len);
YAJL_API yajl_gen_status yajl_gen_null(yajl_gen hand);
YAJL_API yajl_gen_status yajl_gen_bool(yajl_gen hand, int boolean);
YAJL_API yajl_gen_status yajl_gen_map_open(yajl_gen hand);
YAJL_API yajl_gen_status yajl_gen_map_close(yajl_gen hand);
YAJL_API yajl_gen_status yajl_gen_array_open(yajl_gen hand);
YAJL_API yajl_gen_status yajl_gen_array_close(yajl_gen hand);
/** access the null terminated generator buffer. If incrementally
* outputing JSON, one should call yajl_gen_clear to clear the
* buffer. This allows stream generation. */
YAJL_API yajl_gen_status yajl_gen_get_buf(yajl_gen hand,
const unsigned char ** buf,
size_t * len);
/** clear yajl's output buffer, but maintain all internal generation
* state. This function will not "reset" the generator state, and is
* intended to enable incremental JSON outputing. */
YAJL_API void yajl_gen_clear(yajl_gen hand);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -1,226 +0,0 @@
/*
* 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.
*/
/**
* \file yajl_parse.h
* Interface to YAJL's JSON stream parsing facilities.
*/
#include <yajl/yajl_common.h>
#ifndef __YAJL_PARSE_H__
#define __YAJL_PARSE_H__
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
/** error codes returned from this interface */
typedef enum {
/** no error was encountered */
yajl_status_ok,
/** a client callback returned zero, stopping the parse */
yajl_status_client_canceled,
/** An error occured during the parse. Call yajl_get_error for
* more information about the encountered error */
yajl_status_error
} yajl_status;
/** attain a human readable, english, string for an error */
YAJL_API const char * yajl_status_to_string(yajl_status code);
/** an opaque handle to a parser */
typedef struct yajl_handle_t * yajl_handle;
/** yajl is an event driven parser. this means as json elements are
* parsed, you are called back to do something with the data. The
* functions in this table indicate the various events for which
* you will be called back. Each callback accepts a "context"
* pointer, this is a void * that is passed into the yajl_parse
* function which the client code may use to pass around context.
*
* All callbacks return an integer. If non-zero, the parse will
* continue. If zero, the parse will be canceled and
* yajl_status_client_canceled will be returned from the parse.
*
* \attention {
* A note about the handling of numbers:
*
* yajl will only convert numbers that can be represented in a
* double or a 64 bit (long long) int. All other numbers will
* be passed to the client in string form using the yajl_number
* callback. Furthermore, if yajl_number is not NULL, it will
* always be used to return numbers, that is yajl_integer and
* yajl_double will be ignored. If yajl_number is NULL but one
* of yajl_integer or yajl_double are defined, parsing of a
* number larger than is representable in a double or 64 bit
* integer will result in a parse error.
* }
*/
typedef struct {
int (* yajl_null)(void * ctx);
int (* yajl_boolean)(void * ctx, int boolVal);
int (* yajl_integer)(void * ctx, long long integerVal);
int (* yajl_double)(void * ctx, double doubleVal);
/** A callback which passes the string representation of the number
* back to the client. Will be used for all numbers when present */
int (* yajl_number)(void * ctx, const char * numberVal,
size_t numberLen);
/** strings are returned as pointers into the JSON text when,
* possible, as a result, they are _not_ null padded */
int (* yajl_string)(void * ctx, const unsigned char * stringVal,
size_t stringLen);
int (* yajl_start_map)(void * ctx);
int (* yajl_map_key)(void * ctx, const unsigned char * key,
size_t stringLen);
int (* yajl_end_map)(void * ctx);
int (* yajl_start_array)(void * ctx);
int (* yajl_end_array)(void * ctx);
} yajl_callbacks;
/** allocate a parser handle
* \param callbacks a yajl callbacks structure specifying the
* functions to call when different JSON entities
* are encountered in the input text. May be NULL,
* which is only useful for validation.
* \param afs memory allocation functions, may be NULL for to use
* C runtime library routines (malloc and friends)
* \param ctx a context pointer that will be passed to callbacks.
*/
YAJL_API yajl_handle yajl_alloc(const yajl_callbacks * callbacks,
yajl_alloc_funcs * afs,
void * ctx);
/** configuration parameters for the parser, these may be passed to
* yajl_config() along with option specific argument(s). In general,
* all configuration parameters default to *off*. */
typedef enum {
/** Ignore javascript style comments present in
* JSON input. Non-standard, but rather fun
* arguments: toggled off with integer zero, on otherwise.
*
* example:
* yajl_config(h, yajl_allow_comments, 1); // turn comment support on
*/
yajl_allow_comments = 0x01,
/**
* When set the parser will verify that all strings in JSON input are
* valid UTF8 and will emit a parse error if this is not so. When set,
* this option makes parsing slightly more expensive (~7% depending
* on processor and compiler in use)
*
* example:
* yajl_config(h, yajl_dont_validate_strings, 1); // disable utf8 checking
*/
yajl_dont_validate_strings = 0x02,
/**
* By default, upon calls to yajl_complete_parse(), yajl will
* ensure the entire input text was consumed and will raise an error
* otherwise. Enabling this flag will cause yajl to disable this
* check. This can be useful when parsing json out of a that contains more
* than a single JSON document.
*/
yajl_allow_trailing_garbage = 0x04,
/**
* Allow multiple values to be parsed by a single handle. The
* entire text must be valid JSON, and values can be seperated
* by any kind of whitespace. This flag will change the
* behavior of the parser, and cause it continue parsing after
* a value is parsed, rather than transitioning into a
* complete state. This option can be useful when parsing multiple
* values from an input stream.
*/
yajl_allow_multiple_values = 0x08,
/**
* When yajl_complete_parse() is called the parser will
* check that the top level value was completely consumed. I.E.,
* if called whilst in the middle of parsing a value
* yajl will enter an error state (premature EOF). Setting this
* flag suppresses that check and the corresponding error.
*/
yajl_allow_partial_values = 0x10
} yajl_option;
/** allow the modification of parser options subsequent to handle
* allocation (via yajl_alloc)
* \returns zero in case of errors, non-zero otherwise
*/
YAJL_API int yajl_config(yajl_handle h, yajl_option opt, ...);
/** free a parser handle */
YAJL_API void yajl_free(yajl_handle handle);
/** Parse some json!
* \param hand - a handle to the json parser allocated with yajl_alloc
* \param jsonText - a pointer to the UTF8 json text to be parsed
* \param jsonTextLength - the length, in bytes, of input text
*/
YAJL_API yajl_status yajl_parse(yajl_handle hand,
const unsigned char * jsonText,
size_t jsonTextLength);
/** Parse any remaining buffered json.
* Since yajl is a stream-based parser, without an explicit end of
* input, yajl sometimes can't decide if content at the end of the
* stream is valid or not. For example, if "1" has been fed in,
* yajl can't know whether another digit is next or some character
* that would terminate the integer token.
*
* \param hand - a handle to the json parser allocated with yajl_alloc
*/
YAJL_API yajl_status yajl_complete_parse(yajl_handle hand);
/** get an error string describing the state of the
* parse.
*
* If verbose is non-zero, the message will include the JSON
* text where the error occured, along with an arrow pointing to
* the specific char.
*
* \returns A dynamically allocated string will be returned which should
* be freed with yajl_free_error
*/
YAJL_API unsigned char * yajl_get_error(yajl_handle hand, int verbose,
const unsigned char * jsonText,
size_t jsonTextLength);
/**
* get the amount of data consumed from the last chunk passed to YAJL.
*
* In the case of a successful parse this can help you understand if
* the entire buffer was consumed (which will allow you to handle
* "junk at end of input").
*
* In the event an error is encountered during parsing, this function
* affords the client a way to get the offset into the most recent
* chunk where the error occured. 0 will be returned if no error
* was encountered.
*/
YAJL_API size_t yajl_get_bytes_consumed(yajl_handle hand);
/** free an error returned from yajl_get_error */
YAJL_API void yajl_free_error(yajl_handle hand, unsigned char * str);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -1,177 +0,0 @@
/*
* Copyright (c) 2010-2011 Florian Forster <ff at octo.it>
*
* 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.
*/
/**
* \file yajl_tree.h
*
* Parses JSON data and returns the data in tree form.
*
* \author Florian Forster
* \date August 2010
*
* This interface makes quick parsing and extraction of
* smallish JSON docs trivial:
*
* \include example/parse_config.c
*/
#ifndef YAJL_TREE_H
#define YAJL_TREE_H 1
#include <yajl/yajl_common.h>
/** possible data types that a yajl_val_s can hold */
typedef enum {
yajl_t_string = 1,
yajl_t_number = 2,
yajl_t_object = 3,
yajl_t_array = 4,
yajl_t_true = 5,
yajl_t_false = 6,
yajl_t_null = 7,
/** The any type isn't valid for yajl_val_s.type, but can be
* used as an argument to routines like yajl_tree_get().
*/
yajl_t_any = 8
} yajl_type;
#define YAJL_NUMBER_INT_VALID 0x01
#define YAJL_NUMBER_DOUBLE_VALID 0x02
/** A pointer to a node in the parse tree */
typedef struct yajl_val_s * yajl_val;
/**
* A JSON value representation capable of holding one of the seven
* types above. For "string", "number", "object", and "array"
* additional data is available in the union. The "YAJL_IS_*"
* and "YAJL_GET_*" macros below allow type checking and convenient
* value extraction.
*/
struct yajl_val_s
{
/** Type of the value contained. Use the "YAJL_IS_*" macors to check for a
* specific type. */
yajl_type type;
/** Type-specific data. You may use the "YAJL_GET_*" macros to access these
* members. */
union
{
char * string;
struct {
long long i; /*< integer value, if representable. */
double d; /*< double value, if representable. */
/** Signals whether the \em i and \em d members are
* valid. See \c YAJL_NUMBER_INT_VALID and
* \c YAJL_NUMBER_DOUBLE_VALID. */
char *r; /*< unparsed number in string form. */
unsigned int flags;
} number;
struct {
const char **keys; /*< Array of keys */
yajl_val *values; /*< Array of values. */
size_t len; /*< Number of key-value-pairs. */
} object;
struct {
yajl_val *values; /*< Array of elements. */
size_t len; /*< Number of elements. */
} array;
} u;
};
/**
* Parse a string.
*
* Parses an null-terminated string containing JSON data and returns a pointer
* to the top-level value (root of the parse tree).
*
* \param input Pointer to a null-terminated utf8 string containing
* JSON data.
* \param error_buffer Pointer to a buffer in which an error message will
* be stored if \em yajl_tree_parse fails, or
* \c NULL. The buffer will be initialized before
* parsing, so its content will be destroyed even if
* \em yajl_tree_parse succeeds.
* \param error_buffer_size Size of the memory area pointed to by
* \em error_buffer_size. If \em error_buffer_size is
* \c NULL, this argument is ignored.
*
* \returns Pointer to the top-level value or \c NULL on error. The memory
* pointed to must be freed using \em yajl_tree_free. In case of an error, a
* null terminated message describing the error in more detail is stored in
* \em error_buffer if it is not \c NULL.
*/
YAJL_API yajl_val yajl_tree_parse (const char *input,
char *error_buffer, size_t error_buffer_size);
/**
* Free a parse tree returned by "yajl_tree_parse".
*
* \param v Pointer to a JSON value returned by "yajl_tree_parse". Passing NULL
* is valid and results in a no-op.
*/
YAJL_API void yajl_tree_free (yajl_val v);
/**
* Access a nested value inside a tree.
*
* \param parent the node under which you'd like to extract values.
* \param path A null terminated array of strings, each the name of an object key
* \param type the yajl_type of the object you seek, or yajl_t_any if any will do.
*
* \returns a pointer to the found value, or NULL if we came up empty.
*
* Future Ideas: it'd be nice to move path to a string and implement support for
* a teeny tiny micro language here, so you can extract array elements, do things
* like .first and .last, even .length. Inspiration from JSONPath and css selectors?
* No it wouldn't be fast, but that's not what this API is about.
*/
YAJL_API yajl_val yajl_tree_get(yajl_val parent, const char ** path, yajl_type type);
/* Various convenience macros to check the type of a `yajl_val` */
#define YAJL_IS_STRING(v) (((v) != NULL) && ((v)->type == yajl_t_string))
#define YAJL_IS_NUMBER(v) (((v) != NULL) && ((v)->type == yajl_t_number))
#define YAJL_IS_INTEGER(v) (YAJL_IS_NUMBER(v) && ((v)->u.flags & YAJL_NUMBER_INT_VALID))
#define YAJL_IS_DOUBLE(v) (YAJL_IS_NUMBER(v) && ((v)->u.flags & YAJL_NUMBER_DOUBLE_VALID))
#define YAJL_IS_OBJECT(v) (((v) != NULL) && ((v)->type == yajl_t_object))
#define YAJL_IS_ARRAY(v) (((v) != NULL) && ((v)->type == yajl_t_array ))
#define YAJL_IS_TRUE(v) (((v) != NULL) && ((v)->type == yajl_t_true ))
#define YAJL_IS_FALSE(v) (((v) != NULL) && ((v)->type == yajl_t_false ))
#define YAJL_IS_NULL(v) (((v) != NULL) && ((v)->type == yajl_t_null ))
/** Given a yajl_val_string return a ptr to the bare string it contains,
* or NULL if the value is not a string. */
#define YAJL_GET_STRING(v) (YAJL_IS_STRING(v) ? (v)->u.string : NULL)
/** Get the string representation of a number. You should check type first,
* perhaps using YAJL_IS_NUMBER */
#define YAJL_GET_NUMBER(v) ((v)->u.number.r)
/** Get the double representation of a number. You should check type first,
* perhaps using YAJL_IS_DOUBLE */
#define YAJL_GET_DOUBLE(v) ((v)->u.number.d)
/** Get the 64bit (long long) integer representation of a number. You should
* check type first, perhaps using YAJL_IS_INTEGER */
#define YAJL_GET_INTEGER(v) ((v)->u.number.i)
/** Get a pointer to a yajl_val_object or NULL if the value is not an object. */
#define YAJL_GET_OBJECT(v) (YAJL_IS_OBJECT(v) ? &(v)->u.object : NULL)
/** Get a pointer to a yajl_val_array or NULL if the value is not an object. */
#define YAJL_GET_ARRAY(v) (YAJL_IS_ARRAY(v) ? &(v)->u.array : NULL)
#endif /* YAJL_TREE_H */

View File

@ -1,23 +0,0 @@
#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_ */

View File

@ -1,82 +0,0 @@
# 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.
SET (SRCS yajl.c yajl_lex.c yajl_parser.c yajl_buf.c
yajl_encode.c yajl_gen.c yajl_alloc.c
yajl_tree.c yajl_version.c
)
SET (HDRS yajl_parser.h yajl_lex.h yajl_buf.h yajl_encode.h yajl_alloc.h)
SET (PUB_HDRS api/yajl_parse.h api/yajl_gen.h api/yajl_common.h api/yajl_tree.h)
# useful when fixing lexer bugs.
#ADD_DEFINITIONS(-DYAJL_LEXER_DEBUG)
# Ensure defined when building YAJL (as opposed to using it from
# another project). Used to ensure correct function export when
# building win32 DLL.
ADD_DEFINITIONS(-DYAJL_BUILD)
# set up some paths
SET (libDir ${CMAKE_CURRENT_BINARY_DIR}/../${YAJL_DIST_NAME}/lib)
SET (incDir ${CMAKE_CURRENT_BINARY_DIR}/../${YAJL_DIST_NAME}/include/yajl)
# set the output path for libraries
SET(LIBRARY_OUTPUT_PATH ${libDir})
ADD_LIBRARY(yajl_s STATIC ${SRCS} ${HDRS} ${PUB_HDRS})
ADD_LIBRARY(yajl SHARED ${SRCS} ${HDRS} ${PUB_HDRS})
#### setup shared library version number
SET_TARGET_PROPERTIES(yajl PROPERTIES
DEFINE_SYMBOL YAJL_SHARED
SOVERSION ${YAJL_MAJOR}
VERSION ${YAJL_MAJOR}.${YAJL_MINOR}.${YAJL_MICRO})
#### ensure a .dylib has correct absolute installation paths upon installation
IF(APPLE)
MESSAGE("INSTALL_NAME_DIR: ${CMAKE_INSTALL_PREFIX}/lib")
SET_TARGET_PROPERTIES(yajl PROPERTIES
INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/lib")
ENDIF(APPLE)
#### build up an sdk as a post build step
# create some directories
FILE(MAKE_DIRECTORY ${libDir})
FILE(MAKE_DIRECTORY ${incDir})
# generate build-time source
CONFIGURE_FILE(api/yajl_version.h.cmake ${incDir}/yajl_version.h)
# copy public headers to output directory
FOREACH (header ${PUB_HDRS})
SET (header ${CMAKE_CURRENT_SOURCE_DIR}/${header})
EXEC_PROGRAM(${CMAKE_COMMAND} ARGS -E copy_if_different ${header} ${incDir})
ADD_CUSTOM_COMMAND(TARGET yajl_s POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${header} ${incDir})
ENDFOREACH (header ${PUB_HDRS})
INCLUDE_DIRECTORIES(${incDir}/..)
IF(NOT WIN32)
# at build time you may specify the cmake variable LIB_SUFFIX to handle
# 64-bit systems which use 'lib64'
INSTALL(TARGETS yajl LIBRARY DESTINATION lib${LIB_SUFFIX})
INSTALL(TARGETS yajl_s ARCHIVE DESTINATION lib${LIB_SUFFIX})
INSTALL(FILES ${PUB_HDRS} DESTINATION include/yajl)
INSTALL(FILES ${incDir}/yajl_version.h DESTINATION include/yajl)
ENDIF()

File diff suppressed because it is too large Load Diff

View File

@ -1,75 +0,0 @@
/*
* 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.
*/
#ifndef __YAJL_COMMON_H__
#define __YAJL_COMMON_H__
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
#define YAJL_MAX_DEPTH 128
/* msft dll export gunk. To build a DLL on windows, you
* must define WIN32, YAJL_SHARED, and YAJL_BUILD. To use a shared
* DLL, you must define YAJL_SHARED and WIN32 */
#if defined(WIN32) && defined(YAJL_SHARED)
# ifdef YAJL_BUILD
# define YAJL_API __declspec(dllexport)
# else
# define YAJL_API __declspec(dllimport)
# endif
#else
# if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 303
# define YAJL_API __attribute__ ((visibility("default")))
# else
# define YAJL_API
# endif
#endif
/** pointer to a malloc function, supporting client overriding memory
* allocation routines */
typedef void * (*yajl_malloc_func)(void *ctx, size_t sz);
/** pointer to a free function, supporting client overriding memory
* allocation routines */
typedef void (*yajl_free_func)(void *ctx, void * ptr);
/** pointer to a realloc function which can resize an allocation. */
typedef void * (*yajl_realloc_func)(void *ctx, void * ptr, size_t sz);
/** A structure which can be passed to yajl_*_alloc routines to allow the
* client to specify memory allocation functions to be used. */
typedef struct
{
/** pointer to a function that can allocate uninitialized memory */
yajl_malloc_func malloc;
/** pointer to a function that can resize memory allocations */
yajl_realloc_func realloc;
/** pointer to a function that can free memory allocated using
* reallocFunction or mallocFunction */
yajl_free_func free;
/** a context pointer that will be passed to above allocation routines */
void * ctx;
} yajl_alloc_funcs;
#ifdef __cplusplus
}
#endif
#endif

View File

@ -1,157 +0,0 @@
/*
* 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.
*/
/**
* \file yajl_gen.h
* Interface to YAJL's JSON generation facilities.
*/
#include <yajl/yajl_common.h>
#ifndef __YAJL_GEN_H__
#define __YAJL_GEN_H__
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
/** generator status codes */
typedef enum {
/** no error */
yajl_gen_status_ok = 0,
/** at a point where a map key is generated, a function other than
* yajl_gen_string was called */
yajl_gen_keys_must_be_strings,
/** YAJL's maximum generation depth was exceeded. see
* YAJL_MAX_DEPTH */
yajl_max_depth_exceeded,
/** A generator function (yajl_gen_XXX) was called while in an error
* state */
yajl_gen_in_error_state,
/** A complete JSON document has been generated */
yajl_gen_generation_complete,
/** yajl_gen_double was passed an invalid floating point value
* (infinity or NaN). */
yajl_gen_invalid_number,
/** A print callback was passed in, so there is no internal
* buffer to get from */
yajl_gen_no_buf,
/** returned from yajl_gen_string() when the yajl_gen_validate_utf8
* option is enabled and an invalid was passed by client code.
*/
yajl_gen_invalid_string
} yajl_gen_status;
/** an opaque handle to a generator */
typedef struct yajl_gen_t * yajl_gen;
/** a callback used for "printing" the results. */
typedef void (*yajl_print_t)(void * ctx,
const char * str,
size_t len);
/** configuration parameters for the parser, these may be passed to
* yajl_gen_config() along with option specific argument(s). In general,
* all configuration parameters default to *off*. */
typedef enum {
/** generate indented (beautiful) output */
yajl_gen_beautify = 0x01,
/**
* Set an indent string which is used when yajl_gen_beautify
* is enabled. Maybe something like \\t or some number of
* spaces. The default is four spaces ' '.
*/
yajl_gen_indent_string = 0x02,
/**
* Set a function and context argument that should be used to
* output generated json. the function should conform to the
* yajl_print_t prototype while the context argument is a
* void * of your choosing.
*
* example:
* yajl_gen_config(g, yajl_gen_print_callback, myFunc, myVoidPtr);
*/
yajl_gen_print_callback = 0x04,
/**
* Normally the generator does not validate that strings you
* pass to it via yajl_gen_string() are valid UTF8. Enabling
* this option will cause it to do so.
*/
yajl_gen_validate_utf8 = 0x08,
/**
* the forward solidus (slash or '/' in human) is not required to be
* escaped in json text. By default, YAJL will not escape it in the
* iterest of saving bytes. Setting this flag will cause YAJL to
* always escape '/' in generated JSON strings.
*/
yajl_gen_escape_solidus = 0x10
} yajl_gen_option;
/** allow the modification of generator options subsequent to handle
* allocation (via yajl_alloc)
* \returns zero in case of errors, non-zero otherwise
*/
YAJL_API int yajl_gen_config(yajl_gen g, yajl_gen_option opt, ...);
/** allocate a generator handle
* \param allocFuncs an optional pointer to a structure which allows
* the client to overide the memory allocation
* used by yajl. May be NULL, in which case
* malloc/free/realloc will be used.
*
* \returns an allocated handle on success, NULL on failure (bad params)
*/
YAJL_API yajl_gen yajl_gen_alloc(const yajl_alloc_funcs * allocFuncs);
/** free a generator handle */
YAJL_API void yajl_gen_free(yajl_gen handle);
YAJL_API yajl_gen_status yajl_gen_integer(yajl_gen hand, long long int number);
/** generate a floating point number. number may not be infinity or
* NaN, as these have no representation in JSON. In these cases the
* generator will return 'yajl_gen_invalid_number' */
YAJL_API yajl_gen_status yajl_gen_double(yajl_gen hand, double number);
YAJL_API yajl_gen_status yajl_gen_number(yajl_gen hand,
const char * num,
size_t len);
YAJL_API yajl_gen_status yajl_gen_string(yajl_gen hand,
const unsigned char * str,
size_t len);
YAJL_API yajl_gen_status yajl_gen_null(yajl_gen hand);
YAJL_API yajl_gen_status yajl_gen_bool(yajl_gen hand, int boolean);
YAJL_API yajl_gen_status yajl_gen_map_open(yajl_gen hand);
YAJL_API yajl_gen_status yajl_gen_map_close(yajl_gen hand);
YAJL_API yajl_gen_status yajl_gen_array_open(yajl_gen hand);
YAJL_API yajl_gen_status yajl_gen_array_close(yajl_gen hand);
/** access the null terminated generator buffer. If incrementally
* outputing JSON, one should call yajl_gen_clear to clear the
* buffer. This allows stream generation. */
YAJL_API yajl_gen_status yajl_gen_get_buf(yajl_gen hand,
const unsigned char ** buf,
size_t * len);
/** clear yajl's output buffer, but maintain all internal generation
* state. This function will not "reset" the generator state, and is
* intended to enable incremental JSON outputing. */
YAJL_API void yajl_gen_clear(yajl_gen hand);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -1,226 +0,0 @@
/*
* 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.
*/
/**
* \file yajl_parse.h
* Interface to YAJL's JSON stream parsing facilities.
*/
#include <yajl/yajl_common.h>
#ifndef __YAJL_PARSE_H__
#define __YAJL_PARSE_H__
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
/** error codes returned from this interface */
typedef enum {
/** no error was encountered */
yajl_status_ok,
/** a client callback returned zero, stopping the parse */
yajl_status_client_canceled,
/** An error occured during the parse. Call yajl_get_error for
* more information about the encountered error */
yajl_status_error
} yajl_status;
/** attain a human readable, english, string for an error */
YAJL_API const char * yajl_status_to_string(yajl_status code);
/** an opaque handle to a parser */
typedef struct yajl_handle_t * yajl_handle;
/** yajl is an event driven parser. this means as json elements are
* parsed, you are called back to do something with the data. The
* functions in this table indicate the various events for which
* you will be called back. Each callback accepts a "context"
* pointer, this is a void * that is passed into the yajl_parse
* function which the client code may use to pass around context.
*
* All callbacks return an integer. If non-zero, the parse will
* continue. If zero, the parse will be canceled and
* yajl_status_client_canceled will be returned from the parse.
*
* \attention {
* A note about the handling of numbers:
*
* yajl will only convert numbers that can be represented in a
* double or a 64 bit (long long) int. All other numbers will
* be passed to the client in string form using the yajl_number
* callback. Furthermore, if yajl_number is not NULL, it will
* always be used to return numbers, that is yajl_integer and
* yajl_double will be ignored. If yajl_number is NULL but one
* of yajl_integer or yajl_double are defined, parsing of a
* number larger than is representable in a double or 64 bit
* integer will result in a parse error.
* }
*/
typedef struct {
int (* yajl_null)(void * ctx);
int (* yajl_boolean)(void * ctx, int boolVal);
int (* yajl_integer)(void * ctx, long long integerVal);
int (* yajl_double)(void * ctx, double doubleVal);
/** A callback which passes the string representation of the number
* back to the client. Will be used for all numbers when present */
int (* yajl_number)(void * ctx, const char * numberVal,
size_t numberLen);
/** strings are returned as pointers into the JSON text when,
* possible, as a result, they are _not_ null padded */
int (* yajl_string)(void * ctx, const unsigned char * stringVal,
size_t stringLen);
int (* yajl_start_map)(void * ctx);
int (* yajl_map_key)(void * ctx, const unsigned char * key,
size_t stringLen);
int (* yajl_end_map)(void * ctx);
int (* yajl_start_array)(void * ctx);
int (* yajl_end_array)(void * ctx);
} yajl_callbacks;
/** allocate a parser handle
* \param callbacks a yajl callbacks structure specifying the
* functions to call when different JSON entities
* are encountered in the input text. May be NULL,
* which is only useful for validation.
* \param afs memory allocation functions, may be NULL for to use
* C runtime library routines (malloc and friends)
* \param ctx a context pointer that will be passed to callbacks.
*/
YAJL_API yajl_handle yajl_alloc(const yajl_callbacks * callbacks,
yajl_alloc_funcs * afs,
void * ctx);
/** configuration parameters for the parser, these may be passed to
* yajl_config() along with option specific argument(s). In general,
* all configuration parameters default to *off*. */
typedef enum {
/** Ignore javascript style comments present in
* JSON input. Non-standard, but rather fun
* arguments: toggled off with integer zero, on otherwise.
*
* example:
* yajl_config(h, yajl_allow_comments, 1); // turn comment support on
*/
yajl_allow_comments = 0x01,
/**
* When set the parser will verify that all strings in JSON input are
* valid UTF8 and will emit a parse error if this is not so. When set,
* this option makes parsing slightly more expensive (~7% depending
* on processor and compiler in use)
*
* example:
* yajl_config(h, yajl_dont_validate_strings, 1); // disable utf8 checking
*/
yajl_dont_validate_strings = 0x02,
/**
* By default, upon calls to yajl_complete_parse(), yajl will
* ensure the entire input text was consumed and will raise an error
* otherwise. Enabling this flag will cause yajl to disable this
* check. This can be useful when parsing json out of a that contains more
* than a single JSON document.
*/
yajl_allow_trailing_garbage = 0x04,
/**
* Allow multiple values to be parsed by a single handle. The
* entire text must be valid JSON, and values can be seperated
* by any kind of whitespace. This flag will change the
* behavior of the parser, and cause it continue parsing after
* a value is parsed, rather than transitioning into a
* complete state. This option can be useful when parsing multiple
* values from an input stream.
*/
yajl_allow_multiple_values = 0x08,
/**
* When yajl_complete_parse() is called the parser will
* check that the top level value was completely consumed. I.E.,
* if called whilst in the middle of parsing a value
* yajl will enter an error state (premature EOF). Setting this
* flag suppresses that check and the corresponding error.
*/
yajl_allow_partial_values = 0x10
} yajl_option;
/** allow the modification of parser options subsequent to handle
* allocation (via yajl_alloc)
* \returns zero in case of errors, non-zero otherwise
*/
YAJL_API int yajl_config(yajl_handle h, yajl_option opt, ...);
/** free a parser handle */
YAJL_API void yajl_free(yajl_handle handle);
/** Parse some json!
* \param hand - a handle to the json parser allocated with yajl_alloc
* \param jsonText - a pointer to the UTF8 json text to be parsed
* \param jsonTextLength - the length, in bytes, of input text
*/
YAJL_API yajl_status yajl_parse(yajl_handle hand,
const unsigned char * jsonText,
size_t jsonTextLength);
/** Parse any remaining buffered json.
* Since yajl is a stream-based parser, without an explicit end of
* input, yajl sometimes can't decide if content at the end of the
* stream is valid or not. For example, if "1" has been fed in,
* yajl can't know whether another digit is next or some character
* that would terminate the integer token.
*
* \param hand - a handle to the json parser allocated with yajl_alloc
*/
YAJL_API yajl_status yajl_complete_parse(yajl_handle hand);
/** get an error string describing the state of the
* parse.
*
* If verbose is non-zero, the message will include the JSON
* text where the error occured, along with an arrow pointing to
* the specific char.
*
* \returns A dynamically allocated string will be returned which should
* be freed with yajl_free_error
*/
YAJL_API unsigned char * yajl_get_error(yajl_handle hand, int verbose,
const unsigned char * jsonText,
size_t jsonTextLength);
/**
* get the amount of data consumed from the last chunk passed to YAJL.
*
* In the case of a successful parse this can help you understand if
* the entire buffer was consumed (which will allow you to handle
* "junk at end of input").
*
* In the event an error is encountered during parsing, this function
* affords the client a way to get the offset into the most recent
* chunk where the error occured. 0 will be returned if no error
* was encountered.
*/
YAJL_API size_t yajl_get_bytes_consumed(yajl_handle hand);
/** free an error returned from yajl_get_error */
YAJL_API void yajl_free_error(yajl_handle hand, unsigned char * str);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -1,177 +0,0 @@
/*
* Copyright (c) 2010-2011 Florian Forster <ff at octo.it>
*
* 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.
*/
/**
* \file yajl_tree.h
*
* Parses JSON data and returns the data in tree form.
*
* \author Florian Forster
* \date August 2010
*
* This interface makes quick parsing and extraction of
* smallish JSON docs trivial:
*
* \include example/parse_config.c
*/
#ifndef YAJL_TREE_H
#define YAJL_TREE_H 1
#include <yajl/yajl_common.h>
/** possible data types that a yajl_val_s can hold */
typedef enum {
yajl_t_string = 1,
yajl_t_number = 2,
yajl_t_object = 3,
yajl_t_array = 4,
yajl_t_true = 5,
yajl_t_false = 6,
yajl_t_null = 7,
/** The any type isn't valid for yajl_val_s.type, but can be
* used as an argument to routines like yajl_tree_get().
*/
yajl_t_any = 8
} yajl_type;
#define YAJL_NUMBER_INT_VALID 0x01
#define YAJL_NUMBER_DOUBLE_VALID 0x02
/** A pointer to a node in the parse tree */
typedef struct yajl_val_s * yajl_val;
/**
* A JSON value representation capable of holding one of the seven
* types above. For "string", "number", "object", and "array"
* additional data is available in the union. The "YAJL_IS_*"
* and "YAJL_GET_*" macros below allow type checking and convenient
* value extraction.
*/
struct yajl_val_s
{
/** Type of the value contained. Use the "YAJL_IS_*" macors to check for a
* specific type. */
yajl_type type;
/** Type-specific data. You may use the "YAJL_GET_*" macros to access these
* members. */
union
{
char * string;
struct {
long long i; /*< integer value, if representable. */
double d; /*< double value, if representable. */
/** Signals whether the \em i and \em d members are
* valid. See \c YAJL_NUMBER_INT_VALID and
* \c YAJL_NUMBER_DOUBLE_VALID. */
char *r; /*< unparsed number in string form. */
unsigned int flags;
} number;
struct {
const char **keys; /*< Array of keys */
yajl_val *values; /*< Array of values. */
size_t len; /*< Number of key-value-pairs. */
} object;
struct {
yajl_val *values; /*< Array of elements. */
size_t len; /*< Number of elements. */
} array;
} u;
};
/**
* Parse a string.
*
* Parses an null-terminated string containing JSON data and returns a pointer
* to the top-level value (root of the parse tree).
*
* \param input Pointer to a null-terminated utf8 string containing
* JSON data.
* \param error_buffer Pointer to a buffer in which an error message will
* be stored if \em yajl_tree_parse fails, or
* \c NULL. The buffer will be initialized before
* parsing, so its content will be destroyed even if
* \em yajl_tree_parse succeeds.
* \param error_buffer_size Size of the memory area pointed to by
* \em error_buffer_size. If \em error_buffer_size is
* \c NULL, this argument is ignored.
*
* \returns Pointer to the top-level value or \c NULL on error. The memory
* pointed to must be freed using \em yajl_tree_free. In case of an error, a
* null terminated message describing the error in more detail is stored in
* \em error_buffer if it is not \c NULL.
*/
YAJL_API yajl_val yajl_tree_parse (const char *input,
char *error_buffer, size_t error_buffer_size);
/**
* Free a parse tree returned by "yajl_tree_parse".
*
* \param v Pointer to a JSON value returned by "yajl_tree_parse". Passing NULL
* is valid and results in a no-op.
*/
YAJL_API void yajl_tree_free (yajl_val v);
/**
* Access a nested value inside a tree.
*
* \param parent the node under which you'd like to extract values.
* \param path A null terminated array of strings, each the name of an object key
* \param type the yajl_type of the object you seek, or yajl_t_any if any will do.
*
* \returns a pointer to the found value, or NULL if we came up empty.
*
* Future Ideas: it'd be nice to move path to a string and implement support for
* a teeny tiny micro language here, so you can extract array elements, do things
* like .first and .last, even .length. Inspiration from JSONPath and css selectors?
* No it wouldn't be fast, but that's not what this API is about.
*/
YAJL_API yajl_val yajl_tree_get(yajl_val parent, const char ** path, yajl_type type);
/* Various convenience macros to check the type of a `yajl_val` */
#define YAJL_IS_STRING(v) (((v) != NULL) && ((v)->type == yajl_t_string))
#define YAJL_IS_NUMBER(v) (((v) != NULL) && ((v)->type == yajl_t_number))
#define YAJL_IS_INTEGER(v) (YAJL_IS_NUMBER(v) && ((v)->u.flags & YAJL_NUMBER_INT_VALID))
#define YAJL_IS_DOUBLE(v) (YAJL_IS_NUMBER(v) && ((v)->u.flags & YAJL_NUMBER_DOUBLE_VALID))
#define YAJL_IS_OBJECT(v) (((v) != NULL) && ((v)->type == yajl_t_object))
#define YAJL_IS_ARRAY(v) (((v) != NULL) && ((v)->type == yajl_t_array ))
#define YAJL_IS_TRUE(v) (((v) != NULL) && ((v)->type == yajl_t_true ))
#define YAJL_IS_FALSE(v) (((v) != NULL) && ((v)->type == yajl_t_false ))
#define YAJL_IS_NULL(v) (((v) != NULL) && ((v)->type == yajl_t_null ))
/** Given a yajl_val_string return a ptr to the bare string it contains,
* or NULL if the value is not a string. */
#define YAJL_GET_STRING(v) (YAJL_IS_STRING(v) ? (v)->u.string : NULL)
/** Get the string representation of a number. You should check type first,
* perhaps using YAJL_IS_NUMBER */
#define YAJL_GET_NUMBER(v) ((v)->u.number.r)
/** Get the double representation of a number. You should check type first,
* perhaps using YAJL_IS_DOUBLE */
#define YAJL_GET_DOUBLE(v) ((v)->u.number.d)
/** Get the 64bit (long long) integer representation of a number. You should
* check type first, perhaps using YAJL_IS_INTEGER */
#define YAJL_GET_INTEGER(v) ((v)->u.number.i)
/** Get a pointer to a yajl_val_object or NULL if the value is not an object. */
#define YAJL_GET_OBJECT(v) (YAJL_IS_OBJECT(v) ? &(v)->u.object : NULL)
/** Get a pointer to a yajl_val_array or NULL if the value is not an object. */
#define YAJL_GET_ARRAY(v) (YAJL_IS_ARRAY(v) ? &(v)->u.array : NULL)
#endif /* YAJL_TREE_H */

View File

@ -1,23 +0,0 @@
#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_ */

View File

@ -1,33 +0,0 @@
/**
\example reformatter/json_reformat.c
\example example/parse_config.c
*/
/*!
\mainpage Yet Another JSON Library (YAJL)
\author Lloyd Hilaiel
\date 2007-2011
Yet Another JSON Library (YAJL) is a small event-driven (SAX-style)
JSON parser written in ANSI C, and a small validating JSON
generator. YAJL is released under the permissive ISC license.
\section features Features
-# Stream (incremental) parsing and generation of JSON
-# ANSI C
-# Human readable error messages with context
-# tiny
-# event driven
-# support for generating "beautified" JSON
-# includes
It also includes a small simplified tree interface for
simplified parsing and extraction of data from smallish JSON documents.
\section usage Usage
See <a href="reformatter_2json_reformat_8c-example.html">json_reformat.c</a> for a complete example of stream based parsing
and generation of JSON. See <a href="example_2parse_config_8c-example.html">parse_config.c</a> for an example of the
simplified tree interface.
*/

View File

@ -1,175 +0,0 @@
/*
* 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.
*/
#include "api/yajl_parse.h"
#include "yajl_lex.h"
#include "yajl_parser.h"
#include "yajl_alloc.h"
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <assert.h>
const char *
yajl_status_to_string(yajl_status stat)
{
const char * statStr = "unknown";
switch (stat) {
case yajl_status_ok:
statStr = "ok, no error";
break;
case yajl_status_client_canceled:
statStr = "client canceled parse";
break;
case yajl_status_error:
statStr = "parse error";
break;
}
return statStr;
}
yajl_handle
yajl_alloc(const yajl_callbacks * callbacks,
yajl_alloc_funcs * afs,
void * ctx)
{
yajl_handle hand = NULL;
yajl_alloc_funcs afsBuffer;
/* first order of business is to set up memory allocation routines */
if (afs != NULL) {
if (afs->malloc == NULL || afs->realloc == NULL || afs->free == NULL)
{
return NULL;
}
} else {
yajl_set_default_alloc_funcs(&afsBuffer);
afs = &afsBuffer;
}
hand = (yajl_handle) YA_MALLOC(afs, sizeof(struct yajl_handle_t));
/* copy in pointers to allocation routines */
memcpy((void *) &(hand->alloc), (void *) afs, sizeof(yajl_alloc_funcs));
hand->callbacks = callbacks;
hand->ctx = ctx;
hand->lexer = NULL;
hand->bytesConsumed = 0;
hand->decodeBuf = yajl_buf_alloc(&(hand->alloc));
hand->flags = 0;
yajl_bs_init(hand->stateStack, &(hand->alloc));
yajl_bs_push(hand->stateStack, yajl_state_start);
return hand;
}
int
yajl_config(yajl_handle h, yajl_option opt, ...)
{
int rv = 1;
va_list ap;
va_start(ap, opt);
switch(opt) {
case yajl_allow_comments:
case yajl_dont_validate_strings:
case yajl_allow_trailing_garbage:
case yajl_allow_multiple_values:
case yajl_allow_partial_values:
if (va_arg(ap, int)) h->flags |= opt;
else h->flags &= ~opt;
break;
default:
rv = 0;
}
va_end(ap);
return rv;
}
void
yajl_free(yajl_handle handle)
{
yajl_bs_free(handle->stateStack);
yajl_buf_free(handle->decodeBuf);
if (handle->lexer) {
yajl_lex_free(handle->lexer);
handle->lexer = NULL;
}
YA_FREE(&(handle->alloc), handle);
}
yajl_status
yajl_parse(yajl_handle hand, const unsigned char * jsonText,
size_t jsonTextLen)
{
yajl_status status;
/* lazy allocation of the lexer */
if (hand->lexer == NULL) {
hand->lexer = yajl_lex_alloc(&(hand->alloc),
hand->flags & yajl_allow_comments,
!(hand->flags & yajl_dont_validate_strings));
}
status = yajl_do_parse(hand, jsonText, jsonTextLen);
return status;
}
yajl_status
yajl_complete_parse(yajl_handle hand)
{
/* The lexer is lazy allocated in the first call to parse. if parse is
* never called, then no data was provided to parse at all. This is a
* "premature EOF" error unless yajl_allow_partial_values is specified.
* allocating the lexer now is the simplest possible way to handle this
* case while preserving all the other semantics of the parser
* (multiple values, partial values, etc). */
if (hand->lexer == NULL) {
hand->lexer = yajl_lex_alloc(&(hand->alloc),
hand->flags & yajl_allow_comments,
!(hand->flags & yajl_dont_validate_strings));
}
return yajl_do_finish(hand);
}
unsigned char *
yajl_get_error(yajl_handle hand, int verbose,
const unsigned char * jsonText, size_t jsonTextLen)
{
return yajl_render_error_string(hand, jsonText, jsonTextLen, verbose);
}
size_t
yajl_get_bytes_consumed(yajl_handle hand)
{
if (!hand) return 0;
else return hand->bytesConsumed;
}
void
yajl_free_error(yajl_handle hand, unsigned char * str)
{
/* use memory allocation functions if set */
YA_FREE(&(hand->alloc), str);
}
/* XXX: add utility routines to parse from file */

View File

@ -1,49 +0,0 @@
/*
* 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.
*/
/**
* \file yajl_alloc.h
* default memory allocation routines for yajl which use malloc/realloc and
* free
*/
#include "yajl_alloc.h"
#include <stdlib.h>
static void * yajl_internal_malloc(void *ctx, size_t sz)
{
return malloc(sz);
}
static void * yajl_internal_realloc(void *ctx, void * previous,
size_t sz)
{
return realloc(previous, sz);
}
static void yajl_internal_free(void *ctx, void * ptr)
{
free(ptr);
}
void yajl_set_default_alloc_funcs(yajl_alloc_funcs * yaf)
{
yaf->malloc = yajl_internal_malloc;
yaf->free = yajl_internal_free;
yaf->realloc = yajl_internal_realloc;
yaf->ctx = NULL;
}

View File

@ -1,34 +0,0 @@
/*
* 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.
*/
/**
* \file yajl_alloc.h
* default memory allocation routines for yajl which use malloc/realloc and
* free
*/
#ifndef __YAJL_ALLOC_H__
#define __YAJL_ALLOC_H__
#include "api/yajl_common.h"
#define YA_MALLOC(afs, sz) (afs)->malloc((afs)->ctx, (sz))
#define YA_FREE(afs, ptr) (afs)->free((afs)->ctx, (ptr))
#define YA_REALLOC(afs, ptr, sz) (afs)->realloc((afs)->ctx, (ptr), (sz))
void yajl_set_default_alloc_funcs(yajl_alloc_funcs * yaf);
#endif

View File

@ -1,103 +0,0 @@
/*
* 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.
*/
#include "yajl_buf.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#define YAJL_BUF_INIT_SIZE 2048
struct yajl_buf_t {
size_t len;
size_t used;
unsigned char * data;
yajl_alloc_funcs * alloc;
};
static
void yajl_buf_ensure_available(yajl_buf buf, size_t want)
{
size_t need;
assert(buf != NULL);
/* first call */
if (buf->data == NULL) {
buf->len = YAJL_BUF_INIT_SIZE;
buf->data = (unsigned char *) YA_MALLOC(buf->alloc, buf->len);
buf->data[0] = 0;
}
need = buf->len;
while (want >= (need - buf->used)) need <<= 1;
if (need != buf->len) {
buf->data = (unsigned char *) YA_REALLOC(buf->alloc, buf->data, need);
buf->len = need;
}
}
yajl_buf yajl_buf_alloc(yajl_alloc_funcs * alloc)
{
yajl_buf b = YA_MALLOC(alloc, sizeof(struct yajl_buf_t));
memset((void *) b, 0, sizeof(struct yajl_buf_t));
b->alloc = alloc;
return b;
}
void yajl_buf_free(yajl_buf buf)
{
assert(buf != NULL);
if (buf->data) YA_FREE(buf->alloc, buf->data);
YA_FREE(buf->alloc, buf);
}
void yajl_buf_append(yajl_buf buf, const void * data, size_t len)
{
yajl_buf_ensure_available(buf, len);
if (len > 0) {
assert(data != NULL);
memcpy(buf->data + buf->used, data, len);
buf->used += len;
buf->data[buf->used] = 0;
}
}
void yajl_buf_clear(yajl_buf buf)
{
buf->used = 0;
if (buf->data) buf->data[buf->used] = 0;
}
const unsigned char * yajl_buf_data(yajl_buf buf)
{
return buf->data;
}
size_t yajl_buf_len(yajl_buf buf)
{
return buf->used;
}
void
yajl_buf_truncate(yajl_buf buf, size_t len)
{
assert(len <= buf->used);
buf->used = len;
}

View File

@ -1,57 +0,0 @@
/*
* 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.
*/
#ifndef __YAJL_BUF_H__
#define __YAJL_BUF_H__
#include "api/yajl_common.h"
#include "yajl_alloc.h"
/*
* Implementation/performance notes. If this were moved to a header
* only implementation using #define's where possible we might be
* able to sqeeze a little performance out of the guy by killing function
* call overhead. YMMV.
*/
/**
* yajl_buf is a buffer with exponential growth. the buffer ensures that
* you are always null padded.
*/
typedef struct yajl_buf_t * yajl_buf;
/* allocate a new buffer */
yajl_buf yajl_buf_alloc(yajl_alloc_funcs * alloc);
/* free the buffer */
void yajl_buf_free(yajl_buf buf);
/* append a number of bytes to the buffer */
void yajl_buf_append(yajl_buf buf, const void * data, size_t len);
/* empty the buffer */
void yajl_buf_clear(yajl_buf buf);
/* get a pointer to the beginning of the buffer */
const unsigned char * yajl_buf_data(yajl_buf buf);
/* get the length of the buffer */
size_t yajl_buf_len(yajl_buf buf);
/* truncate the buffer */
void yajl_buf_truncate(yajl_buf buf, size_t len);
#endif

View File

@ -1,69 +0,0 @@
/*
* 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.
*/
/*
* A header only implementation of a simple stack of bytes, used in YAJL
* to maintain parse state.
*/
#ifndef __YAJL_BYTESTACK_H__
#define __YAJL_BYTESTACK_H__
#include "api/yajl_common.h"
#define YAJL_BS_INC 128
typedef struct yajl_bytestack_t
{
unsigned char * stack;
size_t size;
size_t used;
yajl_alloc_funcs * yaf;
} yajl_bytestack;
/* initialize a bytestack */
#define yajl_bs_init(obs, _yaf) { \
(obs).stack = NULL; \
(obs).size = 0; \
(obs).used = 0; \
(obs).yaf = (_yaf); \
} \
/* initialize a bytestack */
#define yajl_bs_free(obs) \
if ((obs).stack) (obs).yaf->free((obs).yaf->ctx, (obs).stack);
#define yajl_bs_current(obs) \
(assert((obs).used > 0), (obs).stack[(obs).used - 1])
#define yajl_bs_push(obs, byte) { \
if (((obs).size - (obs).used) == 0) { \
(obs).size += YAJL_BS_INC; \
(obs).stack = (obs).yaf->realloc((obs).yaf->ctx,\
(void *) (obs).stack, (obs).size);\
} \
(obs).stack[((obs).used)++] = (byte); \
}
/* removes the top item of the stack, returns nothing */
#define yajl_bs_pop(obs) { ((obs).used)--; }
#define yajl_bs_set(obs, byte) \
(obs).stack[((obs).used) - 1] = (byte);
#endif

View File

@ -1,220 +0,0 @@
/*
* 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.
*/
#include "yajl_encode.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
static void CharToHex(unsigned char c, char * hexBuf)
{
const char * hexchar = "0123456789ABCDEF";
hexBuf[0] = hexchar[c >> 4];
hexBuf[1] = hexchar[c & 0x0F];
}
void
yajl_string_encode(const yajl_print_t print,
void * ctx,
const unsigned char * str,
size_t len,
int escape_solidus)
{
size_t beg = 0;
size_t end = 0;
char hexBuf[7];
hexBuf[0] = '\\'; hexBuf[1] = 'u'; hexBuf[2] = '0'; hexBuf[3] = '0';
hexBuf[6] = 0;
while (end < len) {
const char * escaped = NULL;
switch (str[end]) {
case '\r': escaped = "\\r"; break;
case '\n': escaped = "\\n"; break;
case '\\': escaped = "\\\\"; break;
/* it is not required to escape a solidus in JSON:
* read sec. 2.5: http://www.ietf.org/rfc/rfc4627.txt
* specifically, this production from the grammar:
* unescaped = %x20-21 / %x23-5B / %x5D-10FFFF
*/
case '/': if (escape_solidus) escaped = "\\/"; break;
case '"': escaped = "\\\""; break;
case '\f': escaped = "\\f"; break;
case '\b': escaped = "\\b"; break;
case '\t': escaped = "\\t"; break;
default:
if ((unsigned char) str[end] < 32) {
CharToHex(str[end], hexBuf + 4);
escaped = hexBuf;
}
break;
}
if (escaped != NULL) {
print(ctx, (const char *) (str + beg), end - beg);
print(ctx, escaped, (unsigned int)strlen(escaped));
beg = ++end;
} else {
++end;
}
}
print(ctx, (const char *) (str + beg), end - beg);
}
static void hexToDigit(unsigned int * val, const unsigned char * hex)
{
unsigned int i;
for (i=0;i<4;i++) {
unsigned char c = hex[i];
if (c >= 'A') c = (c & ~0x20) - 7;
c -= '0';
assert(!(c & 0xF0));
*val = (*val << 4) | c;
}
}
static void Utf32toUtf8(unsigned int codepoint, char * utf8Buf)
{
if (codepoint < 0x80) {
utf8Buf[0] = (char) codepoint;
utf8Buf[1] = 0;
} else if (codepoint < 0x0800) {
utf8Buf[0] = (char) ((codepoint >> 6) | 0xC0);
utf8Buf[1] = (char) ((codepoint & 0x3F) | 0x80);
utf8Buf[2] = 0;
} else if (codepoint < 0x10000) {
utf8Buf[0] = (char) ((codepoint >> 12) | 0xE0);
utf8Buf[1] = (char) (((codepoint >> 6) & 0x3F) | 0x80);
utf8Buf[2] = (char) ((codepoint & 0x3F) | 0x80);
utf8Buf[3] = 0;
} else if (codepoint < 0x200000) {
utf8Buf[0] =(char)((codepoint >> 18) | 0xF0);
utf8Buf[1] =(char)(((codepoint >> 12) & 0x3F) | 0x80);
utf8Buf[2] =(char)(((codepoint >> 6) & 0x3F) | 0x80);
utf8Buf[3] =(char)((codepoint & 0x3F) | 0x80);
utf8Buf[4] = 0;
} else {
utf8Buf[0] = '?';
utf8Buf[1] = 0;
}
}
void yajl_string_decode(yajl_buf buf, const unsigned char * str,
size_t len)
{
size_t beg = 0;
size_t end = 0;
while (end < len) {
if (str[end] == '\\') {
char utf8Buf[5];
const char * unescaped = "?";
yajl_buf_append(buf, str + beg, end - beg);
switch (str[++end]) {
case 'r': unescaped = "\r"; break;
case 'n': unescaped = "\n"; break;
case '\\': unescaped = "\\"; break;
case '/': unescaped = "/"; break;
case '"': unescaped = "\""; break;
case 'f': unescaped = "\f"; break;
case 'b': unescaped = "\b"; break;
case 't': unescaped = "\t"; break;
case 'u': {
unsigned int codepoint = 0;
hexToDigit(&codepoint, str + ++end);
end+=3;
/* check if this is a surrogate */
if ((codepoint & 0xFC00) == 0xD800) {
end++;
if (str[end] == '\\' && str[end + 1] == 'u') {
unsigned int surrogate = 0;
hexToDigit(&surrogate, str + end + 2);
codepoint =
(((codepoint & 0x3F) << 10) |
((((codepoint >> 6) & 0xF) + 1) << 16) |
(surrogate & 0x3FF));
end += 5;
} else {
unescaped = "?";
break;
}
}
Utf32toUtf8(codepoint, utf8Buf);
unescaped = utf8Buf;
if (codepoint == 0) {
yajl_buf_append(buf, unescaped, 1);
beg = ++end;
continue;
}
break;
}
default:
assert("this should never happen" == NULL);
}
yajl_buf_append(buf, unescaped, (unsigned int)strlen(unescaped));
beg = ++end;
} else {
end++;
}
}
yajl_buf_append(buf, str + beg, end - beg);
}
#define ADV_PTR s++; if (!(len--)) return 0;
int yajl_string_validate_utf8(const unsigned char * s, size_t len)
{
if (!len) return 1;
if (!s) return 0;
while (len--) {
/* single byte */
if (*s <= 0x7f) {
/* noop */
}
/* two byte */
else if ((*s >> 5) == 0x6) {
ADV_PTR;
if (!((*s >> 6) == 0x2)) return 0;
}
/* three byte */
else if ((*s >> 4) == 0x0e) {
ADV_PTR;
if (!((*s >> 6) == 0x2)) return 0;
ADV_PTR;
if (!((*s >> 6) == 0x2)) return 0;
}
/* four byte */
else if ((*s >> 3) == 0x1e) {
ADV_PTR;
if (!((*s >> 6) == 0x2)) return 0;
ADV_PTR;
if (!((*s >> 6) == 0x2)) return 0;
ADV_PTR;
if (!((*s >> 6) == 0x2)) return 0;
} else {
return 0;
}
s++;
}
return 1;
}

Some files were not shown because too many files have changed in this diff Show More