474 lines
15 KiB
C++
474 lines
15 KiB
C++
// 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=(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<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
|