// 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 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=(ValueInternalMap other) { swap(other); 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 = 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; } } // namespace Json