// 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(newIndexes); } virtual void releaseArrayPageIndex(Value** indexes, ValueInternalArray::PageIndex indexCount) { if (indexes) free(indexes); } virtual Value* allocateArrayPage() { return static_cast( 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(newIndexes); } virtual void releaseArrayPageIndex(Value** indexes, ValueInternalArray::PageIndex indexCount) { if (indexes) free(indexes); } virtual Value* allocateArrayPage() { return static_cast(pagesAllocator_.allocate()); } virtual void releaseArrayPage(Value* value) { if (value) pagesAllocator_.release(value); } private: BatchAllocator arraysAllocator_; BatchAllocator 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(this); it.currentItemIndex_ = 0; it.currentPageIndex_ = pages_; } void ValueInternalArray::makeIterator(IteratorState& it, ArrayIndex index) const { it.array_ = const_cast(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