Blame | Last modification | View Log | RSS feed
// 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] );}elsebreak;}}ValueMapAllocator::~ValueMapAllocator(){}#ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATORclass DefaultValueMapAllocator : public ValueMapAllocator{public: // overridden from ValueMapAllocatorvirtual 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 ValueMapAllocatorvirtual ValueInternalMap *newMap(){ValueInternalMap *map = mapsAllocator_.allocate();new (map) ValueInternalMap(); // placement newreturn map;}virtual ValueInternalMap *newMapCopy( const ValueInternalMap &other ){ValueInternalMap *map = mapsAllocator_.allocate();new (map) ValueInternalMap( other ); // placement newreturn 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<ValueInternalMap,1> mapsAllocator_;BatchAllocator<ValueInternalLink,1> linksAllocator_;};#endifstatic 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_ );}}voidValueInternalMap::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;}voidValueInternalMap::clear(){ValueInternalMap dummy;swap( dummy );}ValueInternalMap::BucketIndexValueInternalMap::size() const{return itemCount_;}boolValueInternalMap::reserveDelta( BucketIndex growth ){return reserve( itemCount_ + growth );}boolValueInternalMap::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<Value *>( 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 );}voidValueInternalMap::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;}}}}voidValueInternalMap::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 1for ( ;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::HashKeyValueInternalMap::hash( const char *key ) const{HashKey hash = 0;while ( *key )hash += *key++ * 37;return hash;}intValueInternalMap::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 valuesmakeBeginIterator( 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;}voidValueInternalMap::makeBeginIterator( IteratorState &it ) const{it.map_ = const_cast<ValueInternalMap *>( this );it.bucketIndex_ = 0;it.itemIndex_ = 0;it.link_ = buckets_;}voidValueInternalMap::makeEndIterator( IteratorState &it ) const{it.map_ = const_cast<ValueInternalMap *>( this );it.bucketIndex_ = bucketsSize_;it.itemIndex_ = 0;it.link_ = 0;}boolValueInternalMap::equals( const IteratorState &x, const IteratorState &other ){return x.map_ == other.map_&& x.bucketIndex_ == other.bucketIndex_&& x.link_ == other.link_&& x.itemIndex_ == other.itemIndex_;}voidValueInternalMap::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;elseiterator.link_ = &(iterator.map_->buckets_[iterator.bucketIndex_]);iterator.itemIndex_ = 0;}voidValueInternalMap::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 );}}voidValueInternalMap::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_];}intValueInternalMap::distance( const IteratorState &x, const IteratorState &y ){int offset = 0;IteratorState it = x;while ( !equals( it, y ) )increment( it );return offset;}