diff options
author | Brad King <brad.king@kitware.com> | 2017-08-28 11:33:06 -0400 |
---|---|---|
committer | Brad King <brad.king@kitware.com> | 2017-08-30 10:16:59 -0400 |
commit | e09819557f5bf29f8414d5165e2c3e8ad8dee2d1 (patch) | |
tree | a12c94c8890665c2c45d7a859f897efe8931b4ad /Utilities/cmjsoncpp/src/lib_json | |
parent | a7241206bf4e1cb6ebbb24a05334cf4b9e2328df (diff) | |
parent | 6e9ef8d1b18f8a33ab97483437da819e342a9997 (diff) | |
download | cmake-e09819557f5bf29f8414d5165e2c3e8ad8dee2d1.tar.gz |
Merge branch 'upstream-jsoncpp' into update-jsoncpp
* upstream-jsoncpp:
jsoncpp 2017-08-27 (4cfae897)
Diffstat (limited to 'Utilities/cmjsoncpp/src/lib_json')
-rw-r--r-- | Utilities/cmjsoncpp/src/lib_json/json_batchallocator.h | 121 | ||||
-rw-r--r-- | Utilities/cmjsoncpp/src/lib_json/json_internalarray.inl | 360 | ||||
-rw-r--r-- | Utilities/cmjsoncpp/src/lib_json/json_internalmap.inl | 473 | ||||
-rw-r--r-- | Utilities/cmjsoncpp/src/lib_json/json_reader.cpp | 1433 | ||||
-rw-r--r-- | Utilities/cmjsoncpp/src/lib_json/json_tool.h | 44 | ||||
-rw-r--r-- | Utilities/cmjsoncpp/src/lib_json/json_value.cpp | 890 | ||||
-rw-r--r-- | Utilities/cmjsoncpp/src/lib_json/json_valueiterator.inl | 138 | ||||
-rw-r--r-- | Utilities/cmjsoncpp/src/lib_json/json_writer.cpp | 823 |
8 files changed, 2588 insertions, 1694 deletions
diff --git a/Utilities/cmjsoncpp/src/lib_json/json_batchallocator.h b/Utilities/cmjsoncpp/src/lib_json/json_batchallocator.h deleted file mode 100644 index 2fbef7a860..0000000000 --- a/Utilities/cmjsoncpp/src/lib_json/json_batchallocator.h +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright 2007-2010 Baptiste Lepilleur -// Distributed under MIT license, or public domain if desired and -// recognized in your jurisdiction. -// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE - -#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: - 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 diff --git a/Utilities/cmjsoncpp/src/lib_json/json_internalarray.inl b/Utilities/cmjsoncpp/src/lib_json/json_internalarray.inl deleted file mode 100644 index 9ee15e9dbf..0000000000 --- a/Utilities/cmjsoncpp/src/lib_json/json_internalarray.inl +++ /dev/null @@ -1,360 +0,0 @@ -// Copyright 2007-2010 Baptiste Lepilleur -// Distributed under MIT license, or public domain if desired and -// recognized in your jurisdiction. -// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE - -// included by json_value.cpp - -namespace Json { - -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// 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); - JSON_ASSERT_MESSAGE(newIndexes, "Couldn't realloc."); - 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); - JSON_ASSERT_MESSAGE(newIndexes, "Couldn't realloc."); - 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), size_(other.size_), pageCount_(0) { - 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=(ValueInternalArray other) { - swap(other); - 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; -} - -} // namespace Json diff --git a/Utilities/cmjsoncpp/src/lib_json/json_internalmap.inl b/Utilities/cmjsoncpp/src/lib_json/json_internalmap.inl deleted file mode 100644 index ef3f3302dc..0000000000 --- a/Utilities/cmjsoncpp/src/lib_json/json_internalmap.inl +++ /dev/null @@ -1,473 +0,0 @@ -// Copyright 2007-2010 Baptiste Lepilleur -// Distributed under MIT license, or public domain if desired and -// recognized in your jurisdiction. -// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE - -// included by json_value.cpp - -namespace Json { - -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// ////////////////////////////////////////////////////////////////// -// 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=(ValueInternalMap other) { - swap(other); - 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 ¤t->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 = ¤t->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 = 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; -} - -} // namespace Json diff --git a/Utilities/cmjsoncpp/src/lib_json/json_reader.cpp b/Utilities/cmjsoncpp/src/lib_json/json_reader.cpp index 7b338285cb..0886dabab8 100644 --- a/Utilities/cmjsoncpp/src/lib_json/json_reader.cpp +++ b/Utilities/cmjsoncpp/src/lib_json/json_reader.cpp @@ -1,4 +1,5 @@ -// Copyright 2007-2011 Baptiste Lepilleur +// Copyright 2007-2011 Baptiste Lepilleur and The JsonCpp Authors +// Copyright (C) 2016 InfoTeCS JSC. All rights reserved. // Distributed under MIT license, or public domain if desired and // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE @@ -14,18 +15,51 @@ #include <assert.h> #include <string.h> #include <istream> - -#if defined(_MSC_VER) && _MSC_VER < 1500 // VC++ 8.0 and below +#include <sstream> +#include <memory> +#include <set> +#include <limits> + +#if defined(_MSC_VER) +#if !defined(WINCE) && defined(__STDC_SECURE_LIB__) && _MSC_VER >= 1500 // VC++ 9.0 and above +#define snprintf sprintf_s +#elif _MSC_VER >= 1900 // VC++ 14.0 and above +#define snprintf std::snprintf +#else #define snprintf _snprintf #endif +#elif defined(__ANDROID__) || defined(__QNXNTO__) +#define snprintf snprintf +#elif __cplusplus >= 201103L +#if !defined(__MINGW32__) && !defined(__CYGWIN__) +#define snprintf std::snprintf +#endif +#endif + +#if defined(__QNXNTO__) +#define sscanf std::sscanf +#endif #if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0 // Disable warning about strdup being deprecated. #pragma warning(disable : 4996) #endif +// Define JSONCPP_DEPRECATED_STACK_LIMIT as an appropriate integer at compile time to change the stack limit +#if !defined(JSONCPP_DEPRECATED_STACK_LIMIT) +#define JSONCPP_DEPRECATED_STACK_LIMIT 1000 +#endif + +static size_t const stackLimit_g = JSONCPP_DEPRECATED_STACK_LIMIT; // see readValue() + namespace Json { +#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520) +typedef std::unique_ptr<CharReader> CharReaderPtr; +#else +typedef std::auto_ptr<CharReader> CharReaderPtr; +#endif + // Implementation of class Features // //////////////////////////////// @@ -47,24 +81,7 @@ Features Features::strictMode() { // 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) { +bool Reader::containsNewLine(Reader::Location begin, Reader::Location end) { for (; begin < end; ++begin) if (*begin == '\n' || *begin == '\r') return true; @@ -86,7 +103,7 @@ Reader::Reader(const Features& features) bool Reader::parse(const std::string& document, Value& root, bool collectComments) { - document_ = document; + document_.assign(document.begin(), document.end()); const char* begin = document_.c_str(); const char* end = begin + document_.length(); return parse(begin, end, root, collectComments); @@ -98,11 +115,11 @@ bool Reader::parse(std::istream& sin, Value& root, bool collectComments) { // 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 + // Since JSONCPP_STRING is reference-counted, this at least does not // create an extra copy. - std::string doc; + JSONCPP_STRING doc; std::getline(sin, doc, (char)EOF); - return parse(doc, root, collectComments); + return parse(doc.data(), doc.data() + doc.size(), root, collectComments); } bool Reader::parse(const char* beginDoc, @@ -119,7 +136,7 @@ bool Reader::parse(const char* beginDoc, current_ = begin_; lastValueEnd_ = 0; lastValue_ = 0; - commentsBefore_ = ""; + commentsBefore_.clear(); errors_.clear(); while (!nodes_.empty()) nodes_.pop(); @@ -147,21 +164,18 @@ bool Reader::parse(const char* beginDoc, } bool Reader::readValue() { + // readValue() may call itself only if it calls readObject() or ReadArray(). + // These methods execute nodes_.push() just before and nodes_.pop)() just after calling readValue(). + // parse() executes one nodes_.push(), so > instead of >=. + if (nodes_.size() > stackLimit_g) throwRuntimeError("Exceeded stackLimit in readValue()."); + Token token; skipCommentTokens(token); bool successful = true; if (collectComments_ && !commentsBefore_.empty()) { - // Remove newline characters at the end of the comments - size_t lastNonNewline = commentsBefore_.find_last_not_of("\r\n"); - if (lastNonNewline != std::string::npos) { - commentsBefore_.erase(lastNonNewline + 1); - } else { - commentsBefore_.clear(); - } - currentValue().setComment(commentsBefore_, commentBefore); - commentsBefore_ = ""; + commentsBefore_.clear(); } switch (token.type_) { @@ -180,31 +194,42 @@ bool Reader::readValue() { successful = decodeString(token); break; case tokenTrue: - currentValue() = true; + { + Value v(true); + currentValue().swapPayload(v); currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_); + } break; case tokenFalse: - currentValue() = false; + { + Value v(false); + currentValue().swapPayload(v); currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_); + } break; case tokenNull: - currentValue() = Value(); + { + Value v; + currentValue().swapPayload(v); currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_); + } break; case tokenArraySeparator: + case tokenObjectEnd: + case tokenArrayEnd: if (features_.allowDroppedNullPlaceholders_) { // "Un-read" the current token and mark the current value as a null // token. current_--; - currentValue() = Value(); + Value v; + currentValue().swapPayload(v); currentValue().setOffsetStart(current_ - begin_ - 1); currentValue().setOffsetLimit(current_ - begin_); break; - } - // Else, fall through... + } // Else, fall through... default: currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_); @@ -229,13 +254,6 @@ void Reader::skipCommentTokens(Token& 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_; @@ -351,19 +369,39 @@ bool Reader::readComment() { return true; } +JSONCPP_STRING Reader::normalizeEOL(Reader::Location begin, Reader::Location end) { + JSONCPP_STRING normalized; + normalized.reserve(static_cast<size_t>(end - begin)); + Reader::Location current = begin; + while (current != end) { + char c = *current++; + if (c == '\r') { + if (current != end && *current == '\n') + // convert dos EOL + ++current; + // convert Mac EOL + normalized += '\n'; + } else { + normalized += c; + } + } + return normalized; +} + void Reader::addComment(Location begin, Location end, CommentPlacement placement) { assert(collectComments_); + const JSONCPP_STRING& normalized = normalizeEOL(begin, end); if (placement == commentAfterOnSameLine) { assert(lastValue_ != 0); - lastValue_->setComment(std::string(begin, end), placement); + lastValue_->setComment(normalized, placement); } else { - commentsBefore_ += std::string(begin, end); + commentsBefore_ += normalized; } } bool Reader::readCStyleComment() { - while (current_ != end_) { + while ((current_ + 1) < end_) { Char c = getNextChar(); if (c == '*' && *current_ == '/') break; @@ -374,23 +412,43 @@ bool Reader::readCStyleComment() { bool Reader::readCppStyleComment() { while (current_ != end_) { Char c = getNextChar(); - if (c == '\r' || c == '\n') + if (c == '\n') break; + if (c == '\r') { + // Consume DOS EOL. It will be normalized in addComment. + if (current_ != end_ && *current_ == '\n') + getNextChar(); + // Break on Moc OS 9 EOL. + break; + } } return true; } void Reader::readNumber() { - while (current_ != end_) { - if (!(*current_ >= '0' && *current_ <= '9') && - !in(*current_, '.', 'e', 'E', '+', '-')) - break; - ++current_; + const char *p = current_; + char c = '0'; // stopgap for already consumed character + // integral part + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : '\0'; + // fractional part + if (c == '.') { + c = (current_ = p) < end_ ? *p++ : '\0'; + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : '\0'; + } + // exponential part + if (c == 'e' || c == 'E') { + c = (current_ = p) < end_ ? *p++ : '\0'; + if (c == '+' || c == '-') + c = (current_ = p) < end_ ? *p++ : '\0'; + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : '\0'; } } bool Reader::readString() { - Char c = 0; + Char c = '\0'; while (current_ != end_) { c = getNextChar(); if (c == '\\') @@ -403,8 +461,9 @@ bool Reader::readString() { bool Reader::readObject(Token& tokenStart) { Token tokenName; - std::string name; - currentValue() = Value(objectValue); + JSONCPP_STRING name; + Value init(objectValue); + currentValue().swapPayload(init); currentValue().setOffsetStart(tokenStart.start_ - begin_); while (readToken(tokenName)) { bool initialTokenOk = true; @@ -414,7 +473,7 @@ bool Reader::readObject(Token& tokenStart) { break; if (tokenName.type_ == tokenObjectEnd && name.empty()) // empty object return true; - name = ""; + name.clear(); if (tokenName.type_ == tokenString) { if (!decodeString(tokenName, name)) return recoverFromError(tokenObjectEnd); @@ -422,7 +481,7 @@ bool Reader::readObject(Token& tokenStart) { Value numberName; if (!decodeNumber(tokenName, numberName)) return recoverFromError(tokenObjectEnd); - name = numberName.asString(); + name = JSONCPP_STRING(numberName.asCString()); } else { break; } @@ -457,10 +516,11 @@ bool Reader::readObject(Token& tokenStart) { } bool Reader::readArray(Token& tokenStart) { - currentValue() = Value(arrayValue); + Value init(arrayValue); + currentValue().swapPayload(init); currentValue().setOffsetStart(tokenStart.start_ - begin_); skipSpaces(); - if (*current_ == ']') // empty array + if (current_ != end_ && *current_ == ']') // empty array { Token endArray; readToken(endArray); @@ -497,20 +557,1021 @@ bool Reader::decodeNumber(Token& token) { Value decoded; if (!decodeNumber(token, decoded)) return false; - currentValue() = decoded; + currentValue().swapPayload(decoded); currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_); return true; } bool Reader::decodeNumber(Token& token, Value& decoded) { - bool isDouble = false; - for (Location inspect = token.start_; inspect != token.end_; ++inspect) { - isDouble = isDouble || in(*inspect, '.', 'e', 'E', '+') || - (*inspect == '-' && inspect != token.start_); + // Attempts to parse the number as an integer. If the number is + // larger than the maximum supported value of an integer then + // we decode the number as a double. + Location current = token.start_; + bool isNegative = *current == '-'; + if (isNegative) + ++current; + // TODO: Help the compiler do the div and mod at compile time or get rid of them. + Value::LargestUInt maxIntegerValue = + isNegative ? Value::LargestUInt(Value::maxLargestInt) + 1 + : Value::maxLargestUInt; + Value::LargestUInt threshold = maxIntegerValue / 10; + Value::LargestUInt value = 0; + while (current < token.end_) { + Char c = *current++; + if (c < '0' || c > '9') + return decodeDouble(token, decoded); + Value::UInt digit(static_cast<Value::UInt>(c - '0')); + if (value >= threshold) { + // We've hit or exceeded the max value divided by 10 (rounded down). If + // a) we've only just touched the limit, b) this is the last digit, and + // c) it's small enough to fit in that rounding delta, we're okay. + // Otherwise treat this number as a double to avoid overflow. + if (value > threshold || current != token.end_ || + digit > maxIntegerValue % 10) { + return decodeDouble(token, decoded); + } + } + value = value * 10 + digit; + } + if (isNegative && value == maxIntegerValue) + decoded = Value::minLargestInt; + else if (isNegative) + decoded = -Value::LargestInt(value); + else if (value <= Value::LargestUInt(Value::maxInt)) + decoded = Value::LargestInt(value); + else + decoded = value; + return true; +} + +bool Reader::decodeDouble(Token& token) { + Value decoded; + if (!decodeDouble(token, decoded)) + return false; + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool Reader::decodeDouble(Token& token, Value& decoded) { + double value = 0; + JSONCPP_STRING buffer(token.start_, token.end_); + JSONCPP_ISTRINGSTREAM is(buffer); + if (!(is >> value)) + return addError("'" + JSONCPP_STRING(token.start_, token.end_) + + "' is not a number.", + token); + decoded = value; + return true; +} + +bool Reader::decodeString(Token& token) { + JSONCPP_STRING decoded_string; + if (!decodeString(token, decoded_string)) + return false; + Value decoded(decoded_string); + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool Reader::decodeString(Token& token, JSONCPP_STRING& decoded) { + decoded.reserve(static_cast<size_t>(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& ret_unicode) { + if (end - current < 4) + return addError( + "Bad unicode escape sequence in string: four digits expected.", + token, + current); + int 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); + } + ret_unicode = static_cast<unsigned int>(unicode); + return true; +} + +bool +Reader::addError(const JSONCPP_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) { + size_t const errorCount = errors_.size(); + Token skip; + for (;;) { + 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 JSONCPP_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; +} + +JSONCPP_STRING Reader::getLocationLineAndColumn(Location location) const { + int line, column; + getLocationLineAndColumn(location, line, column); + char buffer[18 + 16 + 16 + 1]; + snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column); + return buffer; +} + +// Deprecated. Preserved for backward compatibility +JSONCPP_STRING Reader::getFormatedErrorMessages() const { + return getFormattedErrorMessages(); +} + +JSONCPP_STRING Reader::getFormattedErrorMessages() const { + JSONCPP_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::vector<Reader::StructuredError> Reader::getStructuredErrors() const { + std::vector<Reader::StructuredError> allErrors; + for (Errors::const_iterator itError = errors_.begin(); + itError != errors_.end(); + ++itError) { + const ErrorInfo& error = *itError; + Reader::StructuredError structured; + structured.offset_start = error.token_.start_ - begin_; + structured.offset_limit = error.token_.end_ - begin_; + structured.message = error.message_; + allErrors.push_back(structured); + } + return allErrors; +} + +bool Reader::pushError(const Value& value, const JSONCPP_STRING& message) { + ptrdiff_t const length = end_ - begin_; + if(value.getOffsetStart() > length + || value.getOffsetLimit() > length) + return false; + Token token; + token.type_ = tokenError; + token.start_ = begin_ + value.getOffsetStart(); + token.end_ = end_ + value.getOffsetLimit(); + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = 0; + errors_.push_back(info); + return true; +} + +bool Reader::pushError(const Value& value, const JSONCPP_STRING& message, const Value& extra) { + ptrdiff_t const length = end_ - begin_; + if(value.getOffsetStart() > length + || value.getOffsetLimit() > length + || extra.getOffsetLimit() > length) + return false; + Token token; + token.type_ = tokenError; + token.start_ = begin_ + value.getOffsetStart(); + token.end_ = begin_ + value.getOffsetLimit(); + ErrorInfo info; + info.token_ = token; + info.message_ = message; + info.extra_ = begin_ + extra.getOffsetStart(); + errors_.push_back(info); + return true; +} + +bool Reader::good() const { + return !errors_.size(); +} + +// exact copy of Features +class OurFeatures { +public: + static OurFeatures all(); + bool allowComments_; + bool strictRoot_; + bool allowDroppedNullPlaceholders_; + bool allowNumericKeys_; + bool allowSingleQuotes_; + bool failIfExtra_; + bool rejectDupKeys_; + bool allowSpecialFloats_; + int stackLimit_; +}; // OurFeatures + +// exact copy of Implementation of class Features +// //////////////////////////////// + +OurFeatures OurFeatures::all() { return OurFeatures(); } + +// Implementation of class Reader +// //////////////////////////////// + +// exact copy of Reader, renamed to OurReader +class OurReader { +public: + typedef char Char; + typedef const Char* Location; + struct StructuredError { + ptrdiff_t offset_start; + ptrdiff_t offset_limit; + JSONCPP_STRING message; + }; + + OurReader(OurFeatures const& features); + bool parse(const char* beginDoc, + const char* endDoc, + Value& root, + bool collectComments = true); + JSONCPP_STRING getFormattedErrorMessages() const; + std::vector<StructuredError> getStructuredErrors() const; + bool pushError(const Value& value, const JSONCPP_STRING& message); + bool pushError(const Value& value, const JSONCPP_STRING& message, const Value& extra); + bool good() const; + +private: + OurReader(OurReader const&); // no impl + void operator=(OurReader const&); // no impl + + enum TokenType { + tokenEndOfStream = 0, + tokenObjectBegin, + tokenObjectEnd, + tokenArrayBegin, + tokenArrayEnd, + tokenString, + tokenNumber, + tokenTrue, + tokenFalse, + tokenNull, + tokenNaN, + tokenPosInf, + tokenNegInf, + tokenArraySeparator, + tokenMemberSeparator, + tokenComment, + tokenError + }; + + class Token { + public: + TokenType type_; + Location start_; + Location end_; + }; + + class ErrorInfo { + public: + Token token_; + JSONCPP_STRING message_; + Location extra_; + }; + + typedef std::deque<ErrorInfo> Errors; + + bool readToken(Token& token); + void skipSpaces(); + bool match(Location pattern, int patternLength); + bool readComment(); + bool readCStyleComment(); + bool readCppStyleComment(); + bool readString(); + bool readStringSingleQuote(); + bool readNumber(bool checkInf); + bool readValue(); + bool readObject(Token& token); + bool readArray(Token& token); + bool decodeNumber(Token& token); + bool decodeNumber(Token& token, Value& decoded); + bool decodeString(Token& token); + bool decodeString(Token& token, JSONCPP_STRING& decoded); + bool decodeDouble(Token& token); + bool decodeDouble(Token& token, Value& decoded); + 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 JSONCPP_STRING& message, Token& token, Location extra = 0); + bool recoverFromError(TokenType skipUntilToken); + bool addErrorAndRecover(const JSONCPP_STRING& message, + Token& token, + TokenType skipUntilToken); + void skipUntilSpace(); + Value& currentValue(); + Char getNextChar(); + void + getLocationLineAndColumn(Location location, int& line, int& column) const; + JSONCPP_STRING getLocationLineAndColumn(Location location) const; + void addComment(Location begin, Location end, CommentPlacement placement); + void skipCommentTokens(Token& token); + + static JSONCPP_STRING normalizeEOL(Location begin, Location end); + static bool containsNewLine(Location begin, Location end); + + typedef std::stack<Value*> Nodes; + Nodes nodes_; + Errors errors_; + JSONCPP_STRING document_; + Location begin_; + Location end_; + Location current_; + Location lastValueEnd_; + Value* lastValue_; + JSONCPP_STRING commentsBefore_; + + OurFeatures const features_; + bool collectComments_; +}; // OurReader + +// complete copy of Read impl, for OurReader + +bool OurReader::containsNewLine(OurReader::Location begin, OurReader::Location end) { + for (; begin < end; ++begin) + if (*begin == '\n' || *begin == '\r') + return true; + return false; +} + +OurReader::OurReader(OurFeatures const& features) + : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(), + lastValue_(), commentsBefore_(), + features_(features), collectComments_() { +} + +bool OurReader::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_.clear(); + errors_.clear(); + while (!nodes_.empty()) + nodes_.pop(); + nodes_.push(&root); + + bool successful = readValue(); + Token token; + skipCommentTokens(token); + if (features_.failIfExtra_) { + if ((features_.strictRoot_ || token.type_ != tokenError) && token.type_ != tokenEndOfStream) { + addError("Extra non-whitespace after JSON value.", token); + return false; + } + } + 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 OurReader::readValue() { + // To preserve the old behaviour we cast size_t to int. + if (static_cast<int>(nodes_.size()) > features_.stackLimit_) throwRuntimeError("Exceeded stackLimit in readValue()."); + Token token; + skipCommentTokens(token); + bool successful = true; + + if (collectComments_ && !commentsBefore_.empty()) { + currentValue().setComment(commentsBefore_, commentBefore); + commentsBefore_.clear(); + } + + switch (token.type_) { + case tokenObjectBegin: + successful = readObject(token); + currentValue().setOffsetLimit(current_ - begin_); + break; + case tokenArrayBegin: + successful = readArray(token); + currentValue().setOffsetLimit(current_ - begin_); + break; + case tokenNumber: + successful = decodeNumber(token); + break; + case tokenString: + successful = decodeString(token); + break; + case tokenTrue: + { + Value v(true); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenFalse: + { + Value v(false); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenNull: + { + Value v; + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenNaN: + { + Value v(std::numeric_limits<double>::quiet_NaN()); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenPosInf: + { + Value v(std::numeric_limits<double>::infinity()); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenNegInf: + { + Value v(-std::numeric_limits<double>::infinity()); + currentValue().swapPayload(v); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + } + break; + case tokenArraySeparator: + case tokenObjectEnd: + case tokenArrayEnd: + if (features_.allowDroppedNullPlaceholders_) { + // "Un-read" the current token and mark the current value as a null + // token. + current_--; + Value v; + currentValue().swapPayload(v); + currentValue().setOffsetStart(current_ - begin_ - 1); + currentValue().setOffsetLimit(current_ - begin_); + break; + } // else, fall through ... + default: + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return addError("Syntax error: value, object or array expected.", token); + } + + if (collectComments_) { + lastValueEnd_ = current_; + lastValue_ = ¤tValue(); + } + + return successful; +} + +void OurReader::skipCommentTokens(Token& token) { + if (features_.allowComments_) { + do { + readToken(token); + } while (token.type_ == tokenComment); + } else { + readToken(token); + } +} + +bool OurReader::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 '\'': + if (features_.allowSingleQuotes_) { + token.type_ = tokenString; + ok = readStringSingleQuote(); + break; + } // else continue + 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': + token.type_ = tokenNumber; + readNumber(false); + break; + case '-': + if (readNumber(true)) { + token.type_ = tokenNumber; + } else { + token.type_ = tokenNegInf; + ok = features_.allowSpecialFloats_ && match("nfinity", 7); + } + 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 'N': + if (features_.allowSpecialFloats_) { + token.type_ = tokenNaN; + ok = match("aN", 2); + } else { + ok = false; + } + break; + case 'I': + if (features_.allowSpecialFloats_) { + token.type_ = tokenPosInf; + ok = match("nfinity", 7); + } else { + ok = false; + } + break; + case ',': + token.type_ = tokenArraySeparator; + break; + case ':': + token.type_ = tokenMemberSeparator; + break; + case 0: + token.type_ = tokenEndOfStream; + break; + default: + ok = false; + break; } - if (isDouble) - return decodeDouble(token, decoded); + if (!ok) + token.type_ = tokenError; + token.end_ = current_; + return true; +} + +void OurReader::skipSpaces() { + while (current_ != end_) { + Char c = *current_; + if (c == ' ' || c == '\t' || c == '\r' || c == '\n') + ++current_; + else + break; + } +} + +bool OurReader::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 OurReader::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; +} + +JSONCPP_STRING OurReader::normalizeEOL(OurReader::Location begin, OurReader::Location end) { + JSONCPP_STRING normalized; + normalized.reserve(static_cast<size_t>(end - begin)); + OurReader::Location current = begin; + while (current != end) { + char c = *current++; + if (c == '\r') { + if (current != end && *current == '\n') + // convert dos EOL + ++current; + // convert Mac EOL + normalized += '\n'; + } else { + normalized += c; + } + } + return normalized; +} + +void +OurReader::addComment(Location begin, Location end, CommentPlacement placement) { + assert(collectComments_); + const JSONCPP_STRING& normalized = normalizeEOL(begin, end); + if (placement == commentAfterOnSameLine) { + assert(lastValue_ != 0); + lastValue_->setComment(normalized, placement); + } else { + commentsBefore_ += normalized; + } +} + +bool OurReader::readCStyleComment() { + while ((current_ + 1) < end_) { + Char c = getNextChar(); + if (c == '*' && *current_ == '/') + break; + } + return getNextChar() == '/'; +} + +bool OurReader::readCppStyleComment() { + while (current_ != end_) { + Char c = getNextChar(); + if (c == '\n') + break; + if (c == '\r') { + // Consume DOS EOL. It will be normalized in addComment. + if (current_ != end_ && *current_ == '\n') + getNextChar(); + // Break on Moc OS 9 EOL. + break; + } + } + return true; +} + +bool OurReader::readNumber(bool checkInf) { + const char *p = current_; + if (checkInf && p != end_ && *p == 'I') { + current_ = ++p; + return false; + } + char c = '0'; // stopgap for already consumed character + // integral part + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : '\0'; + // fractional part + if (c == '.') { + c = (current_ = p) < end_ ? *p++ : '\0'; + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : '\0'; + } + // exponential part + if (c == 'e' || c == 'E') { + c = (current_ = p) < end_ ? *p++ : '\0'; + if (c == '+' || c == '-') + c = (current_ = p) < end_ ? *p++ : '\0'; + while (c >= '0' && c <= '9') + c = (current_ = p) < end_ ? *p++ : '\0'; + } + return true; +} +bool OurReader::readString() { + Char c = 0; + while (current_ != end_) { + c = getNextChar(); + if (c == '\\') + getNextChar(); + else if (c == '"') + break; + } + return c == '"'; +} + + +bool OurReader::readStringSingleQuote() { + Char c = 0; + while (current_ != end_) { + c = getNextChar(); + if (c == '\\') + getNextChar(); + else if (c == '\'') + break; + } + return c == '\''; +} + +bool OurReader::readObject(Token& tokenStart) { + Token tokenName; + JSONCPP_STRING name; + Value init(objectValue); + currentValue().swapPayload(init); + currentValue().setOffsetStart(tokenStart.start_ - begin_); + 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; + name.clear(); + if (tokenName.type_ == tokenString) { + if (!decodeString(tokenName, name)) + return recoverFromError(tokenObjectEnd); + } else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) { + Value numberName; + if (!decodeNumber(tokenName, numberName)) + return recoverFromError(tokenObjectEnd); + name = numberName.asString(); + } else { + break; + } + + Token colon; + if (!readToken(colon) || colon.type_ != tokenMemberSeparator) { + return addErrorAndRecover( + "Missing ':' after object member name", colon, tokenObjectEnd); + } + if (name.length() >= (1U<<30)) throwRuntimeError("keylength >= 2^30"); + if (features_.rejectDupKeys_ && currentValue().isMember(name)) { + JSONCPP_STRING msg = "Duplicate key: '" + name + "'"; + return addErrorAndRecover( + msg, tokenName, 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 OurReader::readArray(Token& tokenStart) { + Value init(arrayValue); + currentValue().swapPayload(init); + currentValue().setOffsetStart(tokenStart.start_ - begin_); + skipSpaces(); + if (current_ != end_ && *current_ == ']') // empty array + { + Token endArray; + readToken(endArray); + return true; + } + int index = 0; + for (;;) { + 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 OurReader::decodeNumber(Token& token) { + Value decoded; + if (!decodeNumber(token, decoded)) + return false; + currentValue().swapPayload(decoded); + currentValue().setOffsetStart(token.start_ - begin_); + currentValue().setOffsetLimit(token.end_ - begin_); + return true; +} + +bool OurReader::decodeNumber(Token& token, Value& decoded) { // Attempts to parse the number as an integer. If the number is // larger than the maximum supported value of an integer then // we decode the number as a double. @@ -518,6 +1579,7 @@ bool Reader::decodeNumber(Token& token, Value& decoded) { bool isNegative = *current == '-'; if (isNegative) ++current; + // TODO: Help the compiler do the div and mod at compile time or get rid of them. Value::LargestUInt maxIntegerValue = isNegative ? Value::LargestUInt(-Value::minLargestInt) : Value::maxLargestUInt; @@ -526,9 +1588,7 @@ bool Reader::decodeNumber(Token& token, Value& decoded) { 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); + return decodeDouble(token, decoded); Value::UInt digit(static_cast<Value::UInt>(c - '0')); if (value >= threshold) { // We've hit or exceeded the max value divided by 10 (rounded down). If @@ -551,26 +1611,27 @@ bool Reader::decodeNumber(Token& token, Value& decoded) { return true; } -bool Reader::decodeDouble(Token& token) { +bool OurReader::decodeDouble(Token& token) { Value decoded; if (!decodeDouble(token, decoded)) return false; - currentValue() = decoded; + currentValue().swapPayload(decoded); currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_); return true; } -bool Reader::decodeDouble(Token& token, Value& decoded) { +bool OurReader::decodeDouble(Token& token, Value& decoded) { double value = 0; const int bufferSize = 32; int count; - int length = int(token.end_ - token.start_); + ptrdiff_t const length = token.end_ - token.start_; // Sanity check to avoid buffer overflow exploits. if (length < 0) { return addError("Unable to parse token length", token); } + size_t const ulength = static_cast<size_t>(length); // Avoid using a string constant for the format control string given to // sscanf, as this can cause hard to debug crashes on OS X. See here for more @@ -581,34 +1642,36 @@ bool Reader::decodeDouble(Token& token, Value& decoded) { if (length <= bufferSize) { Char buffer[bufferSize + 1]; - memcpy(buffer, token.start_, length); + memcpy(buffer, token.start_, ulength); buffer[length] = 0; + fixNumericLocaleInput(buffer, buffer + length); count = sscanf(buffer, format, &value); } else { - std::string buffer(token.start_, token.end_); + JSONCPP_STRING buffer(token.start_, token.end_); count = sscanf(buffer.c_str(), format, &value); } if (count != 1) - return addError("'" + std::string(token.start_, token.end_) + + return addError("'" + JSONCPP_STRING(token.start_, token.end_) + "' is not a number.", token); decoded = value; return true; } -bool Reader::decodeString(Token& token) { - std::string decoded; - if (!decodeString(token, decoded)) +bool OurReader::decodeString(Token& token) { + JSONCPP_STRING decoded_string; + if (!decodeString(token, decoded_string)) return false; - currentValue() = decoded; + Value decoded(decoded_string); + currentValue().swapPayload(decoded); currentValue().setOffsetStart(token.start_ - begin_); currentValue().setOffsetLimit(token.end_ - begin_); return true; } -bool Reader::decodeString(Token& token, std::string& decoded) { - decoded.reserve(token.end_ - token.start_ - 2); +bool OurReader::decodeString(Token& token, JSONCPP_STRING& decoded) { + decoded.reserve(static_cast<size_t>(token.end_ - token.start_ - 2)); Location current = token.start_ + 1; // skip '"' Location end = token.end_ - 1; // do not include '"' while (current != end) { @@ -660,7 +1723,7 @@ bool Reader::decodeString(Token& token, std::string& decoded) { return true; } -bool Reader::decodeUnicodeCodePoint(Token& token, +bool OurReader::decodeUnicodeCodePoint(Token& token, Location& current, Location end, unsigned int& unicode) { @@ -689,16 +1752,16 @@ bool Reader::decodeUnicodeCodePoint(Token& token, return true; } -bool Reader::decodeUnicodeEscapeSequence(Token& token, +bool OurReader::decodeUnicodeEscapeSequence(Token& token, Location& current, Location end, - unsigned int& unicode) { + unsigned int& ret_unicode) { if (end - current < 4) return addError( "Bad unicode escape sequence in string: four digits expected.", token, current); - unicode = 0; + int unicode = 0; for (int index = 0; index < 4; ++index) { Char c = *current++; unicode *= 16; @@ -714,11 +1777,12 @@ bool Reader::decodeUnicodeEscapeSequence(Token& token, token, current); } + ret_unicode = static_cast<unsigned int>(unicode); return true; } bool -Reader::addError(const std::string& message, Token& token, Location extra) { +OurReader::addError(const JSONCPP_STRING& message, Token& token, Location extra) { ErrorInfo info; info.token_ = token; info.message_ = message; @@ -727,8 +1791,8 @@ Reader::addError(const std::string& message, Token& token, Location extra) { return false; } -bool Reader::recoverFromError(TokenType skipUntilToken) { - int errorCount = int(errors_.size()); +bool OurReader::recoverFromError(TokenType skipUntilToken) { + size_t errorCount = errors_.size(); Token skip; for (;;) { if (!readToken(skip)) @@ -740,22 +1804,22 @@ bool Reader::recoverFromError(TokenType skipUntilToken) { return false; } -bool Reader::addErrorAndRecover(const std::string& message, +bool OurReader::addErrorAndRecover(const JSONCPP_STRING& message, Token& token, TokenType skipUntilToken) { addError(message, token); return recoverFromError(skipUntilToken); } -Value& Reader::currentValue() { return *(nodes_.top()); } +Value& OurReader::currentValue() { return *(nodes_.top()); } -Reader::Char Reader::getNextChar() { +OurReader::Char OurReader::getNextChar() { if (current_ == end_) return 0; return *current_++; } -void Reader::getLocationLineAndColumn(Location location, +void OurReader::getLocationLineAndColumn(Location location, int& line, int& column) const { Location current = begin_; @@ -778,29 +1842,16 @@ void Reader::getLocationLineAndColumn(Location location, ++line; } -std::string Reader::getLocationLineAndColumn(Location location) const { +JSONCPP_STRING OurReader::getLocationLineAndColumn(Location location) const { int line, column; getLocationLineAndColumn(location, line, column); char buffer[18 + 16 + 16 + 1]; -#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) -#if defined(WINCE) - _snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column); -#else - sprintf_s(buffer, sizeof(buffer), "Line %d, Column %d", line, column); -#endif -#else snprintf(buffer, sizeof(buffer), "Line %d, Column %d", line, column); -#endif return buffer; } -// Deprecated. Preserved for backward compatibility -std::string Reader::getFormatedErrorMessages() const { - return getFormattedErrorMessages(); -} - -std::string Reader::getFormattedErrorMessages() const { - std::string formattedMessage; +JSONCPP_STRING OurReader::getFormattedErrorMessages() const { + JSONCPP_STRING formattedMessage; for (Errors::const_iterator itError = errors_.begin(); itError != errors_.end(); ++itError) { @@ -815,13 +1866,13 @@ std::string Reader::getFormattedErrorMessages() const { return formattedMessage; } -std::vector<Reader::StructuredError> Reader::getStructuredErrors() const { - std::vector<Reader::StructuredError> allErrors; +std::vector<OurReader::StructuredError> OurReader::getStructuredErrors() const { + std::vector<OurReader::StructuredError> allErrors; for (Errors::const_iterator itError = errors_.begin(); itError != errors_.end(); ++itError) { const ErrorInfo& error = *itError; - Reader::StructuredError structured; + OurReader::StructuredError structured; structured.offset_start = error.token_.start_ - begin_; structured.offset_limit = error.token_.end_ - begin_; structured.message = error.message_; @@ -830,8 +1881,8 @@ std::vector<Reader::StructuredError> Reader::getStructuredErrors() const { return allErrors; } -bool Reader::pushError(const Value& value, const std::string& message) { - size_t length = end_ - begin_; +bool OurReader::pushError(const Value& value, const JSONCPP_STRING& message) { + ptrdiff_t length = end_ - begin_; if(value.getOffsetStart() > length || value.getOffsetLimit() > length) return false; @@ -847,8 +1898,8 @@ bool Reader::pushError(const Value& value, const std::string& message) { return true; } -bool Reader::pushError(const Value& value, const std::string& message, const Value& extra) { - size_t length = end_ - begin_; +bool OurReader::pushError(const Value& value, const JSONCPP_STRING& message, const Value& extra) { + ptrdiff_t length = end_ - begin_; if(value.getOffsetStart() > length || value.getOffsetLimit() > length || extra.getOffsetLimit() > length) @@ -865,19 +1916,147 @@ bool Reader::pushError(const Value& value, const std::string& message, const Val return true; } -bool Reader::good() const { +bool OurReader::good() const { return !errors_.size(); } -std::istream& operator>>(std::istream& sin, Value& root) { - Json::Reader reader; - bool ok = reader.parse(sin, root, true); + +class OurCharReader : public CharReader { + bool const collectComments_; + OurReader reader_; +public: + OurCharReader( + bool collectComments, + OurFeatures const& features) + : collectComments_(collectComments) + , reader_(features) + {} + bool parse( + char const* beginDoc, char const* endDoc, + Value* root, JSONCPP_STRING* errs) JSONCPP_OVERRIDE { + bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_); + if (errs) { + *errs = reader_.getFormattedErrorMessages(); + } + return ok; + } +}; + +CharReaderBuilder::CharReaderBuilder() +{ + setDefaults(&settings_); +} +CharReaderBuilder::~CharReaderBuilder() +{} +CharReader* CharReaderBuilder::newCharReader() const +{ + bool collectComments = settings_["collectComments"].asBool(); + OurFeatures features = OurFeatures::all(); + features.allowComments_ = settings_["allowComments"].asBool(); + features.strictRoot_ = settings_["strictRoot"].asBool(); + features.allowDroppedNullPlaceholders_ = settings_["allowDroppedNullPlaceholders"].asBool(); + features.allowNumericKeys_ = settings_["allowNumericKeys"].asBool(); + features.allowSingleQuotes_ = settings_["allowSingleQuotes"].asBool(); + features.stackLimit_ = settings_["stackLimit"].asInt(); + features.failIfExtra_ = settings_["failIfExtra"].asBool(); + features.rejectDupKeys_ = settings_["rejectDupKeys"].asBool(); + features.allowSpecialFloats_ = settings_["allowSpecialFloats"].asBool(); + return new OurCharReader(collectComments, features); +} +static void getValidReaderKeys(std::set<JSONCPP_STRING>* valid_keys) +{ + valid_keys->clear(); + valid_keys->insert("collectComments"); + valid_keys->insert("allowComments"); + valid_keys->insert("strictRoot"); + valid_keys->insert("allowDroppedNullPlaceholders"); + valid_keys->insert("allowNumericKeys"); + valid_keys->insert("allowSingleQuotes"); + valid_keys->insert("stackLimit"); + valid_keys->insert("failIfExtra"); + valid_keys->insert("rejectDupKeys"); + valid_keys->insert("allowSpecialFloats"); +} +bool CharReaderBuilder::validate(Json::Value* invalid) const +{ + Json::Value my_invalid; + if (!invalid) invalid = &my_invalid; // so we do not need to test for NULL + Json::Value& inv = *invalid; + std::set<JSONCPP_STRING> valid_keys; + getValidReaderKeys(&valid_keys); + Value::Members keys = settings_.getMemberNames(); + size_t n = keys.size(); + for (size_t i = 0; i < n; ++i) { + JSONCPP_STRING const& key = keys[i]; + if (valid_keys.find(key) == valid_keys.end()) { + inv[key] = settings_[key]; + } + } + return 0u == inv.size(); +} +Value& CharReaderBuilder::operator[](JSONCPP_STRING key) +{ + return settings_[key]; +} +// static +void CharReaderBuilder::strictMode(Json::Value* settings) +{ +//! [CharReaderBuilderStrictMode] + (*settings)["allowComments"] = false; + (*settings)["strictRoot"] = true; + (*settings)["allowDroppedNullPlaceholders"] = false; + (*settings)["allowNumericKeys"] = false; + (*settings)["allowSingleQuotes"] = false; + (*settings)["stackLimit"] = 1000; + (*settings)["failIfExtra"] = true; + (*settings)["rejectDupKeys"] = true; + (*settings)["allowSpecialFloats"] = false; +//! [CharReaderBuilderStrictMode] +} +// static +void CharReaderBuilder::setDefaults(Json::Value* settings) +{ +//! [CharReaderBuilderDefaults] + (*settings)["collectComments"] = true; + (*settings)["allowComments"] = true; + (*settings)["strictRoot"] = false; + (*settings)["allowDroppedNullPlaceholders"] = false; + (*settings)["allowNumericKeys"] = false; + (*settings)["allowSingleQuotes"] = false; + (*settings)["stackLimit"] = 1000; + (*settings)["failIfExtra"] = false; + (*settings)["rejectDupKeys"] = false; + (*settings)["allowSpecialFloats"] = false; +//! [CharReaderBuilderDefaults] +} + +////////////////////////////////// +// global functions + +bool parseFromStream( + CharReader::Factory const& fact, JSONCPP_ISTREAM& sin, + Value* root, JSONCPP_STRING* errs) +{ + JSONCPP_OSTRINGSTREAM ssin; + ssin << sin.rdbuf(); + JSONCPP_STRING doc = ssin.str(); + char const* begin = doc.data(); + char const* end = begin + doc.size(); + // Note that we do not actually need a null-terminator. + CharReaderPtr const reader(fact.newCharReader()); + return reader->parse(begin, end, root, errs); +} + +JSONCPP_ISTREAM& operator>>(JSONCPP_ISTREAM& sin, Value& root) { + CharReaderBuilder b; + JSONCPP_STRING errs; + bool ok = parseFromStream(b, sin, &root, &errs); if (!ok) { fprintf(stderr, "Error from reader: %s", - reader.getFormattedErrorMessages().c_str()); + errs.c_str()); - JSON_FAIL_MESSAGE("reader error"); + throwRuntimeError(errs); } return sin; } diff --git a/Utilities/cmjsoncpp/src/lib_json/json_tool.h b/Utilities/cmjsoncpp/src/lib_json/json_tool.h index f9b61c38c7..431617830c 100644 --- a/Utilities/cmjsoncpp/src/lib_json/json_tool.h +++ b/Utilities/cmjsoncpp/src/lib_json/json_tool.h @@ -1,4 +1,4 @@ -// Copyright 2007-2010 Baptiste Lepilleur +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors // Distributed under MIT license, or public domain if desired and // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE @@ -6,6 +6,16 @@ #ifndef LIB_JSONCPP_JSON_TOOL_H_INCLUDED #define LIB_JSONCPP_JSON_TOOL_H_INCLUDED + +// Also support old flag NO_LOCALE_SUPPORT +#ifdef NO_LOCALE_SUPPORT +#define JSONCPP_NO_LOCALE_SUPPORT +#endif + +#ifndef JSONCPP_NO_LOCALE_SUPPORT +#include <clocale> +#endif + /* This header provides common string manipulation support, such as UTF-8, * portable conversion from/to string... * @@ -13,10 +23,18 @@ */ namespace Json { +static char getDecimalPoint() { +#ifdef JSONCPP_NO_LOCALE_SUPPORT + return '\0'; +#else + struct lconv* lc = localeconv(); + return lc ? *(lc->decimal_point) : '\0'; +#endif +} /// Converts a unicode code-point to UTF-8. -static inline std::string codePointToUTF8(unsigned int cp) { - std::string result; +static inline JSONCPP_STRING codePointToUTF8(unsigned int cp) { + JSONCPP_STRING result; // based on description from http://en.wikipedia.org/wiki/UTF-8 @@ -30,8 +48,8 @@ static inline std::string codePointToUTF8(unsigned int cp) { } 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))); + result[1] = static_cast<char>(0x80 | (0x3f & (cp >> 6))); + result[0] = static_cast<char>(0xE0 | (0xf & (cp >> 12))); } else if (cp <= 0x10FFFF) { result.resize(4); result[3] = static_cast<char>(0x80 | (0x3f & cp)); @@ -43,7 +61,7 @@ static inline std::string codePointToUTF8(unsigned int cp) { return result; } -/// Returns true if ch is a control character (in range [0,32[). +/// Returns true if ch is a control character (in range [1,31]). static inline bool isControlCharacter(char ch) { return ch > 0 && ch <= 0x1F; } enum { @@ -63,7 +81,7 @@ typedef char UIntToStringBuffer[uintToStringBufferSize]; static inline void uintToString(LargestUInt value, char*& current) { *--current = 0; do { - *--current = char(value % 10) + '0'; + *--current = static_cast<char>(value % 10U + static_cast<unsigned>('0')); value /= 10; } while (value != 0); } @@ -82,6 +100,18 @@ static inline void fixNumericLocale(char* begin, char* end) { } } +static inline void fixNumericLocaleInput(char* begin, char* end) { + char decimalPoint = getDecimalPoint(); + if (decimalPoint != '\0' && decimalPoint != '.') { + while (begin < end) { + if (*begin == '.') { + *begin = decimalPoint; + } + ++begin; + } + } +} + } // namespace Json { #endif // LIB_JSONCPP_JSON_TOOL_H_INCLUDED diff --git a/Utilities/cmjsoncpp/src/lib_json/json_value.cpp b/Utilities/cmjsoncpp/src/lib_json/json_value.cpp index 478afe102b..c6ee5e9963 100644 --- a/Utilities/cmjsoncpp/src/lib_json/json_value.cpp +++ b/Utilities/cmjsoncpp/src/lib_json/json_value.cpp @@ -1,4 +1,4 @@ -// Copyright 2011 Baptiste Lepilleur +// Copyright 2011 Baptiste Lepilleur and The JsonCpp Authors // Distributed under MIT license, or public domain if desired and // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE @@ -7,9 +7,6 @@ #include <json/assertions.h> #include <json/value.h> #include <json/writer.h> -#ifndef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR -#include "json_batchallocator.h" -#endif // #ifndef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR #endif // if !defined(JSON_IS_AMALGAMATION) #include <math.h> #include <sstream> @@ -20,6 +17,7 @@ #include <cpptl/conststring.h> #endif #include <cstddef> // size_t +#include <algorithm> // min() #define JSON_ASSERT_UNREACHABLE assert(false) @@ -33,9 +31,22 @@ namespace Json { #else #define ALIGNAS(byte_alignment) #endif -static const unsigned char ALIGNAS(8) kNull[sizeof(Value)] = { 0 }; -const unsigned char& kNullRef = kNull[0]; -const Value& Value::null = reinterpret_cast<const Value&>(kNullRef); +//static const unsigned char ALIGNAS(8) kNull[sizeof(Value)] = { 0 }; +//const unsigned char& kNullRef = kNull[0]; +//const Value& Value::null = reinterpret_cast<const Value&>(kNullRef); +//const Value& Value::nullRef = null; + +// static +Value const& Value::nullSingleton() +{ + static Value const nullStatic; + return nullStatic; +} + +// for backwards compatibility, we'll leave these global references around, but DO NOT +// use them in JSONCPP library code any more! +Value const& Value::null = Value::nullSingleton(); +Value const& Value::nullRef = Value::nullSingleton(); const Int Value::minInt = Int(~(UInt(-1) / 2)); const Int Value::maxInt = Int(UInt(-1) / 2); @@ -53,17 +64,17 @@ const LargestInt Value::minLargestInt = LargestInt(~(LargestUInt(-1) / 2)); const LargestInt Value::maxLargestInt = LargestInt(LargestUInt(-1) / 2); const LargestUInt Value::maxLargestUInt = LargestUInt(-1); -/// Unknown size marker -static const unsigned int unknown = (unsigned)-1; - #if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) template <typename T, typename U> static inline bool InRange(double d, T min, U max) { + // The casts can lose precision, but we are looking only for + // an approximate range. Might fail on edge cases though. ~cdunn + //return d >= static_cast<double>(min) && d <= static_cast<double>(max); return d >= min && d <= max; } #else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) static inline double integerToDouble(Json::UInt64 value) { - return static_cast<double>(Int64(value / 2)) * 2.0 + Int64(value & 1); + return static_cast<double>(Int64(value / 2)) * 2.0 + static_cast<double>(Int64(value & 1)); } template <typename T> static inline double integerToDouble(T value) { @@ -84,27 +95,84 @@ static inline bool InRange(double d, T min, U max) { * @return Pointer on the duplicate instance of string. */ static inline char* duplicateStringValue(const char* value, - unsigned int length = unknown) { - if (length == unknown) - length = (unsigned int)strlen(value); - + size_t length) +{ // Avoid an integer overflow in the call to malloc below by limiting length // to a sane value. - if (length >= (unsigned)Value::maxInt) + if (length >= static_cast<size_t>(Value::maxInt)) length = Value::maxInt - 1; char* newString = static_cast<char*>(malloc(length + 1)); - JSON_ASSERT_MESSAGE(newString != 0, - "in Json::Value::duplicateStringValue(): " - "Failed to allocate string value buffer"); + if (newString == NULL) { + throwRuntimeError( + "in Json::Value::duplicateStringValue(): " + "Failed to allocate string value buffer"); + } memcpy(newString, value, length); newString[length] = 0; return newString; } -/** Free the string duplicated by duplicateStringValue(). +/* Record the length as a prefix. + */ +static inline char* duplicateAndPrefixStringValue( + const char* value, + unsigned int length) +{ + // Avoid an integer overflow in the call to malloc below by limiting length + // to a sane value. + JSON_ASSERT_MESSAGE(length <= static_cast<unsigned>(Value::maxInt) - sizeof(unsigned) - 1U, + "in Json::Value::duplicateAndPrefixStringValue(): " + "length too big for prefixing"); + unsigned actualLength = length + static_cast<unsigned>(sizeof(unsigned)) + 1U; + char* newString = static_cast<char*>(malloc(actualLength)); + if (newString == 0) { + throwRuntimeError( + "in Json::Value::duplicateAndPrefixStringValue(): " + "Failed to allocate string value buffer"); + } + *reinterpret_cast<unsigned*>(newString) = length; + memcpy(newString + sizeof(unsigned), value, length); + newString[actualLength - 1U] = 0; // to avoid buffer over-run accidents by users later + return newString; +} +inline static void decodePrefixedString( + bool isPrefixed, char const* prefixed, + unsigned* length, char const** value) +{ + if (!isPrefixed) { + *length = static_cast<unsigned>(strlen(prefixed)); + *value = prefixed; + } else { + *length = *reinterpret_cast<unsigned const*>(prefixed); + *value = prefixed + sizeof(unsigned); + } +} +/** Free the string duplicated by duplicateStringValue()/duplicateAndPrefixStringValue(). */ -static inline void releaseStringValue(char* value) { free(value); } +#if JSONCPP_USING_SECURE_MEMORY +static inline void releasePrefixedStringValue(char* value) { + unsigned length = 0; + char const* valueDecoded; + decodePrefixedString(true, value, &length, &valueDecoded); + size_t const size = sizeof(unsigned) + length + 1U; + memset(value, 0, size); + free(value); +} +static inline void releaseStringValue(char* value, unsigned length) { + // length==0 => we allocated the strings memory + size_t size = (length==0) ? strlen(value) : length; + memset(value, 0, size); + free(value); +} +#else // !JSONCPP_USING_SECURE_MEMORY +static inline void releasePrefixedStringValue(char* value) { + free(value); +} +static inline void releaseStringValue(char* value, unsigned) { + free(value); +} +#endif // JSONCPP_USING_SECURE_MEMORY } // namespace Json @@ -116,16 +184,36 @@ static inline void releaseStringValue(char* value) { free(value); } // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// #if !defined(JSON_IS_AMALGAMATION) -#ifdef JSON_VALUE_USE_INTERNAL_MAP -#include "json_internalarray.inl" -#include "json_internalmap.inl" -#endif // JSON_VALUE_USE_INTERNAL_MAP #include "json_valueiterator.inl" #endif // if !defined(JSON_IS_AMALGAMATION) namespace Json { +Exception::Exception(JSONCPP_STRING const& msg) + : msg_(msg) +{} +Exception::~Exception() JSONCPP_NOEXCEPT +{} +char const* Exception::what() const JSONCPP_NOEXCEPT +{ + return msg_.c_str(); +} +RuntimeError::RuntimeError(JSONCPP_STRING const& msg) + : Exception(msg) +{} +LogicError::LogicError(JSONCPP_STRING const& msg) + : Exception(msg) +{} +JSONCPP_NORETURN void throwRuntimeError(JSONCPP_STRING const& msg) +{ + throw RuntimeError(msg); +} +JSONCPP_NORETURN void throwLogicError(JSONCPP_STRING const& msg) +{ + throw LogicError(msg); +} + // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// @@ -134,22 +222,25 @@ namespace Json { // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// -Value::CommentInfo::CommentInfo() : comment_(0) {} +Value::CommentInfo::CommentInfo() : comment_(0) +{} Value::CommentInfo::~CommentInfo() { if (comment_) - releaseStringValue(comment_); + releaseStringValue(comment_, 0u); } -void Value::CommentInfo::setComment(const char* text) { - if (comment_) - releaseStringValue(comment_); +void Value::CommentInfo::setComment(const char* text, size_t len) { + if (comment_) { + releaseStringValue(comment_, 0u); + comment_ = 0; + } JSON_ASSERT(text != 0); JSON_ASSERT_MESSAGE( text[0] == '\0' || text[0] == '/', "in Json::Value::setComment(): Comments must start with /"); // It seems that /**/ style comments are acceptable as well. - comment_ = duplicateStringValue(text); + comment_ = duplicateStringValue(text, len); } // ////////////////////////////////////////////////////////////////// @@ -159,29 +250,41 @@ void Value::CommentInfo::setComment(const char* text) { // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// -#ifndef JSON_VALUE_USE_INTERNAL_MAP -// Notes: index_ indicates if the string was allocated when +// Notes: policy_ indicates if the string was allocated when // a string is stored. -Value::CZString::CZString(ArrayIndex index) : cstr_(0), index_(index) {} +Value::CZString::CZString(ArrayIndex aindex) : cstr_(0), index_(aindex) {} -Value::CZString::CZString(const char* cstr, DuplicationPolicy allocate) - : cstr_(allocate == duplicate ? duplicateStringValue(cstr) : cstr), - index_(allocate) {} +Value::CZString::CZString(char const* str, unsigned ulength, DuplicationPolicy allocate) + : cstr_(str) { + // allocate != duplicate + storage_.policy_ = allocate & 0x3; + storage_.length_ = ulength & 0x3FFFFFFF; +} -Value::CZString::CZString(const CZString& other) - : cstr_(other.index_ != noDuplication && other.cstr_ != 0 - ? duplicateStringValue(other.cstr_) - : other.cstr_), - index_(other.cstr_ - ? static_cast<ArrayIndex>(other.index_ == noDuplication +Value::CZString::CZString(const CZString& other) { + cstr_ = (other.storage_.policy_ != noDuplication && other.cstr_ != 0 + ? duplicateStringValue(other.cstr_, other.storage_.length_) + : other.cstr_); + storage_.policy_ = static_cast<unsigned>(other.cstr_ + ? (static_cast<DuplicationPolicy>(other.storage_.policy_) == noDuplication ? noDuplication : duplicate) - : other.index_) {} + : static_cast<DuplicationPolicy>(other.storage_.policy_)) & 3U; + storage_.length_ = other.storage_.length_; +} + +#if JSON_HAS_RVALUE_REFERENCES +Value::CZString::CZString(CZString&& other) + : cstr_(other.cstr_), index_(other.index_) { + other.cstr_ = nullptr; +} +#endif Value::CZString::~CZString() { - if (cstr_ && index_ == duplicate) - releaseStringValue(const_cast<char*>(cstr_)); + if (cstr_ && storage_.policy_ == duplicate) { + releaseStringValue(const_cast<char*>(cstr_), storage_.length_ + 1u); //+1 for null terminating character for sake of completeness but not actually necessary + } } void Value::CZString::swap(CZString& other) { @@ -189,34 +292,53 @@ void Value::CZString::swap(CZString& other) { std::swap(index_, other.index_); } -Value::CZString& Value::CZString::operator=(CZString other) { - swap(other); +Value::CZString& Value::CZString::operator=(const CZString& other) { + cstr_ = other.cstr_; + index_ = other.index_; return *this; } +#if JSON_HAS_RVALUE_REFERENCES +Value::CZString& Value::CZString::operator=(CZString&& other) { + cstr_ = other.cstr_; + index_ = other.index_; + other.cstr_ = nullptr; + return *this; +} +#endif + bool Value::CZString::operator<(const CZString& other) const { - if (cstr_) { - assert(other.cstr_); - return strcmp(cstr_, other.cstr_) < 0; - } - return index_ < other.index_; + if (!cstr_) return index_ < other.index_; + //return strcmp(cstr_, other.cstr_) < 0; + // Assume both are strings. + unsigned this_len = this->storage_.length_; + unsigned other_len = other.storage_.length_; + unsigned min_len = std::min<unsigned>(this_len, other_len); + JSON_ASSERT(this->cstr_ && other.cstr_); + int comp = memcmp(this->cstr_, other.cstr_, min_len); + if (comp < 0) return true; + if (comp > 0) return false; + return (this_len < other_len); } bool Value::CZString::operator==(const CZString& other) const { - if (cstr_) { - assert(other.cstr_); - return strcmp(cstr_, other.cstr_) == 0; - } - return index_ == other.index_; + if (!cstr_) return index_ == other.index_; + //return strcmp(cstr_, other.cstr_) == 0; + // Assume both are strings. + unsigned this_len = this->storage_.length_; + unsigned other_len = other.storage_.length_; + if (this_len != other_len) return false; + JSON_ASSERT(this->cstr_ && other.cstr_); + int comp = memcmp(this->cstr_, other.cstr_, this_len); + return comp == 0; } ArrayIndex Value::CZString::index() const { return index_; } -const char* Value::CZString::c_str() const { return cstr_; } - -bool Value::CZString::isStaticString() const { return index_ == noDuplication; } - -#endif // ifndef JSON_VALUE_USE_INTERNAL_MAP +//const char* Value::CZString::c_str() const { return cstr_; } +const char* Value::CZString::data() const { return cstr_; } +unsigned Value::CZString::length() const { return storage_.length_; } +bool Value::CZString::isStaticString() const { return storage_.policy_ == noDuplication; } // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// @@ -230,9 +352,10 @@ bool Value::CZString::isStaticString() const { return index_ == noDuplication; } * memset( this, 0, sizeof(Value) ) * This optimization is used in ValueInternalMap fast allocator. */ -Value::Value(ValueType type) { - initBasic(type); - switch (type) { +Value::Value(ValueType vtype) { + static char const emptyString[] = ""; + initBasic(vtype); + switch (vtype) { case nullValue: break; case intValue: @@ -243,21 +366,13 @@ Value::Value(ValueType type) { value_.real_ = 0.0; break; case stringValue: - value_.string_ = 0; + // allocated_ == false, so this is safe. + value_.string_ = const_cast<char*>(static_cast<char const*>(emptyString)); break; -#ifndef JSON_VALUE_USE_INTERNAL_MAP case arrayValue: case objectValue: value_.map_ = new ObjectValues(); break; -#else - case arrayValue: - value_.array_ = arrayAllocator()->newArray(); - break; - case objectValue: - value_.map_ = mapAllocator()->newMap(); - break; -#endif case booleanValue: value_.bool_ = false; break; @@ -293,19 +408,20 @@ Value::Value(double value) { Value::Value(const char* value) { initBasic(stringValue, true); - value_.string_ = duplicateStringValue(value); + JSON_ASSERT_MESSAGE(value != NULL, "Null Value Passed to Value Constructor"); + value_.string_ = duplicateAndPrefixStringValue(value, static_cast<unsigned>(strlen(value))); } Value::Value(const char* beginValue, const char* endValue) { initBasic(stringValue, true); value_.string_ = - duplicateStringValue(beginValue, (unsigned int)(endValue - beginValue)); + duplicateAndPrefixStringValue(beginValue, static_cast<unsigned>(endValue - beginValue)); } -Value::Value(const std::string& value) { +Value::Value(const JSONCPP_STRING& value) { initBasic(stringValue, true); value_.string_ = - duplicateStringValue(value.c_str(), (unsigned int)value.length()); + duplicateAndPrefixStringValue(value.data(), static_cast<unsigned>(value.length())); } Value::Value(const StaticString& value) { @@ -316,7 +432,7 @@ Value::Value(const StaticString& value) { #ifdef JSON_USE_CPPTL Value::Value(const CppTL::ConstString& value) { initBasic(stringValue, true); - value_.string_ = duplicateStringValue(value, value.length()); + value_.string_ = duplicateAndPrefixStringValue(value, static_cast<unsigned>(value.length())); } #endif @@ -325,14 +441,11 @@ Value::Value(bool value) { value_.bool_ = value; } -Value::Value(const Value& other) +Value::Value(Value const& other) : type_(other.type_), allocated_(false) -#ifdef JSON_VALUE_USE_INTERNAL_MAP , - itemIsUsed_(0) -#endif - , - comments_(0), start_(other.start_), limit_(other.limit_) { + comments_(0), start_(other.start_), limit_(other.limit_) +{ switch (type_) { case nullValue: case intValue: @@ -342,27 +455,22 @@ Value::Value(const Value& other) value_ = other.value_; break; case stringValue: - if (other.value_.string_) { - value_.string_ = duplicateStringValue(other.value_.string_); + if (other.value_.string_ && other.allocated_) { + unsigned len; + char const* str; + decodePrefixedString(other.allocated_, other.value_.string_, + &len, &str); + value_.string_ = duplicateAndPrefixStringValue(str, len); allocated_ = true; } else { - value_.string_ = 0; + value_.string_ = other.value_.string_; allocated_ = false; } break; -#ifndef JSON_VALUE_USE_INTERNAL_MAP case arrayValue: case objectValue: value_.map_ = new ObjectValues(*other.value_.map_); break; -#else - case arrayValue: - value_.array_ = arrayAllocator()->newArrayCopy(*other.value_.array_); - break; - case objectValue: - value_.map_ = mapAllocator()->newMapCopy(*other.value_.map_); - break; -#endif default: JSON_ASSERT_UNREACHABLE; } @@ -371,11 +479,20 @@ Value::Value(const Value& other) for (int comment = 0; comment < numberOfCommentPlacement; ++comment) { const CommentInfo& otherComment = other.comments_[comment]; if (otherComment.comment_) - comments_[comment].setComment(otherComment.comment_); + comments_[comment].setComment( + otherComment.comment_, strlen(otherComment.comment_)); } } } +#if JSON_HAS_RVALUE_REFERENCES +// Move constructor +Value::Value(Value&& other) { + initBasic(nullValue); + swap(other); +} +#endif + Value::~Value() { switch (type_) { case nullValue: @@ -386,27 +503,19 @@ Value::~Value() { break; case stringValue: if (allocated_) - releaseStringValue(value_.string_); + releasePrefixedStringValue(value_.string_); break; -#ifndef JSON_VALUE_USE_INTERNAL_MAP case arrayValue: case objectValue: delete value_.map_; break; -#else - case arrayValue: - arrayAllocator()->destructArray(value_.array_); - break; - case objectValue: - mapAllocator()->destructMap(value_.map_); - break; -#endif default: JSON_ASSERT_UNREACHABLE; } - if (comments_) - delete[] comments_; + delete[] comments_; + + value_.uint_ = 0; } Value& Value::operator=(Value other) { @@ -414,18 +523,36 @@ Value& Value::operator=(Value other) { return *this; } -void Value::swap(Value& other) { +void Value::swapPayload(Value& other) { ValueType temp = type_; type_ = other.type_; other.type_ = temp; std::swap(value_, other.value_); int temp2 = allocated_; allocated_ = other.allocated_; - other.allocated_ = temp2; + other.allocated_ = temp2 & 0x1; +} + +void Value::copyPayload(const Value& other) { + type_ = other.type_; + value_ = other.value_; + allocated_ = other.allocated_; +} + +void Value::swap(Value& other) { + swapPayload(other); + std::swap(comments_, other.comments_); std::swap(start_, other.start_); std::swap(limit_, other.limit_); } +void Value::copy(const Value& other) { + copyPayload(other); + comments_ = other.comments_; + start_ = other.start_; + limit_ = other.limit_; +} + ValueType Value::type() const { return type_; } int Value::compare(const Value& other) const { @@ -452,10 +579,24 @@ bool Value::operator<(const Value& other) const { case booleanValue: return value_.bool_ < other.value_.bool_; case stringValue: - return (value_.string_ == 0 && other.value_.string_) || - (other.value_.string_ && value_.string_ && - strcmp(value_.string_, other.value_.string_) < 0); -#ifndef JSON_VALUE_USE_INTERNAL_MAP + { + if ((value_.string_ == 0) || (other.value_.string_ == 0)) { + if (other.value_.string_) return true; + else return false; + } + unsigned this_len; + unsigned other_len; + char const* this_str; + char const* other_str; + decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str); + decodePrefixedString(other.allocated_, other.value_.string_, &other_len, &other_str); + unsigned min_len = std::min<unsigned>(this_len, other_len); + JSON_ASSERT(this_str && other_str); + int comp = memcmp(this_str, other_str, min_len); + if (comp < 0) return true; + if (comp > 0) return false; + return (this_len < other_len); + } case arrayValue: case objectValue: { int delta = int(value_.map_->size() - other.value_.map_->size()); @@ -463,12 +604,6 @@ bool Value::operator<(const Value& other) const { return delta < 0; return (*value_.map_) < (*other.value_.map_); } -#else - case arrayValue: - return value_.array_->compare(*(other.value_.array_)) < 0; - case objectValue: - return value_.map_->compare(*(other.value_.map_)) < 0; -#endif default: JSON_ASSERT_UNREACHABLE; } @@ -501,20 +636,25 @@ bool Value::operator==(const Value& other) const { case booleanValue: return value_.bool_ == other.value_.bool_; case stringValue: - return (value_.string_ == other.value_.string_) || - (other.value_.string_ && value_.string_ && - strcmp(value_.string_, other.value_.string_) == 0); -#ifndef JSON_VALUE_USE_INTERNAL_MAP + { + if ((value_.string_ == 0) || (other.value_.string_ == 0)) { + return (value_.string_ == other.value_.string_); + } + unsigned this_len; + unsigned other_len; + char const* this_str; + char const* other_str; + decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str); + decodePrefixedString(other.allocated_, other.value_.string_, &other_len, &other_str); + if (this_len != other_len) return false; + JSON_ASSERT(this_str && other_str); + int comp = memcmp(this_str, other_str, this_len); + return comp == 0; + } case arrayValue: case objectValue: return value_.map_->size() == other.value_.map_->size() && (*value_.map_) == (*other.value_.map_); -#else - case arrayValue: - return value_.array_->compare(*(other.value_.array_)) == 0; - case objectValue: - return value_.map_->compare(*(other.value_.map_)) == 0; -#endif default: JSON_ASSERT_UNREACHABLE; } @@ -526,15 +666,46 @@ bool Value::operator!=(const Value& other) const { return !(*this == other); } const char* Value::asCString() const { JSON_ASSERT_MESSAGE(type_ == stringValue, "in Json::Value::asCString(): requires stringValue"); - return value_.string_; + if (value_.string_ == 0) return 0; + unsigned this_len; + char const* this_str; + decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str); + return this_str; +} + +#if JSONCPP_USING_SECURE_MEMORY +unsigned Value::getCStringLength() const { + JSON_ASSERT_MESSAGE(type_ == stringValue, + "in Json::Value::asCString(): requires stringValue"); + if (value_.string_ == 0) return 0; + unsigned this_len; + char const* this_str; + decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str); + return this_len; +} +#endif + +bool Value::getString(char const** str, char const** cend) const { + if (type_ != stringValue) return false; + if (value_.string_ == 0) return false; + unsigned length; + decodePrefixedString(this->allocated_, this->value_.string_, &length, str); + *cend = *str + length; + return true; } -std::string Value::asString() const { +JSONCPP_STRING Value::asString() const { switch (type_) { case nullValue: return ""; case stringValue: - return value_.string_ ? value_.string_ : ""; + { + if (value_.string_ == 0) return ""; + unsigned this_len; + char const* this_str; + decodePrefixedString(this->allocated_, this->value_.string_, &this_len, &this_str); + return JSONCPP_STRING(this_str, this_len); + } case booleanValue: return value_.bool_ ? "true" : "false"; case intValue: @@ -550,7 +721,11 @@ std::string Value::asString() const { #ifdef JSON_USE_CPPTL CppTL::ConstString Value::asConstString() const { - return CppTL::ConstString(asString().c_str()); + unsigned len; + char const* str; + decodePrefixedString(allocated_, value_.string_, + &len, &str); + return CppTL::ConstString(str, len); } #endif @@ -689,7 +864,8 @@ float Value::asFloat() const { #if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) return static_cast<float>(value_.uint_); #else // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) - return integerToDouble(value_.uint_); + // This can fail (silently?) if the value is bigger than MAX_FLOAT. + return static_cast<float>(integerToDouble(value_.uint_)); #endif // if !defined(JSON_USE_INT64_DOUBLE_CONVERSION) case realValue: return static_cast<float>(value_.real_); @@ -714,7 +890,8 @@ bool Value::asBool() const { case uintValue: return value_.uint_ ? true : false; case realValue: - return value_.real_ ? true : false; + // This is kind of strange. Not recommended. + return (value_.real_ != 0.0) ? true : false; default: break; } @@ -726,7 +903,7 @@ bool Value::isConvertibleTo(ValueType other) const { case nullValue: return (isNumeric() && asDouble() == 0.0) || (type_ == booleanValue && value_.bool_ == false) || - (type_ == stringValue && asString() == "") || + (type_ == stringValue && asString().empty()) || (type_ == arrayValue && value_.map_->size() == 0) || (type_ == objectValue && value_.map_->size() == 0) || type_ == nullValue; @@ -764,7 +941,6 @@ ArrayIndex Value::size() const { case booleanValue: case stringValue: return 0; -#ifndef JSON_VALUE_USE_INTERNAL_MAP case arrayValue: // size of the array is highest index + 1 if (!value_.map_->empty()) { ObjectValues::const_iterator itLast = value_.map_->end(); @@ -774,12 +950,6 @@ ArrayIndex Value::size() const { return 0; case objectValue: return ArrayIndex(value_.map_->size()); -#else - case arrayValue: - return Int(value_.array_->size()); - case objectValue: - return Int(value_.map_->size()); -#endif } JSON_ASSERT_UNREACHABLE; return 0; // unreachable; @@ -801,19 +971,10 @@ void Value::clear() { start_ = 0; limit_ = 0; switch (type_) { -#ifndef JSON_VALUE_USE_INTERNAL_MAP case arrayValue: case objectValue: value_.map_->clear(); break; -#else - case arrayValue: - value_.array_->clear(); - break; - case objectValue: - value_.map_->clear(); - break; -#endif default: break; } @@ -824,7 +985,6 @@ void Value::resize(ArrayIndex newSize) { "in Json::Value::resize(): requires arrayValue"); if (type_ == nullValue) *this = Value(arrayValue); -#ifndef JSON_VALUE_USE_INTERNAL_MAP ArrayIndex oldSize = size(); if (newSize == 0) clear(); @@ -834,11 +994,8 @@ void Value::resize(ArrayIndex newSize) { for (ArrayIndex index = newSize; index < oldSize; ++index) { value_.map_->erase(index); } - assert(size() == newSize); + JSON_ASSERT(size() == newSize); } -#else - value_.array_->resize(newSize); -#endif } Value& Value::operator[](ArrayIndex index) { @@ -847,18 +1004,14 @@ Value& Value::operator[](ArrayIndex index) { "in Json::Value::operator[](ArrayIndex): requires arrayValue"); if (type_ == nullValue) *this = Value(arrayValue); -#ifndef JSON_VALUE_USE_INTERNAL_MAP CZString key(index); ObjectValues::iterator it = value_.map_->lower_bound(key); if (it != value_.map_->end() && (*it).first == key) return (*it).second; - ObjectValues::value_type defaultValue(key, null); + ObjectValues::value_type defaultValue(key, nullSingleton()); it = value_.map_->insert(it, defaultValue); return (*it).second; -#else - return value_.array_->resolveReference(index); -#endif } Value& Value::operator[](int index) { @@ -873,17 +1026,12 @@ const Value& Value::operator[](ArrayIndex index) const { type_ == nullValue || type_ == arrayValue, "in Json::Value::operator[](ArrayIndex)const: requires arrayValue"); if (type_ == nullValue) - return null; -#ifndef JSON_VALUE_USE_INTERNAL_MAP + return nullSingleton(); CZString key(index); ObjectValues::const_iterator it = value_.map_->find(key); if (it == value_.map_->end()) - return null; + return nullSingleton(); return (*it).second; -#else - Value* value = value_.array_->find(index); - return value ? *value : null; -#endif } const Value& Value::operator[](int index) const { @@ -893,149 +1041,215 @@ const Value& Value::operator[](int index) const { return (*this)[ArrayIndex(index)]; } -Value& Value::operator[](const char* key) { - return resolveReference(key, false); -} - -void Value::initBasic(ValueType type, bool allocated) { - type_ = type; +void Value::initBasic(ValueType vtype, bool allocated) { + type_ = vtype; allocated_ = allocated; -#ifdef JSON_VALUE_USE_INTERNAL_MAP - itemIsUsed_ = 0; -#endif comments_ = 0; start_ = 0; limit_ = 0; } -Value& Value::resolveReference(const char* key, bool isStatic) { +// Access an object value by name, create a null member if it does not exist. +// @pre Type of '*this' is object or null. +// @param key is null-terminated. +Value& Value::resolveReference(const char* key) { JSON_ASSERT_MESSAGE( type_ == nullValue || type_ == objectValue, "in Json::Value::resolveReference(): requires objectValue"); if (type_ == nullValue) *this = Value(objectValue); -#ifndef JSON_VALUE_USE_INTERNAL_MAP CZString actualKey( - key, isStatic ? CZString::noDuplication : CZString::duplicateOnCopy); + key, static_cast<unsigned>(strlen(key)), CZString::noDuplication); // NOTE! ObjectValues::iterator it = value_.map_->lower_bound(actualKey); if (it != value_.map_->end() && (*it).first == actualKey) return (*it).second; - ObjectValues::value_type defaultValue(actualKey, null); + ObjectValues::value_type defaultValue(actualKey, nullSingleton()); + it = value_.map_->insert(it, defaultValue); + Value& value = (*it).second; + return value; +} + +// @param key is not null-terminated. +Value& Value::resolveReference(char const* key, char const* cend) +{ + JSON_ASSERT_MESSAGE( + type_ == nullValue || type_ == objectValue, + "in Json::Value::resolveReference(key, end): requires objectValue"); + if (type_ == nullValue) + *this = Value(objectValue); + CZString actualKey( + key, static_cast<unsigned>(cend-key), CZString::duplicateOnCopy); + ObjectValues::iterator it = value_.map_->lower_bound(actualKey); + if (it != value_.map_->end() && (*it).first == actualKey) + return (*it).second; + + ObjectValues::value_type defaultValue(actualKey, nullSingleton()); it = value_.map_->insert(it, defaultValue); Value& value = (*it).second; return value; -#else - return value_.map_->resolveReference(key, isStatic); -#endif } Value Value::get(ArrayIndex index, const Value& defaultValue) const { const Value* value = &((*this)[index]); - return value == &null ? defaultValue : *value; + return value == &nullSingleton() ? defaultValue : *value; } bool Value::isValidIndex(ArrayIndex index) const { return index < size(); } -const Value& Value::operator[](const char* key) const { +Value const* Value::find(char const* key, char const* cend) const +{ JSON_ASSERT_MESSAGE( type_ == nullValue || type_ == objectValue, - "in Json::Value::operator[](char const*)const: requires objectValue"); - if (type_ == nullValue) - return null; -#ifndef JSON_VALUE_USE_INTERNAL_MAP - CZString actualKey(key, CZString::noDuplication); + "in Json::Value::find(key, end, found): requires objectValue or nullValue"); + if (type_ == nullValue) return NULL; + CZString actualKey(key, static_cast<unsigned>(cend-key), CZString::noDuplication); ObjectValues::const_iterator it = value_.map_->find(actualKey); - if (it == value_.map_->end()) - return null; - return (*it).second; -#else - const Value* value = value_.map_->find(key); - return value ? *value : null; -#endif + if (it == value_.map_->end()) return NULL; + return &(*it).second; +} +const Value& Value::operator[](const char* key) const +{ + Value const* found = find(key, key + strlen(key)); + if (!found) return nullSingleton(); + return *found; +} +Value const& Value::operator[](JSONCPP_STRING const& key) const +{ + Value const* found = find(key.data(), key.data() + key.length()); + if (!found) return nullSingleton(); + return *found; } -Value& Value::operator[](const std::string& key) { - return (*this)[key.c_str()]; +Value& Value::operator[](const char* key) { + return resolveReference(key, key + strlen(key)); } -const Value& Value::operator[](const std::string& key) const { - return (*this)[key.c_str()]; +Value& Value::operator[](const JSONCPP_STRING& key) { + return resolveReference(key.data(), key.data() + key.length()); } Value& Value::operator[](const StaticString& key) { - return resolveReference(key, true); + return resolveReference(key.c_str()); } #ifdef JSON_USE_CPPTL Value& Value::operator[](const CppTL::ConstString& key) { - return (*this)[key.c_str()]; + return resolveReference(key.c_str(), key.end_c_str()); } - -const Value& Value::operator[](const CppTL::ConstString& key) const { - return (*this)[key.c_str()]; +Value const& Value::operator[](CppTL::ConstString const& key) const +{ + Value const* found = find(key.c_str(), key.end_c_str()); + if (!found) return nullSingleton(); + return *found; } #endif Value& Value::append(const Value& value) { return (*this)[size()] = value; } -Value Value::get(const char* key, const Value& defaultValue) const { - const Value* value = &((*this)[key]); - return value == &null ? defaultValue : *value; -} +#if JSON_HAS_RVALUE_REFERENCES + Value& Value::append(Value&& value) { return (*this)[size()] = value; } +#endif -Value Value::get(const std::string& key, const Value& defaultValue) const { - return get(key.c_str(), defaultValue); +Value Value::get(char const* key, char const* cend, Value const& defaultValue) const +{ + Value const* found = find(key, cend); + return !found ? defaultValue : *found; +} +Value Value::get(char const* key, Value const& defaultValue) const +{ + return get(key, key + strlen(key), defaultValue); +} +Value Value::get(JSONCPP_STRING const& key, Value const& defaultValue) const +{ + return get(key.data(), key.data() + key.length(), defaultValue); } -Value Value::removeMember(const char* key) { - JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == objectValue, - "in Json::Value::removeMember(): requires objectValue"); - if (type_ == nullValue) - return null; -#ifndef JSON_VALUE_USE_INTERNAL_MAP - CZString actualKey(key, CZString::noDuplication); + +bool Value::removeMember(const char* key, const char* cend, Value* removed) +{ + if (type_ != objectValue) { + return false; + } + CZString actualKey(key, static_cast<unsigned>(cend-key), CZString::noDuplication); ObjectValues::iterator it = value_.map_->find(actualKey); if (it == value_.map_->end()) - return null; - Value old(it->second); + return false; + *removed = it->second; value_.map_->erase(it); - return old; -#else - Value* value = value_.map_->find(key); - if (value) { - Value old(*value); - value_.map_.remove(key); - return old; - } else { - return null; - } -#endif + return true; +} +bool Value::removeMember(const char* key, Value* removed) +{ + return removeMember(key, key + strlen(key), removed); } +bool Value::removeMember(JSONCPP_STRING const& key, Value* removed) +{ + return removeMember(key.data(), key.data() + key.length(), removed); +} +Value Value::removeMember(const char* key) +{ + JSON_ASSERT_MESSAGE(type_ == nullValue || type_ == objectValue, + "in Json::Value::removeMember(): requires objectValue"); + if (type_ == nullValue) + return nullSingleton(); -Value Value::removeMember(const std::string& key) { + Value removed; // null + removeMember(key, key + strlen(key), &removed); + return removed; // still null if removeMember() did nothing +} +Value Value::removeMember(const JSONCPP_STRING& key) +{ return removeMember(key.c_str()); } +bool Value::removeIndex(ArrayIndex index, Value* removed) { + if (type_ != arrayValue) { + return false; + } + CZString key(index); + ObjectValues::iterator it = value_.map_->find(key); + if (it == value_.map_->end()) { + return false; + } + *removed = it->second; + ArrayIndex oldSize = size(); + // shift left all items left, into the place of the "removed" + for (ArrayIndex i = index; i < (oldSize - 1); ++i){ + CZString keey(i); + (*value_.map_)[keey] = (*this)[i + 1]; + } + // erase the last one ("leftover") + CZString keyLast(oldSize - 1); + ObjectValues::iterator itLast = value_.map_->find(keyLast); + value_.map_->erase(itLast); + return true; +} + #ifdef JSON_USE_CPPTL Value Value::get(const CppTL::ConstString& key, const Value& defaultValue) const { - return get(key.c_str(), defaultValue); + return get(key.c_str(), key.end_c_str(), defaultValue); } #endif -bool Value::isMember(const char* key) const { - const Value* value = &((*this)[key]); - return value != &null; +bool Value::isMember(char const* key, char const* cend) const +{ + Value const* value = find(key, cend); + return NULL != value; } - -bool Value::isMember(const std::string& key) const { - return isMember(key.c_str()); +bool Value::isMember(char const* key) const +{ + return isMember(key, key + strlen(key)); +} +bool Value::isMember(JSONCPP_STRING const& key) const +{ + return isMember(key.data(), key.data() + key.length()); } #ifdef JSON_USE_CPPTL bool Value::isMember(const CppTL::ConstString& key) const { - return isMember(key.c_str()); + return isMember(key.c_str(), key.end_c_str()); } #endif @@ -1047,19 +1261,12 @@ Value::Members Value::getMemberNames() const { return Value::Members(); Members members; members.reserve(value_.map_->size()); -#ifndef JSON_VALUE_USE_INTERNAL_MAP ObjectValues::const_iterator it = value_.map_->begin(); ObjectValues::const_iterator itEnd = value_.map_->end(); - for (; it != itEnd; ++it) - members.push_back(std::string((*it).first.c_str())); -#else - ValueInternalMap::IteratorState it; - ValueInternalMap::IteratorState itEnd; - value_.map_->makeBeginIterator(it); - value_.map_->makeEndIterator(itEnd); - for (; !ValueInternalMap::equals(it, itEnd); ValueInternalMap::increment(it)) - members.push_back(std::string(ValueInternalMap::key(it))); -#endif + for (; it != itEnd; ++it) { + members.push_back(JSONCPP_STRING((*it).first.data(), + (*it).first.length())); + } return members; } // @@ -1100,7 +1307,11 @@ bool Value::isBool() const { return type_ == booleanValue; } bool Value::isInt() const { switch (type_) { case intValue: +#if defined(JSON_HAS_INT64) return value_.int_ >= minInt && value_.int_ <= maxInt; +#else + return true; +#endif case uintValue: return value_.uint_ <= UInt(maxInt); case realValue: @@ -1115,9 +1326,17 @@ bool Value::isInt() const { bool Value::isUInt() const { switch (type_) { case intValue: +#if defined(JSON_HAS_INT64) return value_.int_ >= 0 && LargestUInt(value_.int_) <= LargestUInt(maxUInt); +#else + return value_.int_ >= 0; +#endif case uintValue: +#if defined(JSON_HAS_INT64) return value_.uint_ <= maxUInt; +#else + return true; +#endif case realValue: return value_.real_ >= 0 && value_.real_ <= maxUInt && IsIntegral(value_.real_); @@ -1168,16 +1387,28 @@ bool Value::isUInt64() const { } bool Value::isIntegral() const { + switch (type_) { + case intValue: + case uintValue: + return true; + case realValue: #if defined(JSON_HAS_INT64) - return isInt64() || isUInt64(); + // Note that maxUInt64 (= 2^64 - 1) is not exactly representable as a + // double, so double(maxUInt64) will be rounded up to 2^64. Therefore we + // require the value to be strictly less than the limit. + return value_.real_ >= double(minInt64) && value_.real_ < maxUInt64AsDouble && IsIntegral(value_.real_); #else - return isInt() || isUInt(); -#endif + return value_.real_ >= minInt && value_.real_ <= maxUInt && IsIntegral(value_.real_); +#endif // JSON_HAS_INT64 + default: + break; + } + return false; } -bool Value::isDouble() const { return type_ == realValue || isIntegral(); } +bool Value::isDouble() const { return type_ == intValue || type_ == uintValue || type_ == realValue; } -bool Value::isNumeric() const { return isIntegral() || isDouble(); } +bool Value::isNumeric() const { return isDouble(); } bool Value::isString() const { return type_ == stringValue; } @@ -1185,63 +1416,59 @@ bool Value::isArray() const { return type_ == arrayValue; } bool Value::isObject() const { return type_ == objectValue; } -void Value::setComment(const char* comment, CommentPlacement placement) { +void Value::setComment(const char* comment, size_t len, CommentPlacement placement) { if (!comments_) comments_ = new CommentInfo[numberOfCommentPlacement]; - comments_[placement].setComment(comment); + if ((len > 0) && (comment[len-1] == '\n')) { + // Always discard trailing newline, to aid indentation. + len -= 1; + } + comments_[placement].setComment(comment, len); +} + +void Value::setComment(const char* comment, CommentPlacement placement) { + setComment(comment, strlen(comment), placement); } -void Value::setComment(const std::string& comment, CommentPlacement placement) { - setComment(comment.c_str(), placement); +void Value::setComment(const JSONCPP_STRING& comment, CommentPlacement placement) { + setComment(comment.c_str(), comment.length(), placement); } bool Value::hasComment(CommentPlacement placement) const { return comments_ != 0 && comments_[placement].comment_ != 0; } -std::string Value::getComment(CommentPlacement placement) const { +JSONCPP_STRING Value::getComment(CommentPlacement placement) const { if (hasComment(placement)) return comments_[placement].comment_; return ""; } -void Value::setOffsetStart(size_t start) { start_ = start; } +void Value::setOffsetStart(ptrdiff_t start) { start_ = start; } + +void Value::setOffsetLimit(ptrdiff_t limit) { limit_ = limit; } -void Value::setOffsetLimit(size_t limit) { limit_ = limit; } +ptrdiff_t Value::getOffsetStart() const { return start_; } -size_t Value::getOffsetStart() const { return start_; } +ptrdiff_t Value::getOffsetLimit() const { return limit_; } -size_t Value::getOffsetLimit() const { return limit_; } +JSONCPP_STRING Value::toStyledString() const { + StreamWriterBuilder builder; -std::string Value::toStyledString() const { - StyledWriter writer; - return writer.write(*this); + JSONCPP_STRING out = this->hasComment(commentBefore) ? "\n" : ""; + out += Json::writeString(builder, *this); + out += "\n"; + + return out; } Value::const_iterator Value::begin() const { switch (type_) { -#ifdef JSON_VALUE_USE_INTERNAL_MAP - case arrayValue: - if (value_.array_) { - ValueInternalArray::IteratorState it; - value_.array_->makeBeginIterator(it); - return const_iterator(it); - } - break; - case objectValue: - if (value_.map_) { - ValueInternalMap::IteratorState it; - value_.map_->makeBeginIterator(it); - return const_iterator(it); - } - break; -#else case arrayValue: case objectValue: if (value_.map_) return const_iterator(value_.map_->begin()); break; -#endif default: break; } @@ -1250,28 +1477,11 @@ Value::const_iterator Value::begin() const { Value::const_iterator Value::end() const { switch (type_) { -#ifdef JSON_VALUE_USE_INTERNAL_MAP - case arrayValue: - if (value_.array_) { - ValueInternalArray::IteratorState it; - value_.array_->makeEndIterator(it); - return const_iterator(it); - } - break; - case objectValue: - if (value_.map_) { - ValueInternalMap::IteratorState it; - value_.map_->makeEndIterator(it); - return const_iterator(it); - } - break; -#else case arrayValue: case objectValue: if (value_.map_) return const_iterator(value_.map_->end()); break; -#endif default: break; } @@ -1280,28 +1490,11 @@ Value::const_iterator Value::end() const { Value::iterator Value::begin() { switch (type_) { -#ifdef JSON_VALUE_USE_INTERNAL_MAP - case arrayValue: - if (value_.array_) { - ValueInternalArray::IteratorState it; - value_.array_->makeBeginIterator(it); - return iterator(it); - } - break; - case objectValue: - if (value_.map_) { - ValueInternalMap::IteratorState it; - value_.map_->makeBeginIterator(it); - return iterator(it); - } - break; -#else case arrayValue: case objectValue: if (value_.map_) return iterator(value_.map_->begin()); break; -#endif default: break; } @@ -1310,28 +1503,11 @@ Value::iterator Value::begin() { Value::iterator Value::end() { switch (type_) { -#ifdef JSON_VALUE_USE_INTERNAL_MAP - case arrayValue: - if (value_.array_) { - ValueInternalArray::IteratorState it; - value_.array_->makeEndIterator(it); - return iterator(it); - } - break; - case objectValue: - if (value_.map_) { - ValueInternalMap::IteratorState it; - value_.map_->makeEndIterator(it); - return iterator(it); - } - break; -#else case arrayValue: case objectValue: if (value_.map_) return iterator(value_.map_->end()); break; -#endif default: break; } @@ -1349,19 +1525,20 @@ PathArgument::PathArgument(ArrayIndex index) PathArgument::PathArgument(const char* key) : key_(key), index_(), kind_(kindKey) {} -PathArgument::PathArgument(const std::string& key) +PathArgument::PathArgument(const JSONCPP_STRING& key) : key_(key.c_str()), index_(), kind_(kindKey) {} // class Path // ////////////////////////////////////////////////////////////////// -Path::Path(const std::string& path, +Path::Path(const JSONCPP_STRING& path, const PathArgument& a1, const PathArgument& a2, const PathArgument& a3, const PathArgument& a4, const PathArgument& a5) { InArgs in; + in.reserve(5); in.push_back(&a1); in.push_back(&a2); in.push_back(&a3); @@ -1370,7 +1547,7 @@ Path::Path(const std::string& path, makePath(path, in); } -void Path::makePath(const std::string& path, const InArgs& in) { +void Path::makePath(const JSONCPP_STRING& path, const InArgs& in) { const char* current = path.c_str(); const char* end = current + path.length(); InArgs::const_iterator itInArg = in.begin(); @@ -1385,23 +1562,23 @@ void Path::makePath(const std::string& path, const InArgs& in) { index = index * 10 + ArrayIndex(*current - '0'); args_.push_back(index); } - if (current == end || *current++ != ']') + if (current == end || *++current != ']') invalidPath(path, int(current - path.c_str())); } else if (*current == '%') { addPathInArg(path, in, itInArg, PathArgument::kindKey); ++current; - } else if (*current == '.') { + } else if (*current == '.' || *current == ']') { ++current; } else { const char* beginName = current; while (current != end && !strchr("[.", *current)) ++current; - args_.push_back(std::string(beginName, current)); + args_.push_back(JSONCPP_STRING(beginName, current)); } } } -void Path::addPathInArg(const std::string& /*path*/, +void Path::addPathInArg(const JSONCPP_STRING& /*path*/, const InArgs& in, InArgs::const_iterator& itInArg, PathArgument::Kind kind) { @@ -1410,11 +1587,11 @@ void Path::addPathInArg(const std::string& /*path*/, } else if ((*itInArg)->kind_ != kind) { // Error: bad argument type } else { - args_.push_back(**itInArg); + args_.push_back(**itInArg++); } } -void Path::invalidPath(const std::string& /*path*/, int /*location*/) { +void Path::invalidPath(const JSONCPP_STRING& /*path*/, int /*location*/) { // Error: invalid path. } @@ -1425,16 +1602,19 @@ const Value& Path::resolve(const Value& root) const { if (arg.kind_ == PathArgument::kindIndex) { if (!node->isArray() || !node->isValidIndex(arg.index_)) { // Error: unable to resolve path (array value expected at position... + return Value::null; } node = &((*node)[arg.index_]); } else if (arg.kind_ == PathArgument::kindKey) { if (!node->isObject()) { // Error: unable to resolve path (object value expected at position...) + return Value::null; } node = &((*node)[arg.key_]); - if (node == &Value::null) { + if (node == &Value::nullSingleton()) { // Error: unable to resolve path (object has no member named '' at // position...) + return Value::null; } } } @@ -1453,7 +1633,7 @@ Value Path::resolve(const Value& root, const Value& defaultValue) const { if (!node->isObject()) return defaultValue; node = &((*node)[arg.key_]); - if (node == &Value::null) + if (node == &Value::nullSingleton()) return defaultValue; } } diff --git a/Utilities/cmjsoncpp/src/lib_json/json_valueiterator.inl b/Utilities/cmjsoncpp/src/lib_json/json_valueiterator.inl index a9f7df63a7..5243bfe4bb 100644 --- a/Utilities/cmjsoncpp/src/lib_json/json_valueiterator.inl +++ b/Utilities/cmjsoncpp/src/lib_json/json_valueiterator.inl @@ -1,4 +1,4 @@ -// Copyright 2007-2010 Baptiste Lepilleur +// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors // Distributed under MIT license, or public domain if desired and // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE @@ -16,68 +16,29 @@ namespace Json { // ////////////////////////////////////////////////////////////////// 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_; + return other.current_ - current_; #else // Iterator for null value are initialized using the default // constructor, which initialize current_ to the default @@ -100,80 +61,58 @@ ValueIteratorBase::computeDistance(const SelfType& other) const { } 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_; isNull_ = other.isNull_; -#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.data()) { if (czstring.isStaticString()) - return Value(StaticString(czstring.c_str())); - return Value(czstring.c_str()); + return Value(StaticString(czstring.data())); + return Value(czstring.data(), czstring.data() + czstring.length()); } 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()) + if (!czstring.data()) 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 +JSONCPP_STRING ValueIteratorBase::name() const { + char const* keey; + char const* end; + keey = memberName(&end); + if (!keey) return JSONCPP_STRING(); + return JSONCPP_STRING(keey, end); +} + +char const* ValueIteratorBase::memberName() const { + const char* cname = (*current_).first.data(); + return cname ? cname : ""; +} + +char const* ValueIteratorBase::memberName(char const** end) const { + const char* cname = (*current_).first.data(); + if (!cname) { + *end = NULL; + return NULL; + } + *end = cname + (*current_).first.length(); + return cname; } // ////////////////////////////////////////////////////////////////// @@ -186,19 +125,12 @@ const char* ValueIteratorBase::memberName() const { 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(ValueIterator const& other) + : ValueIteratorBase(other) {} ValueConstIterator& ValueConstIterator:: operator=(const ValueIteratorBase& other) { @@ -216,19 +148,13 @@ operator=(const ValueIteratorBase& other) { 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) {} + : ValueIteratorBase(other) { + throwRuntimeError("ConstIterator to Iterator should never be allowed."); +} ValueIterator::ValueIterator(const ValueIterator& other) : ValueIteratorBase(other) {} diff --git a/Utilities/cmjsoncpp/src/lib_json/json_writer.cpp b/Utilities/cmjsoncpp/src/lib_json/json_writer.cpp index e3f4e53cfc..6e6e57e1f8 100644 --- a/Utilities/cmjsoncpp/src/lib_json/json_writer.cpp +++ b/Utilities/cmjsoncpp/src/lib_json/json_writer.cpp @@ -1,4 +1,4 @@ -// Copyright 2011 Baptiste Lepilleur +// Copyright 2011 Baptiste Lepilleur and The JsonCpp Authors // Distributed under MIT license, or public domain if desired and // recognized in your jurisdiction. // See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE @@ -7,15 +7,62 @@ #include <json/writer.h> #include "json_tool.h" #endif // if !defined(JSON_IS_AMALGAMATION) -#include <utility> -#include <assert.h> -#include <stdio.h> -#include <string.h> -#include <sstream> #include <iomanip> +#include <memory> +#include <sstream> +#include <utility> +#include <set> +#include <cassert> +#include <cstring> +#include <cstdio> + +#if defined(_MSC_VER) && _MSC_VER >= 1200 && _MSC_VER < 1800 // Between VC++ 6.0 and VC++ 11.0 +#include <float.h> +#define isfinite _finite +#elif defined(__sun) && defined(__SVR4) //Solaris +#if !defined(isfinite) +#include <ieeefp.h> +#define isfinite finite +#endif +#elif defined(_AIX) +#if !defined(isfinite) #include <math.h> +#define isfinite finite +#endif +#elif defined(__hpux) +#if !defined(isfinite) +#if defined(__ia64) && !defined(finite) +#define isfinite(x) ((sizeof(x) == sizeof(float) ? \ + _Isfinitef(x) : _IsFinite(x))) +#else +#include <math.h> +#define isfinite finite +#endif +#endif +#else +#include <cmath> +#if !(defined(__QNXNTO__)) // QNX already defines isfinite +#define isfinite std::isfinite +#endif +#endif + +#if defined(_MSC_VER) +#if !defined(WINCE) && defined(__STDC_SECURE_LIB__) && _MSC_VER >= 1500 // VC++ 9.0 and above +#define snprintf sprintf_s +#elif _MSC_VER >= 1900 // VC++ 14.0 and above +#define snprintf std::snprintf +#else +#define snprintf _snprintf +#endif +#elif defined(__ANDROID__) || defined(__QNXNTO__) +#define snprintf snprintf +#elif __cplusplus >= 201103L +#if !defined(__MINGW32__) && !defined(__CYGWIN__) +#define snprintf std::snprintf +#endif +#endif -#if defined(_MSC_VER) && _MSC_VER < 1500 // VC++ 8.0 and below +#if defined(__BORLANDC__) #include <float.h> #define isfinite _finite #define snprintf _snprintf @@ -62,6 +109,12 @@ namespace Json { +#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520) +typedef std::unique_ptr<StreamWriter> StreamWriterPtr; +#else +typedef std::auto_ptr<StreamWriter> StreamWriterPtr; +#endif + static bool containsControlCharacter(const char* str) { while (*str) { if (isControlCharacter(*(str++))) @@ -70,20 +123,33 @@ static bool containsControlCharacter(const char* str) { return false; } -std::string valueToString(LargestInt value) { +static bool containsControlCharacter0(const char* str, unsigned len) { + char const* end = str + len; + while (end != str) { + if (isControlCharacter(*str) || 0==*str) + return true; + ++str; + } + return false; +} + +JSONCPP_STRING valueToString(LargestInt value) { UIntToStringBuffer buffer; char* current = buffer + sizeof(buffer); - bool isNegative = value < 0; - if (isNegative) - value = -value; - uintToString(LargestUInt(value), current); - if (isNegative) + if (value == Value::minLargestInt) { + uintToString(LargestUInt(Value::maxLargestInt) + 1, current); *--current = '-'; + } else if (value < 0) { + uintToString(LargestUInt(-value), current); + *--current = '-'; + } else { + uintToString(LargestUInt(value), current); + } assert(current >= buffer); return current; } -std::string valueToString(LargestUInt value) { +JSONCPP_STRING valueToString(LargestUInt value) { UIntToStringBuffer buffer; char* current = buffer + sizeof(buffer); uintToString(value, current); @@ -93,68 +159,70 @@ std::string valueToString(LargestUInt value) { #if defined(JSON_HAS_INT64) -std::string valueToString(Int value) { +JSONCPP_STRING valueToString(Int value) { return valueToString(LargestInt(value)); } -std::string valueToString(UInt value) { +JSONCPP_STRING valueToString(UInt value) { return valueToString(LargestUInt(value)); } #endif // # if defined(JSON_HAS_INT64) -std::string valueToString(double value) { +namespace { +JSONCPP_STRING valueToString(double value, bool useSpecialFloats, unsigned int precision) { // Allocate a buffer that is more than large enough to store the 16 digits of // precision requested below. - char buffer[32]; + char buffer[36]; int len = -1; -// Print into the buffer. We need not request the alternative representation -// that always has a decimal point because JSON doesn't distingish the -// concepts of reals and integers. -#if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__) // Use secure version with - // visual studio 2005 to - // avoid warning. -#if defined(WINCE) - len = _snprintf(buffer, sizeof(buffer), "%.16g", value); -#else - len = sprintf_s(buffer, sizeof(buffer), "%.16g", value); -#endif -#else + char formatString[15]; + snprintf(formatString, sizeof(formatString), "%%.%dg", precision); + + // Print into the buffer. We need not request the alternative representation + // that always has a decimal point because JSON doesn't distingish the + // concepts of reals and integers. if (isfinite(value)) { - len = snprintf(buffer, sizeof(buffer), "%.16g", value); + len = snprintf(buffer, sizeof(buffer), formatString, value); + fixNumericLocale(buffer, buffer + len); + + // try to ensure we preserve the fact that this was given to us as a double on input + if (!strchr(buffer, '.') && !strchr(buffer, 'e')) { + strcat(buffer, ".0"); + } + } else { // IEEE standard states that NaN values will not compare to themselves if (value != value) { - len = snprintf(buffer, sizeof(buffer), "null"); + len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "NaN" : "null"); } else if (value < 0) { - len = snprintf(buffer, sizeof(buffer), "-1e+9999"); + len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "-Infinity" : "-1e+9999"); } else { - len = snprintf(buffer, sizeof(buffer), "1e+9999"); + len = snprintf(buffer, sizeof(buffer), useSpecialFloats ? "Infinity" : "1e+9999"); } - // For those, we do not need to call fixNumLoc, but it is fast. } -#endif assert(len >= 0); - fixNumericLocale(buffer, buffer + len); return buffer; } +} + +JSONCPP_STRING valueToString(double value) { return valueToString(value, false, 17); } -std::string valueToString(bool value) { return value ? "true" : "false"; } +JSONCPP_STRING valueToString(bool value) { return value ? "true" : "false"; } -std::string valueToQuotedString(const char* value) { +JSONCPP_STRING valueToQuotedString(const char* value) { if (value == NULL) return ""; // Not sure how to handle unicode... if (strpbrk(value, "\"\\\b\f\n\r\t") == NULL && !containsControlCharacter(value)) - return std::string("\"") + value + "\""; + return JSONCPP_STRING("\"") + value + "\""; // We have to walk value and escape any special characters. - // Appending to std::string is not efficient, but this should be rare. + // Appending to JSONCPP_STRING is not efficient, but this should be rare. // (Note: forward slashes are *not* rare, but I am not escaping them.) - std::string::size_type maxsize = + JSONCPP_STRING::size_type maxsize = strlen(value) * 2 + 3; // allescaped+quotes+NULL - std::string result; + JSONCPP_STRING result; result.reserve(maxsize); // to avoid lots of mallocs result += "\""; for (const char* c = value; *c != 0; ++c) { @@ -190,7 +258,85 @@ std::string valueToQuotedString(const char* value) { // sequence from occurring. default: if (isControlCharacter(*c)) { - std::ostringstream oss; + JSONCPP_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; +} + +// https://github.com/upcaste/upcaste/blob/master/src/upcore/src/cstring/strnpbrk.cpp +static char const* strnpbrk(char const* s, char const* accept, size_t n) { + assert((s || !n) && accept); + + char const* const end = s + n; + for (char const* cur = s; cur < end; ++cur) { + int const c = *cur; + for (char const* a = accept; *a; ++a) { + if (*a == c) { + return cur; + } + } + } + return NULL; +} +static JSONCPP_STRING valueToQuotedStringN(const char* value, unsigned length) { + if (value == NULL) + return ""; + // Not sure how to handle unicode... + if (strnpbrk(value, "\"\\\b\f\n\r\t", length) == NULL && + !containsControlCharacter0(value, length)) + return JSONCPP_STRING("\"") + value + "\""; + // We have to walk value and escape any special characters. + // Appending to JSONCPP_STRING is not efficient, but this should be rare. + // (Note: forward slashes are *not* rare, but I am not escaping them.) + JSONCPP_STRING::size_type maxsize = + length * 2 + 3; // allescaped+quotes+NULL + JSONCPP_STRING result; + result.reserve(maxsize); // to avoid lots of mallocs + result += "\""; + char const* end = value + length; + for (const char* c = value; c != end; ++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)) || (*c == 0)) { + JSONCPP_OSTRINGSTREAM oss; oss << "\\u" << std::hex << std::uppercase << std::setfill('0') << std::setw(4) << static_cast<int>(*c); result += oss.str(); @@ -221,8 +367,8 @@ void FastWriter::dropNullPlaceholders() { dropNullPlaceholders_ = true; } void FastWriter::omitEndingLineFeed() { omitEndingLineFeed_ = true; } -std::string FastWriter::write(const Value& root) { - document_ = ""; +JSONCPP_STRING FastWriter::write(const Value& root) { + document_.clear(); writeValue(root); if (!omitEndingLineFeed_) document_ += "\n"; @@ -245,15 +391,21 @@ void FastWriter::writeValue(const Value& value) { document_ += valueToString(value.asDouble()); break; case stringValue: - document_ += valueToQuotedString(value.asCString()); + { + // Is NULL possible for value.string_? No. + char const* str; + char const* end; + bool ok = value.getString(&str, &end); + if (ok) document_ += valueToQuotedStringN(str, static_cast<unsigned>(end-str)); break; + } case booleanValue: document_ += valueToString(value.asBool()); break; case arrayValue: { document_ += '['; - int size = value.size(); - for (int index = 0; index < size; ++index) { + ArrayIndex size = value.size(); + for (ArrayIndex index = 0; index < size; ++index) { if (index > 0) document_ += ','; writeValue(value[index]); @@ -265,10 +417,10 @@ void FastWriter::writeValue(const Value& value) { document_ += '{'; for (Value::Members::iterator it = members.begin(); it != members.end(); ++it) { - const std::string& name = *it; + const JSONCPP_STRING& name = *it; if (it != members.begin()) document_ += ','; - document_ += valueToQuotedString(name.c_str()); + document_ += valueToQuotedStringN(name.data(), static_cast<unsigned>(name.length())); document_ += yamlCompatiblityEnabled_ ? ": " : ":"; writeValue(value[name]); } @@ -283,10 +435,10 @@ void FastWriter::writeValue(const Value& value) { StyledWriter::StyledWriter() : rightMargin_(74), indentSize_(3), addChildValues_() {} -std::string StyledWriter::write(const Value& root) { - document_ = ""; +JSONCPP_STRING StyledWriter::write(const Value& root) { + document_.clear(); addChildValues_ = false; - indentString_ = ""; + indentString_.clear(); writeCommentBeforeValue(root); writeValue(root); writeCommentAfterValueOnSameLine(root); @@ -309,8 +461,15 @@ void StyledWriter::writeValue(const Value& value) { pushValue(valueToString(value.asDouble())); break; case stringValue: - pushValue(valueToQuotedString(value.asCString())); + { + // Is NULL possible for value.string_? No. + char const* str; + char const* end; + bool ok = value.getString(&str, &end); + if (ok) pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end-str))); + else pushValue(""); break; + } case booleanValue: pushValue(valueToString(value.asBool())); break; @@ -326,7 +485,7 @@ void StyledWriter::writeValue(const Value& value) { indent(); Value::Members::iterator it = members.begin(); for (;;) { - const std::string& name = *it; + const JSONCPP_STRING& name = *it; const Value& childValue = value[name]; writeCommentBeforeValue(childValue); writeWithIndent(valueToQuotedString(name.c_str())); @@ -390,23 +549,25 @@ void StyledWriter::writeArrayValue(const Value& value) { } bool StyledWriter::isMultineArray(const Value& value) { - int size = value.size(); + ArrayIndex const size = value.size(); bool isMultiLine = size * 3 >= rightMargin_; childValues_.clear(); - for (int index = 0; index < size && !isMultiLine; ++index) { + for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) { const Value& childValue = value[index]; - isMultiLine = - isMultiLine || ((childValue.isArray() || childValue.isObject()) && + 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; ++index) { + ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' + for (ArrayIndex index = 0; index < size; ++index) { + if (hasCommentForValue(value[index])) { + isMultiLine = true; + } writeValue(value[index]); - lineLength += int(childValues_[index].length()); + lineLength += static_cast<ArrayIndex>(childValues_[index].length()); } addChildValues_ = false; isMultiLine = isMultiLine || lineLength >= rightMargin_; @@ -414,7 +575,7 @@ bool StyledWriter::isMultineArray(const Value& value) { return isMultiLine; } -void StyledWriter::pushValue(const std::string& value) { +void StyledWriter::pushValue(const JSONCPP_STRING& value) { if (addChildValues_) childValues_.push_back(value); else @@ -432,15 +593,15 @@ void StyledWriter::writeIndent() { document_ += indentString_; } -void StyledWriter::writeWithIndent(const std::string& value) { +void StyledWriter::writeWithIndent(const JSONCPP_STRING& value) { writeIndent(); document_ += value; } -void StyledWriter::indent() { indentString_ += std::string(indentSize_, ' '); } +void StyledWriter::indent() { indentString_ += JSONCPP_STRING(indentSize_, ' '); } void StyledWriter::unindent() { - assert(int(indentString_.size()) >= indentSize_); + assert(indentString_.size() >= indentSize_); indentString_.resize(indentString_.size() - indentSize_); } @@ -450,26 +611,27 @@ void StyledWriter::writeCommentBeforeValue(const Value& root) { document_ += "\n"; writeIndent(); - std::string normalizedComment = normalizeEOL(root.getComment(commentBefore)); - std::string::const_iterator iter = normalizedComment.begin(); - while (iter != normalizedComment.end()) { + const JSONCPP_STRING& comment = root.getComment(commentBefore); + JSONCPP_STRING::const_iterator iter = comment.begin(); + while (iter != comment.end()) { document_ += *iter; - if (*iter == '\n' && *(iter + 1) == '/') + if (*iter == '\n' && + (iter != comment.end() && *(iter + 1) == '/')) writeIndent(); ++iter; } - // Comments are stripped of newlines, so add one here + // Comments are stripped of trailing newlines, so add one here document_ += "\n"; } void StyledWriter::writeCommentAfterValueOnSameLine(const Value& root) { if (root.hasComment(commentAfterOnSameLine)) - document_ += " " + normalizeEOL(root.getComment(commentAfterOnSameLine)); + document_ += " " + root.getComment(commentAfterOnSameLine); if (root.hasComment(commentAfter)) { document_ += "\n"; - document_ += normalizeEOL(root.getComment(commentAfter)); + document_ += root.getComment(commentAfter); document_ += "\n"; } } @@ -480,37 +642,21 @@ bool StyledWriter::hasCommentForValue(const Value& value) { 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) +StyledStreamWriter::StyledStreamWriter(JSONCPP_STRING indentation) : document_(NULL), rightMargin_(74), indentation_(indentation), addChildValues_() {} -void StyledStreamWriter::write(std::ostream& out, const Value& root) { +void StyledStreamWriter::write(JSONCPP_OSTREAM& out, const Value& root) { document_ = &out; addChildValues_ = false; - indentString_ = ""; + indentString_.clear(); + indented_ = true; writeCommentBeforeValue(root); + if (!indented_) writeIndent(); + indented_ = true; writeValue(root); writeCommentAfterValueOnSameLine(root); *document_ << "\n"; @@ -532,8 +678,15 @@ void StyledStreamWriter::writeValue(const Value& value) { pushValue(valueToString(value.asDouble())); break; case stringValue: - pushValue(valueToQuotedString(value.asCString())); + { + // Is NULL possible for value.string_? No. + char const* str; + char const* end; + bool ok = value.getString(&str, &end); + if (ok) pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end-str))); + else pushValue(""); break; + } case booleanValue: pushValue(valueToString(value.asBool())); break; @@ -549,7 +702,7 @@ void StyledStreamWriter::writeValue(const Value& value) { indent(); Value::Members::iterator it = members.begin(); for (;;) { - const std::string& name = *it; + const JSONCPP_STRING& name = *it; const Value& childValue = value[name]; writeCommentBeforeValue(childValue); writeWithIndent(valueToQuotedString(name.c_str())); @@ -586,8 +739,10 @@ void StyledStreamWriter::writeArrayValue(const Value& value) { if (hasChildValue) writeWithIndent(childValues_[index]); else { - writeIndent(); + if (!indented_) writeIndent(); + indented_ = true; writeValue(childValue); + indented_ = false; } if (++index == size) { writeCommentAfterValueOnSameLine(childValue); @@ -613,23 +768,25 @@ void StyledStreamWriter::writeArrayValue(const Value& value) { } bool StyledStreamWriter::isMultineArray(const Value& value) { - int size = value.size(); + ArrayIndex const size = value.size(); bool isMultiLine = size * 3 >= rightMargin_; childValues_.clear(); - for (int index = 0; index < size && !isMultiLine; ++index) { + for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) { const Value& childValue = value[index]; - isMultiLine = - isMultiLine || ((childValue.isArray() || childValue.isObject()) && + 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; ++index) { + ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' + for (ArrayIndex index = 0; index < size; ++index) { + if (hasCommentForValue(value[index])) { + isMultiLine = true; + } writeValue(value[index]); - lineLength += int(childValues_[index].length()); + lineLength += static_cast<ArrayIndex>(childValues_[index].length()); } addChildValues_ = false; isMultiLine = isMultiLine || lineLength >= rightMargin_; @@ -637,7 +794,7 @@ bool StyledStreamWriter::isMultineArray(const Value& value) { return isMultiLine; } -void StyledStreamWriter::pushValue(const std::string& value) { +void StyledStreamWriter::pushValue(const JSONCPP_STRING& value) { if (addChildValues_) childValues_.push_back(value); else @@ -645,24 +802,17 @@ void StyledStreamWriter::pushValue(const std::string& 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'; - } - */ + // blep intended this to look at the so-far-written string + // to determine whether we are already indented, but + // with a stream we cannot do that. So we rely on some saved state. + // The caller checks indented_. *document_ << '\n' << indentString_; } -void StyledStreamWriter::writeWithIndent(const std::string& value) { - writeIndent(); +void StyledStreamWriter::writeWithIndent(const JSONCPP_STRING& value) { + if (!indented_) writeIndent(); *document_ << value; + indented_ = false; } void StyledStreamWriter::indent() { indentString_ += indentation_; } @@ -675,19 +825,30 @@ void StyledStreamWriter::unindent() { void StyledStreamWriter::writeCommentBeforeValue(const Value& root) { if (!root.hasComment(commentBefore)) return; - *document_ << normalizeEOL(root.getComment(commentBefore)); - *document_ << "\n"; + + if (!indented_) writeIndent(); + const JSONCPP_STRING& comment = root.getComment(commentBefore); + JSONCPP_STRING::const_iterator iter = comment.begin(); + while (iter != comment.end()) { + *document_ << *iter; + if (*iter == '\n' && + (iter != comment.end() && *(iter + 1) == '/')) + // writeIndent(); // would include newline + *document_ << indentString_; + ++iter; + } + indented_ = false; } void StyledStreamWriter::writeCommentAfterValueOnSameLine(const Value& root) { if (root.hasComment(commentAfterOnSameLine)) - *document_ << " " + normalizeEOL(root.getComment(commentAfterOnSameLine)); + *document_ << ' ' << root.getComment(commentAfterOnSameLine); if (root.hasComment(commentAfter)) { - *document_ << "\n"; - *document_ << normalizeEOL(root.getComment(commentAfter)); - *document_ << "\n"; + writeIndent(); + *document_ << root.getComment(commentAfter); } + indented_ = false; } bool StyledStreamWriter::hasCommentForValue(const Value& value) { @@ -696,28 +857,400 @@ bool StyledStreamWriter::hasCommentForValue(const Value& value) { 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 +////////////////////////// +// BuiltStyledStreamWriter + +/// Scoped enums are not available until C++11. +struct CommentStyle { + /// Decide whether to write comments. + enum Enum { + None, ///< Drop all comments. + Most, ///< Recover odd behavior of previous versions (not implemented yet). + All ///< Keep all comments. + }; +}; + +struct BuiltStyledStreamWriter : public StreamWriter +{ + BuiltStyledStreamWriter( + JSONCPP_STRING const& indentation, + CommentStyle::Enum cs, + JSONCPP_STRING const& colonSymbol, + JSONCPP_STRING const& nullSymbol, + JSONCPP_STRING const& endingLineFeedSymbol, + bool useSpecialFloats, + unsigned int precision); + int write(Value const& root, JSONCPP_OSTREAM* sout) JSONCPP_OVERRIDE; +private: + void writeValue(Value const& value); + void writeArrayValue(Value const& value); + bool isMultineArray(Value const& value); + void pushValue(JSONCPP_STRING const& value); + void writeIndent(); + void writeWithIndent(JSONCPP_STRING const& value); + void indent(); + void unindent(); + void writeCommentBeforeValue(Value const& root); + void writeCommentAfterValueOnSameLine(Value const& root); + static bool hasCommentForValue(const Value& value); + + typedef std::vector<JSONCPP_STRING> ChildValues; + + ChildValues childValues_; + JSONCPP_STRING indentString_; + unsigned int rightMargin_; + JSONCPP_STRING indentation_; + CommentStyle::Enum cs_; + JSONCPP_STRING colonSymbol_; + JSONCPP_STRING nullSymbol_; + JSONCPP_STRING endingLineFeedSymbol_; + bool addChildValues_ : 1; + bool indented_ : 1; + bool useSpecialFloats_ : 1; + unsigned int precision_; +}; +BuiltStyledStreamWriter::BuiltStyledStreamWriter( + JSONCPP_STRING const& indentation, + CommentStyle::Enum cs, + JSONCPP_STRING const& colonSymbol, + JSONCPP_STRING const& nullSymbol, + JSONCPP_STRING const& endingLineFeedSymbol, + bool useSpecialFloats, + unsigned int precision) + : rightMargin_(74) + , indentation_(indentation) + , cs_(cs) + , colonSymbol_(colonSymbol) + , nullSymbol_(nullSymbol) + , endingLineFeedSymbol_(endingLineFeedSymbol) + , addChildValues_(false) + , indented_(false) + , useSpecialFloats_(useSpecialFloats) + , precision_(precision) +{ +} +int BuiltStyledStreamWriter::write(Value const& root, JSONCPP_OSTREAM* sout) +{ + sout_ = sout; + addChildValues_ = false; + indented_ = true; + indentString_.clear(); + writeCommentBeforeValue(root); + if (!indented_) writeIndent(); + indented_ = true; + writeValue(root); + writeCommentAfterValueOnSameLine(root); + *sout_ << endingLineFeedSymbol_; + sout_ = NULL; + return 0; +} +void BuiltStyledStreamWriter::writeValue(Value const& value) { + switch (value.type()) { + case nullValue: + pushValue(nullSymbol_); + break; + case intValue: + pushValue(valueToString(value.asLargestInt())); + break; + case uintValue: + pushValue(valueToString(value.asLargestUInt())); + break; + case realValue: + pushValue(valueToString(value.asDouble(), useSpecialFloats_, precision_)); + break; + case stringValue: + { + // Is NULL is possible for value.string_? No. + char const* str; + char const* end; + bool ok = value.getString(&str, &end); + if (ok) pushValue(valueToQuotedStringN(str, static_cast<unsigned>(end-str))); + else pushValue(""); + 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(); + for (;;) { + JSONCPP_STRING const& name = *it; + Value const& childValue = value[name]; + writeCommentBeforeValue(childValue); + writeWithIndent(valueToQuotedStringN(name.data(), static_cast<unsigned>(name.length()))); + *sout_ << colonSymbol_; + writeValue(childValue); + if (++it == members.end()) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + *sout_ << ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("}"); + } + } break; + } +} + +void BuiltStyledStreamWriter::writeArrayValue(Value const& value) { + unsigned size = value.size(); + if (size == 0) + pushValue("[]"); + else { + bool isMultiLine = (cs_ == CommentStyle::All) || isMultineArray(value); + if (isMultiLine) { + writeWithIndent("["); + indent(); + bool hasChildValue = !childValues_.empty(); + unsigned index = 0; + for (;;) { + Value const& childValue = value[index]; + writeCommentBeforeValue(childValue); + if (hasChildValue) + writeWithIndent(childValues_[index]); + else { + if (!indented_) writeIndent(); + indented_ = true; + writeValue(childValue); + indented_ = false; + } + if (++index == size) { + writeCommentAfterValueOnSameLine(childValue); + break; + } + *sout_ << ","; + writeCommentAfterValueOnSameLine(childValue); + } + unindent(); + writeWithIndent("]"); + } else // output on a single line { - if (*current == '\n') // convert dos EOL - ++current; - normalized += '\n'; - } else // handle unix EOL & other char - normalized += c; + assert(childValues_.size() == size); + *sout_ << "["; + if (!indentation_.empty()) *sout_ << " "; + for (unsigned index = 0; index < size; ++index) { + if (index > 0) + *sout_ << ((!indentation_.empty()) ? ", " : ","); + *sout_ << childValues_[index]; + } + if (!indentation_.empty()) *sout_ << " "; + *sout_ << "]"; + } + } +} + +bool BuiltStyledStreamWriter::isMultineArray(Value const& value) { + ArrayIndex const size = value.size(); + bool isMultiLine = size * 3 >= rightMargin_; + childValues_.clear(); + for (ArrayIndex index = 0; index < size && !isMultiLine; ++index) { + Value const& childValue = value[index]; + isMultiLine = ((childValue.isArray() || childValue.isObject()) && + childValue.size() > 0); + } + if (!isMultiLine) // check if line length > max line length + { + childValues_.reserve(size); + addChildValues_ = true; + ArrayIndex lineLength = 4 + (size - 1) * 2; // '[ ' + ', '*n + ' ]' + for (ArrayIndex index = 0; index < size; ++index) { + if (hasCommentForValue(value[index])) { + isMultiLine = true; + } + writeValue(value[index]); + lineLength += static_cast<ArrayIndex>(childValues_[index].length()); + } + addChildValues_ = false; + isMultiLine = isMultiLine || lineLength >= rightMargin_; + } + return isMultiLine; +} + +void BuiltStyledStreamWriter::pushValue(JSONCPP_STRING const& value) { + if (addChildValues_) + childValues_.push_back(value); + else + *sout_ << value; +} + +void BuiltStyledStreamWriter::writeIndent() { + // blep intended this to look at the so-far-written string + // to determine whether we are already indented, but + // with a stream we cannot do that. So we rely on some saved state. + // The caller checks indented_. + + if (!indentation_.empty()) { + // In this case, drop newlines too. + *sout_ << '\n' << indentString_; + } +} + +void BuiltStyledStreamWriter::writeWithIndent(JSONCPP_STRING const& value) { + if (!indented_) writeIndent(); + *sout_ << value; + indented_ = false; +} + +void BuiltStyledStreamWriter::indent() { indentString_ += indentation_; } + +void BuiltStyledStreamWriter::unindent() { + assert(indentString_.size() >= indentation_.size()); + indentString_.resize(indentString_.size() - indentation_.size()); +} + +void BuiltStyledStreamWriter::writeCommentBeforeValue(Value const& root) { + if (cs_ == CommentStyle::None) return; + if (!root.hasComment(commentBefore)) + return; + + if (!indented_) writeIndent(); + const JSONCPP_STRING& comment = root.getComment(commentBefore); + JSONCPP_STRING::const_iterator iter = comment.begin(); + while (iter != comment.end()) { + *sout_ << *iter; + if (*iter == '\n' && + (iter != comment.end() && *(iter + 1) == '/')) + // writeIndent(); // would write extra newline + *sout_ << indentString_; + ++iter; } - return normalized; + indented_ = false; +} + +void BuiltStyledStreamWriter::writeCommentAfterValueOnSameLine(Value const& root) { + if (cs_ == CommentStyle::None) return; + if (root.hasComment(commentAfterOnSameLine)) + *sout_ << " " + root.getComment(commentAfterOnSameLine); + + if (root.hasComment(commentAfter)) { + writeIndent(); + *sout_ << root.getComment(commentAfter); + } +} + +// static +bool BuiltStyledStreamWriter::hasCommentForValue(const Value& value) { + return value.hasComment(commentBefore) || + value.hasComment(commentAfterOnSameLine) || + value.hasComment(commentAfter); +} + +/////////////// +// StreamWriter + +StreamWriter::StreamWriter() + : sout_(NULL) +{ +} +StreamWriter::~StreamWriter() +{ +} +StreamWriter::Factory::~Factory() +{} +StreamWriterBuilder::StreamWriterBuilder() +{ + setDefaults(&settings_); +} +StreamWriterBuilder::~StreamWriterBuilder() +{} +StreamWriter* StreamWriterBuilder::newStreamWriter() const +{ + JSONCPP_STRING indentation = settings_["indentation"].asString(); + JSONCPP_STRING cs_str = settings_["commentStyle"].asString(); + bool eyc = settings_["enableYAMLCompatibility"].asBool(); + bool dnp = settings_["dropNullPlaceholders"].asBool(); + bool usf = settings_["useSpecialFloats"].asBool(); + unsigned int pre = settings_["precision"].asUInt(); + CommentStyle::Enum cs = CommentStyle::All; + if (cs_str == "All") { + cs = CommentStyle::All; + } else if (cs_str == "None") { + cs = CommentStyle::None; + } else { + throwRuntimeError("commentStyle must be 'All' or 'None'"); + } + JSONCPP_STRING colonSymbol = " : "; + if (eyc) { + colonSymbol = ": "; + } else if (indentation.empty()) { + colonSymbol = ":"; + } + JSONCPP_STRING nullSymbol = "null"; + if (dnp) { + nullSymbol.clear(); + } + if (pre > 17) pre = 17; + JSONCPP_STRING endingLineFeedSymbol; + return new BuiltStyledStreamWriter( + indentation, cs, + colonSymbol, nullSymbol, endingLineFeedSymbol, usf, pre); +} +static void getValidWriterKeys(std::set<JSONCPP_STRING>* valid_keys) +{ + valid_keys->clear(); + valid_keys->insert("indentation"); + valid_keys->insert("commentStyle"); + valid_keys->insert("enableYAMLCompatibility"); + valid_keys->insert("dropNullPlaceholders"); + valid_keys->insert("useSpecialFloats"); + valid_keys->insert("precision"); +} +bool StreamWriterBuilder::validate(Json::Value* invalid) const +{ + Json::Value my_invalid; + if (!invalid) invalid = &my_invalid; // so we do not need to test for NULL + Json::Value& inv = *invalid; + std::set<JSONCPP_STRING> valid_keys; + getValidWriterKeys(&valid_keys); + Value::Members keys = settings_.getMemberNames(); + size_t n = keys.size(); + for (size_t i = 0; i < n; ++i) { + JSONCPP_STRING const& key = keys[i]; + if (valid_keys.find(key) == valid_keys.end()) { + inv[key] = settings_[key]; + } + } + return 0u == inv.size(); +} +Value& StreamWriterBuilder::operator[](JSONCPP_STRING key) +{ + return settings_[key]; +} +// static +void StreamWriterBuilder::setDefaults(Json::Value* settings) +{ + //! [StreamWriterBuilderDefaults] + (*settings)["commentStyle"] = "All"; + (*settings)["indentation"] = "\t"; + (*settings)["enableYAMLCompatibility"] = false; + (*settings)["dropNullPlaceholders"] = false; + (*settings)["useSpecialFloats"] = false; + (*settings)["precision"] = 17; + //! [StreamWriterBuilderDefaults] +} + +JSONCPP_STRING writeString(StreamWriter::Factory const& builder, Value const& root) { + JSONCPP_OSTRINGSTREAM sout; + StreamWriterPtr const writer(builder.newStreamWriter()); + writer->write(root, &sout); + return sout.str(); } -std::ostream& operator<<(std::ostream& sout, const Value& root) { - Json::StyledStreamWriter writer; - writer.write(sout, root); +JSONCPP_OSTREAM& operator<<(JSONCPP_OSTREAM& sout, Value const& root) { + StreamWriterBuilder builder; + StreamWriterPtr const writer(builder.newStreamWriter()); + writer->write(root, &sout); return sout; } |