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