| // 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 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<ValueInternalMap,1> mapsAllocator_; |
| BatchAllocator<ValueInternalLink,1> 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<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 ); |
| } |
| |
| |
| 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 = 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<ValueInternalMap *>( this ); |
| it.bucketIndex_ = 0; |
| it.itemIndex_ = 0; |
| it.link_ = buckets_; |
| } |
| |
| |
| void |
| ValueInternalMap::makeEndIterator( IteratorState &it ) const |
| { |
| it.map_ = const_cast<ValueInternalMap *>( 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; |
| } |
| |
| } // namespace Json |