//------------------------------------------------------------------------------ /* Copyright (c) 2011-2013, OpenCoin, Inc. */ //============================================================================== // included by json_value.cpp // everything is within Json namespace // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // ////////////////////////////////////////////////////////////////// // 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 mapsAllocator_; BatchAllocator 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 = ( const ValueInternalMap& other ) { ValueInternalMap dummy ( other ); swap ( dummy ); 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 ( 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 = valueAllocator ()->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 ( this ); it.bucketIndex_ = 0; it.itemIndex_ = 0; it.link_ = buckets_; } void ValueInternalMap::makeEndIterator ( IteratorState& it ) const { it.map_ = const_cast ( 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; }