//------------------------------------------------------------------------------ /* This file is part of rippled: https://github.com/ripple/rippled Copyright (c) 2012, 2013 Ripple Labs Inc. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ //============================================================================== // included by json_value.cpp // everything is within Json namespace // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // class ValueInternalArray // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// ValueArrayAllocator::~ValueArrayAllocator () { } // ////////////////////////////////////////////////////////////////// // class DefaultValueArrayAllocator // ////////////////////////////////////////////////////////////////// #ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR class DefaultValueArrayAllocator : public ValueArrayAllocator { public: // overridden from ValueArrayAllocator virtual ~DefaultValueArrayAllocator () { } virtual ValueInternalArray* newArray () { return new ValueInternalArray (); } virtual ValueInternalArray* newArrayCopy ( const ValueInternalArray& other ) { return new ValueInternalArray ( other ); } virtual void destructArray ( ValueInternalArray* array ) { delete array; } virtual void reallocateArrayPageIndex ( Value**& indexes, ValueInternalArray::PageIndex& indexCount, ValueInternalArray::PageIndex minNewIndexCount ) { ValueInternalArray::PageIndex newIndexCount = (indexCount * 3) / 2 + 1; if ( minNewIndexCount > newIndexCount ) newIndexCount = minNewIndexCount; void* newIndexes = realloc ( indexes, sizeof (Value*) * newIndexCount ); if ( !newIndexes ) throw std::bad_alloc (); indexCount = newIndexCount; indexes = static_cast ( 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 ); if ( !newIndexes ) throw std::bad_alloc (); 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 ) , pageCount_ ( 0 ) , size_ ( other.size_ ) { PageIndex minNewPages = other.size_ / itemsPerPage; arrayAllocator ()->reallocateArrayPageIndex ( pages_, pageCount_, minNewPages ); JSON_ASSERT_MESSAGE ( pageCount_ >= minNewPages, "ValueInternalArray::reserve(): bad reallocation" ); IteratorState itOther; other.makeBeginIterator ( itOther ); Value* value; for ( ArrayIndex index = 0; index < size_; ++index, increment (itOther) ) { if ( index % itemsPerPage == 0 ) { PageIndex pageIndex = index / itemsPerPage; value = arrayAllocator ()->allocateArrayPage (); pages_[pageIndex] = value; } new (value) Value ( dereference ( itOther ) ); } } ValueInternalArray& ValueInternalArray::operator = ( const ValueInternalArray& other ) { ValueInternalArray temp ( other ); swap ( temp ); return *this; } ValueInternalArray::~ValueInternalArray () { // destroy all constructed items IteratorState it; IteratorState itEnd; makeBeginIterator ( it); makeEndIterator ( itEnd ); for ( ; !equals (it, itEnd); increment (it) ) { Value* value = &dereference (it); value->~Value (); } // release all pages PageIndex lastPageIndex = size_ / itemsPerPage; for ( PageIndex pageIndex = 0; pageIndex < lastPageIndex; ++pageIndex ) arrayAllocator ()->releaseArrayPage ( pages_[pageIndex] ); // release pages index arrayAllocator ()->releaseArrayPageIndex ( pages_, pageCount_ ); } void ValueInternalArray::swap ( ValueInternalArray& other ) { Value** tempPages = pages_; pages_ = other.pages_; other.pages_ = tempPages; ArrayIndex tempSize = size_; size_ = other.size_; other.size_ = tempSize; PageIndex tempPageCount = pageCount_; pageCount_ = other.pageCount_; other.pageCount_ = tempPageCount; } void ValueInternalArray::clear () { ValueInternalArray dummy; swap ( dummy ); } void ValueInternalArray::resize ( ArrayIndex newSize ) { if ( newSize == 0 ) clear (); else if ( newSize < size_ ) { IteratorState it; IteratorState itEnd; makeIterator ( it, newSize ); makeIterator ( itEnd, size_ ); for ( ; !equals (it, itEnd); increment (it) ) { Value* value = &dereference (it); value->~Value (); } PageIndex pageIndex = (newSize + itemsPerPage - 1) / itemsPerPage; PageIndex lastPageIndex = size_ / itemsPerPage; for ( ; pageIndex < lastPageIndex; ++pageIndex ) arrayAllocator ()->releaseArrayPage ( pages_[pageIndex] ); size_ = newSize; } else if ( newSize > size_ ) resolveReference ( newSize ); } void ValueInternalArray::makeIndexValid ( ArrayIndex index ) { // Need to enlarge page index ? if ( index >= pageCount_ * itemsPerPage ) { PageIndex minNewPages = (index + 1) / itemsPerPage; arrayAllocator ()->reallocateArrayPageIndex ( pages_, pageCount_, minNewPages ); JSON_ASSERT_MESSAGE ( pageCount_ >= minNewPages, "ValueInternalArray::reserve(): bad reallocation" ); } // Need to allocate new pages ? ArrayIndex nextPageIndex = (size_ % itemsPerPage) != 0 ? size_ - (size_ % itemsPerPage) + itemsPerPage : size_; if ( nextPageIndex <= index ) { PageIndex pageIndex = nextPageIndex / itemsPerPage; PageIndex pageToAllocate = (index - nextPageIndex) / itemsPerPage + 1; for ( ; pageToAllocate-- > 0; ++pageIndex ) pages_[pageIndex] = arrayAllocator ()->allocateArrayPage (); } // Initialize all new entries IteratorState it; IteratorState itEnd; makeIterator ( it, size_ ); size_ = index + 1; makeIterator ( itEnd, size_ ); for ( ; !equals (it, itEnd); increment (it) ) { Value* value = &dereference (it); new (value) Value (); // Construct a default value using placement new } } Value& ValueInternalArray::resolveReference ( ArrayIndex index ) { if ( index >= size_ ) makeIndexValid ( index ); return pages_[index / itemsPerPage][index % itemsPerPage]; } Value* ValueInternalArray::find ( ArrayIndex index ) const { if ( index >= size_ ) return 0; return & (pages_[index / itemsPerPage][index % itemsPerPage]); } ValueInternalArray::ArrayIndex ValueInternalArray::size () const { return size_; } int ValueInternalArray::distance ( const IteratorState& x, const IteratorState& y ) { return indexOf (y) - indexOf (x); } ValueInternalArray::ArrayIndex ValueInternalArray::indexOf ( const IteratorState& iterator ) { if ( !iterator.array_ ) return ArrayIndex (-1); return ArrayIndex ( (iterator.currentPageIndex_ - iterator.array_->pages_) * itemsPerPage + iterator.currentItemIndex_ ); } int ValueInternalArray::compare ( const ValueInternalArray& other ) const { int sizeDiff ( size_ - other.size_ ); if ( sizeDiff != 0 ) return sizeDiff; for ( ArrayIndex index = 0; index < size_; ++index ) { int diff = pages_[index / itemsPerPage][index % itemsPerPage].compare ( other.pages_[index / itemsPerPage][index % itemsPerPage] ); if ( diff != 0 ) return diff; } return 0; }