361 lines
12 KiB
C++
361 lines
12 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 ValueInternalArray
|
|
// //////////////////////////////////////////////////////////////////
|
|
// //////////////////////////////////////////////////////////////////
|
|
// //////////////////////////////////////////////////////////////////
|
|
|
|
ValueArrayAllocator::~ValueArrayAllocator() {}
|
|
|
|
// //////////////////////////////////////////////////////////////////
|
|
// class DefaultValueArrayAllocator
|
|
// //////////////////////////////////////////////////////////////////
|
|
#ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR
|
|
class DefaultValueArrayAllocator : public ValueArrayAllocator {
|
|
public: // overridden from ValueArrayAllocator
|
|
virtual ~DefaultValueArrayAllocator() {}
|
|
|
|
virtual ValueInternalArray* newArray() { return new ValueInternalArray(); }
|
|
|
|
virtual ValueInternalArray* newArrayCopy(const ValueInternalArray& other) {
|
|
return new ValueInternalArray(other);
|
|
}
|
|
|
|
virtual void destructArray(ValueInternalArray* array) { delete array; }
|
|
|
|
virtual void
|
|
reallocateArrayPageIndex(Value**& indexes,
|
|
ValueInternalArray::PageIndex& indexCount,
|
|
ValueInternalArray::PageIndex minNewIndexCount) {
|
|
ValueInternalArray::PageIndex newIndexCount = (indexCount * 3) / 2 + 1;
|
|
if (minNewIndexCount > newIndexCount)
|
|
newIndexCount = minNewIndexCount;
|
|
void* newIndexes = realloc(indexes, sizeof(Value*) * newIndexCount);
|
|
JSON_ASSERT_MESSAGE(newIndexes, "Couldn't realloc.");
|
|
indexCount = newIndexCount;
|
|
indexes = static_cast<Value**>(newIndexes);
|
|
}
|
|
virtual void releaseArrayPageIndex(Value** indexes,
|
|
ValueInternalArray::PageIndex indexCount) {
|
|
if (indexes)
|
|
free(indexes);
|
|
}
|
|
|
|
virtual Value* allocateArrayPage() {
|
|
return static_cast<Value*>(
|
|
malloc(sizeof(Value) * ValueInternalArray::itemsPerPage));
|
|
}
|
|
|
|
virtual void releaseArrayPage(Value* value) {
|
|
if (value)
|
|
free(value);
|
|
}
|
|
};
|
|
|
|
#else // #ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR
|
|
/// @todo make this thread-safe (lock when accessign batch allocator)
|
|
class DefaultValueArrayAllocator : public ValueArrayAllocator {
|
|
public: // overridden from ValueArrayAllocator
|
|
virtual ~DefaultValueArrayAllocator() {}
|
|
|
|
virtual ValueInternalArray* newArray() {
|
|
ValueInternalArray* array = arraysAllocator_.allocate();
|
|
new (array) ValueInternalArray(); // placement new
|
|
return array;
|
|
}
|
|
|
|
virtual ValueInternalArray* newArrayCopy(const ValueInternalArray& other) {
|
|
ValueInternalArray* array = arraysAllocator_.allocate();
|
|
new (array) ValueInternalArray(other); // placement new
|
|
return array;
|
|
}
|
|
|
|
virtual void destructArray(ValueInternalArray* array) {
|
|
if (array) {
|
|
array->~ValueInternalArray();
|
|
arraysAllocator_.release(array);
|
|
}
|
|
}
|
|
|
|
virtual void
|
|
reallocateArrayPageIndex(Value**& indexes,
|
|
ValueInternalArray::PageIndex& indexCount,
|
|
ValueInternalArray::PageIndex minNewIndexCount) {
|
|
ValueInternalArray::PageIndex newIndexCount = (indexCount * 3) / 2 + 1;
|
|
if (minNewIndexCount > newIndexCount)
|
|
newIndexCount = minNewIndexCount;
|
|
void* newIndexes = realloc(indexes, sizeof(Value*) * newIndexCount);
|
|
JSON_ASSERT_MESSAGE(newIndexes, "Couldn't realloc.");
|
|
indexCount = newIndexCount;
|
|
indexes = static_cast<Value**>(newIndexes);
|
|
}
|
|
virtual void releaseArrayPageIndex(Value** indexes,
|
|
ValueInternalArray::PageIndex indexCount) {
|
|
if (indexes)
|
|
free(indexes);
|
|
}
|
|
|
|
virtual Value* allocateArrayPage() {
|
|
return static_cast<Value*>(pagesAllocator_.allocate());
|
|
}
|
|
|
|
virtual void releaseArrayPage(Value* value) {
|
|
if (value)
|
|
pagesAllocator_.release(value);
|
|
}
|
|
|
|
private:
|
|
BatchAllocator<ValueInternalArray, 1> arraysAllocator_;
|
|
BatchAllocator<Value, ValueInternalArray::itemsPerPage> pagesAllocator_;
|
|
};
|
|
#endif // #ifdef JSON_USE_SIMPLE_INTERNAL_ALLOCATOR
|
|
|
|
static ValueArrayAllocator*& arrayAllocator() {
|
|
static DefaultValueArrayAllocator defaultAllocator;
|
|
static ValueArrayAllocator* arrayAllocator = &defaultAllocator;
|
|
return arrayAllocator;
|
|
}
|
|
|
|
static struct DummyArrayAllocatorInitializer {
|
|
DummyArrayAllocatorInitializer() {
|
|
arrayAllocator(); // ensure arrayAllocator() statics are initialized before
|
|
// main().
|
|
}
|
|
} dummyArrayAllocatorInitializer;
|
|
|
|
// //////////////////////////////////////////////////////////////////
|
|
// class ValueInternalArray
|
|
// //////////////////////////////////////////////////////////////////
|
|
bool ValueInternalArray::equals(const IteratorState& x,
|
|
const IteratorState& other) {
|
|
return x.array_ == other.array_ &&
|
|
x.currentItemIndex_ == other.currentItemIndex_ &&
|
|
x.currentPageIndex_ == other.currentPageIndex_;
|
|
}
|
|
|
|
void ValueInternalArray::increment(IteratorState& it) {
|
|
JSON_ASSERT_MESSAGE(
|
|
it.array_ && (it.currentPageIndex_ - it.array_->pages_) * itemsPerPage +
|
|
it.currentItemIndex_ !=
|
|
it.array_->size_,
|
|
"ValueInternalArray::increment(): moving iterator beyond end");
|
|
++(it.currentItemIndex_);
|
|
if (it.currentItemIndex_ == itemsPerPage) {
|
|
it.currentItemIndex_ = 0;
|
|
++(it.currentPageIndex_);
|
|
}
|
|
}
|
|
|
|
void ValueInternalArray::decrement(IteratorState& it) {
|
|
JSON_ASSERT_MESSAGE(
|
|
it.array_ && it.currentPageIndex_ == it.array_->pages_ &&
|
|
it.currentItemIndex_ == 0,
|
|
"ValueInternalArray::decrement(): moving iterator beyond end");
|
|
if (it.currentItemIndex_ == 0) {
|
|
it.currentItemIndex_ = itemsPerPage - 1;
|
|
--(it.currentPageIndex_);
|
|
} else {
|
|
--(it.currentItemIndex_);
|
|
}
|
|
}
|
|
|
|
Value& ValueInternalArray::unsafeDereference(const IteratorState& it) {
|
|
return (*(it.currentPageIndex_))[it.currentItemIndex_];
|
|
}
|
|
|
|
Value& ValueInternalArray::dereference(const IteratorState& it) {
|
|
JSON_ASSERT_MESSAGE(
|
|
it.array_ && (it.currentPageIndex_ - it.array_->pages_) * itemsPerPage +
|
|
it.currentItemIndex_ <
|
|
it.array_->size_,
|
|
"ValueInternalArray::dereference(): dereferencing invalid iterator");
|
|
return unsafeDereference(it);
|
|
}
|
|
|
|
void ValueInternalArray::makeBeginIterator(IteratorState& it) const {
|
|
it.array_ = const_cast<ValueInternalArray*>(this);
|
|
it.currentItemIndex_ = 0;
|
|
it.currentPageIndex_ = pages_;
|
|
}
|
|
|
|
void ValueInternalArray::makeIterator(IteratorState& it,
|
|
ArrayIndex index) const {
|
|
it.array_ = const_cast<ValueInternalArray*>(this);
|
|
it.currentItemIndex_ = index % itemsPerPage;
|
|
it.currentPageIndex_ = pages_ + index / itemsPerPage;
|
|
}
|
|
|
|
void ValueInternalArray::makeEndIterator(IteratorState& it) const {
|
|
makeIterator(it, size_);
|
|
}
|
|
|
|
ValueInternalArray::ValueInternalArray() : pages_(0), size_(0), pageCount_(0) {}
|
|
|
|
ValueInternalArray::ValueInternalArray(const ValueInternalArray& other)
|
|
: pages_(0), size_(other.size_), pageCount_(0) {
|
|
PageIndex minNewPages = other.size_ / itemsPerPage;
|
|
arrayAllocator()->reallocateArrayPageIndex(pages_, pageCount_, minNewPages);
|
|
JSON_ASSERT_MESSAGE(pageCount_ >= minNewPages,
|
|
"ValueInternalArray::reserve(): bad reallocation");
|
|
IteratorState itOther;
|
|
other.makeBeginIterator(itOther);
|
|
Value* value;
|
|
for (ArrayIndex index = 0; index < size_; ++index, increment(itOther)) {
|
|
if (index % itemsPerPage == 0) {
|
|
PageIndex pageIndex = index / itemsPerPage;
|
|
value = arrayAllocator()->allocateArrayPage();
|
|
pages_[pageIndex] = value;
|
|
}
|
|
new (value) Value(dereference(itOther));
|
|
}
|
|
}
|
|
|
|
ValueInternalArray& ValueInternalArray::operator=(ValueInternalArray other) {
|
|
swap(other);
|
|
return *this;
|
|
}
|
|
|
|
ValueInternalArray::~ValueInternalArray() {
|
|
// destroy all constructed items
|
|
IteratorState it;
|
|
IteratorState itEnd;
|
|
makeBeginIterator(it);
|
|
makeEndIterator(itEnd);
|
|
for (; !equals(it, itEnd); increment(it)) {
|
|
Value* value = &dereference(it);
|
|
value->~Value();
|
|
}
|
|
// release all pages
|
|
PageIndex lastPageIndex = size_ / itemsPerPage;
|
|
for (PageIndex pageIndex = 0; pageIndex < lastPageIndex; ++pageIndex)
|
|
arrayAllocator()->releaseArrayPage(pages_[pageIndex]);
|
|
// release pages index
|
|
arrayAllocator()->releaseArrayPageIndex(pages_, pageCount_);
|
|
}
|
|
|
|
void ValueInternalArray::swap(ValueInternalArray& other) {
|
|
Value** tempPages = pages_;
|
|
pages_ = other.pages_;
|
|
other.pages_ = tempPages;
|
|
ArrayIndex tempSize = size_;
|
|
size_ = other.size_;
|
|
other.size_ = tempSize;
|
|
PageIndex tempPageCount = pageCount_;
|
|
pageCount_ = other.pageCount_;
|
|
other.pageCount_ = tempPageCount;
|
|
}
|
|
|
|
void ValueInternalArray::clear() {
|
|
ValueInternalArray dummy;
|
|
swap(dummy);
|
|
}
|
|
|
|
void ValueInternalArray::resize(ArrayIndex newSize) {
|
|
if (newSize == 0)
|
|
clear();
|
|
else if (newSize < size_) {
|
|
IteratorState it;
|
|
IteratorState itEnd;
|
|
makeIterator(it, newSize);
|
|
makeIterator(itEnd, size_);
|
|
for (; !equals(it, itEnd); increment(it)) {
|
|
Value* value = &dereference(it);
|
|
value->~Value();
|
|
}
|
|
PageIndex pageIndex = (newSize + itemsPerPage - 1) / itemsPerPage;
|
|
PageIndex lastPageIndex = size_ / itemsPerPage;
|
|
for (; pageIndex < lastPageIndex; ++pageIndex)
|
|
arrayAllocator()->releaseArrayPage(pages_[pageIndex]);
|
|
size_ = newSize;
|
|
} else if (newSize > size_)
|
|
resolveReference(newSize);
|
|
}
|
|
|
|
void ValueInternalArray::makeIndexValid(ArrayIndex index) {
|
|
// Need to enlarge page index ?
|
|
if (index >= pageCount_ * itemsPerPage) {
|
|
PageIndex minNewPages = (index + 1) / itemsPerPage;
|
|
arrayAllocator()->reallocateArrayPageIndex(pages_, pageCount_, minNewPages);
|
|
JSON_ASSERT_MESSAGE(pageCount_ >= minNewPages,
|
|
"ValueInternalArray::reserve(): bad reallocation");
|
|
}
|
|
|
|
// Need to allocate new pages ?
|
|
ArrayIndex nextPageIndex = (size_ % itemsPerPage) != 0
|
|
? size_ - (size_ % itemsPerPage) + itemsPerPage
|
|
: size_;
|
|
if (nextPageIndex <= index) {
|
|
PageIndex pageIndex = nextPageIndex / itemsPerPage;
|
|
PageIndex pageToAllocate = (index - nextPageIndex) / itemsPerPage + 1;
|
|
for (; pageToAllocate-- > 0; ++pageIndex)
|
|
pages_[pageIndex] = arrayAllocator()->allocateArrayPage();
|
|
}
|
|
|
|
// Initialize all new entries
|
|
IteratorState it;
|
|
IteratorState itEnd;
|
|
makeIterator(it, size_);
|
|
size_ = index + 1;
|
|
makeIterator(itEnd, size_);
|
|
for (; !equals(it, itEnd); increment(it)) {
|
|
Value* value = &dereference(it);
|
|
new (value) Value(); // Construct a default value using placement new
|
|
}
|
|
}
|
|
|
|
Value& ValueInternalArray::resolveReference(ArrayIndex index) {
|
|
if (index >= size_)
|
|
makeIndexValid(index);
|
|
return pages_[index / itemsPerPage][index % itemsPerPage];
|
|
}
|
|
|
|
Value* ValueInternalArray::find(ArrayIndex index) const {
|
|
if (index >= size_)
|
|
return 0;
|
|
return &(pages_[index / itemsPerPage][index % itemsPerPage]);
|
|
}
|
|
|
|
ValueInternalArray::ArrayIndex ValueInternalArray::size() const {
|
|
return size_;
|
|
}
|
|
|
|
int ValueInternalArray::distance(const IteratorState& x,
|
|
const IteratorState& y) {
|
|
return indexOf(y) - indexOf(x);
|
|
}
|
|
|
|
ValueInternalArray::ArrayIndex
|
|
ValueInternalArray::indexOf(const IteratorState& iterator) {
|
|
if (!iterator.array_)
|
|
return ArrayIndex(-1);
|
|
return ArrayIndex((iterator.currentPageIndex_ - iterator.array_->pages_) *
|
|
itemsPerPage +
|
|
iterator.currentItemIndex_);
|
|
}
|
|
|
|
int ValueInternalArray::compare(const ValueInternalArray& other) const {
|
|
int sizeDiff(size_ - other.size_);
|
|
if (sizeDiff != 0)
|
|
return sizeDiff;
|
|
|
|
for (ArrayIndex index = 0; index < size_; ++index) {
|
|
int diff = pages_[index / itemsPerPage][index % itemsPerPage].compare(
|
|
other.pages_[index / itemsPerPage][index % itemsPerPage]);
|
|
if (diff != 0)
|
|
return diff;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
} // namespace Json
|