Files
rippled/src/ripple/json/impl/json_internalarray.inl
2013-11-20 10:16:46 -08:00

498 lines
14 KiB
C++

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