Commit 4a330282 authored by Kenton Varda's avatar Kenton Varda

Add IntertionOrderIndex.

parent b0868e34
......@@ -893,5 +893,109 @@ KJ_TEST("benchmark: std::set<StringPtr>") {
}
}
// =======================================================================================
KJ_TEST("insertion order index") {
Table<uint, InsertionOrderIndex> table;
{
auto range = table.ordered();
KJ_EXPECT(range.begin() == range.end());
}
table.insert(12);
table.insert(34);
table.insert(56);
table.insert(78);
{
auto range = table.ordered();
auto iter = range.begin();
KJ_ASSERT(iter != range.end());
KJ_EXPECT(*iter++ == 12);
KJ_ASSERT(iter != range.end());
KJ_EXPECT(*iter++ == 34);
KJ_ASSERT(iter != range.end());
KJ_EXPECT(*iter++ == 56);
KJ_ASSERT(iter != range.end());
KJ_EXPECT(*iter++ == 78);
KJ_EXPECT(iter == range.end());
KJ_EXPECT(*--iter == 78);
KJ_EXPECT(*--iter == 56);
KJ_EXPECT(*--iter == 34);
KJ_EXPECT(*--iter == 12);
KJ_EXPECT(iter == range.begin());
}
table.erase(table.begin()[1]);
{
auto range = table.ordered();
auto iter = range.begin();
KJ_ASSERT(iter != range.end());
KJ_EXPECT(*iter++ == 12);
KJ_ASSERT(iter != range.end());
KJ_EXPECT(*iter++ == 56);
KJ_ASSERT(iter != range.end());
KJ_EXPECT(*iter++ == 78);
KJ_EXPECT(iter == range.end());
KJ_EXPECT(*--iter == 78);
KJ_EXPECT(*--iter == 56);
KJ_EXPECT(*--iter == 12);
KJ_EXPECT(iter == range.begin());
}
// Allocate enough more elements to cause a resize.
table.insert(111);
table.insert(222);
table.insert(333);
table.insert(444);
table.insert(555);
table.insert(666);
table.insert(777);
table.insert(888);
table.insert(999);
{
auto range = table.ordered();
auto iter = range.begin();
KJ_ASSERT(iter != range.end());
KJ_EXPECT(*iter++ == 12);
KJ_ASSERT(iter != range.end());
KJ_EXPECT(*iter++ == 56);
KJ_ASSERT(iter != range.end());
KJ_EXPECT(*iter++ == 78);
KJ_ASSERT(iter != range.end());
KJ_EXPECT(*iter++ == 111);
KJ_ASSERT(iter != range.end());
KJ_EXPECT(*iter++ == 222);
KJ_ASSERT(iter != range.end());
KJ_EXPECT(*iter++ == 333);
KJ_ASSERT(iter != range.end());
KJ_EXPECT(*iter++ == 444);
KJ_ASSERT(iter != range.end());
KJ_EXPECT(*iter++ == 555);
KJ_ASSERT(iter != range.end());
KJ_EXPECT(*iter++ == 666);
KJ_ASSERT(iter != range.end());
KJ_EXPECT(*iter++ == 777);
KJ_ASSERT(iter != range.end());
KJ_EXPECT(*iter++ == 888);
KJ_ASSERT(iter != range.end());
KJ_EXPECT(*iter++ == 999);
KJ_EXPECT(iter == range.end());
}
// Remove everything.
while (table.size() > 0) {
table.erase(*table.begin());
}
{
auto range = table.ordered();
KJ_EXPECT(range.begin() == range.end());
}
}
} // namespace kj
} // namespace _
......@@ -721,4 +721,90 @@ void BTreeImpl::Parent::eraseAfter(uint i) {
}
} // namespace _
// =======================================================================================
// Insertion order
const InsertionOrderIndex::Link InsertionOrderIndex::EMPTY_LINK = { 0, 0 };
InsertionOrderIndex::InsertionOrderIndex(): capacity(0), links(const_cast<Link*>(&EMPTY_LINK)) {}
InsertionOrderIndex::~InsertionOrderIndex() noexcept(false) {
if (links != &EMPTY_LINK) delete links;
}
void InsertionOrderIndex::reserve(size_t size) {
KJ_ASSERT(size < (1u << 31), "Table too big for InsertionOrderIndex");
if (size > capacity) {
// Need to grow.
// Note that `size` and `capacity` do not include the special link[0].
// Round up to the next power of 2.
size_t allocation = 1u << (_::lg(size) + 1);
KJ_DASSERT(allocation > size);
KJ_DASSERT(allocation <= size * 2);
// Round first allocation up to 8.
allocation = kj::max(allocation, 8);
Link* newLinks = new Link[allocation];
#ifdef KJ_DEBUG
// To catch bugs, fill unused links with 0xff.
memset(newLinks, 0xff, allocation * sizeof(Link));
#endif
_::acopy(newLinks, links, capacity + 1);
if (links != &EMPTY_LINK) delete[] links;
links = newLinks;
capacity = allocation - 1;
}
}
void InsertionOrderIndex::clear() {
links[0] = Link { 0, 0 };
#ifdef KJ_DEBUG
// To catch bugs, fill unused links with 0xff.
memset(links + 1, 0xff, capacity * sizeof(Link));
#endif
}
kj::Maybe<size_t> InsertionOrderIndex::insertImpl(size_t pos) {
if (pos >= capacity) {
reserve(pos + 1);
}
links[pos + 1].prev = links[0].prev;
links[pos + 1].next = 0;
links[links[0].prev].next = pos + 1;
links[0].prev = pos + 1;
return nullptr;
}
void InsertionOrderIndex::eraseImpl(size_t pos) {
Link& link = links[pos + 1];
links[link.next].prev = link.prev;
links[link.prev].next = link.next;
#ifdef KJ_DEBUG
memset(&link, 0xff, sizeof(Link));
#endif
}
void InsertionOrderIndex::moveImpl(size_t oldPos, size_t newPos) {
Link& link = links[oldPos + 1];
Link& newLink = links[newPos + 1];
newLink = link;
KJ_DASSERT(links[link.next].prev == oldPos + 1);
KJ_DASSERT(links[link.prev].next == oldPos + 1);
links[link.next].prev = newPos + 1;
links[link.prev].next = newPos + 1;
#ifdef KJ_DEBUG
memset(&link, 0xff, sizeof(Link));
#endif
}
} // namespace kj
......@@ -1388,4 +1388,95 @@ private:
}
};
// -----------------------------------------------------------------------------
// Insertion order index
class InsertionOrderIndex {
// Table index which allows iterating over elements in order of insertion. This index cannot
// be used for Table::find(), but can be used for Table::ordered().
struct Link;
public:
InsertionOrderIndex();
~InsertionOrderIndex() noexcept(false);
class Iterator {
public:
Iterator(const Link* links, uint pos)
: links(links), pos(pos) {}
inline size_t operator*() const {
KJ_IREQUIRE(pos != 0, "can't derefrence end() iterator");
return pos - 1;
};
inline Iterator& operator++() {
pos = links[pos].next;
return *this;
}
inline Iterator operator++(int) {
Iterator result = *this;
++*this;
return result;
}
inline Iterator& operator--() {
pos = links[pos].prev;
return *this;
}
inline Iterator operator--(int) {
Iterator result = *this;
--*this;
return result;
}
inline bool operator==(const Iterator& other) const {
return pos == other.pos;
}
inline bool operator!=(const Iterator& other) const {
return pos != other.pos;
}
private:
const Link* links;
uint pos;
};
void reserve(size_t size);
void clear();
inline Iterator begin() const { return Iterator(links, links[0].next); }
inline Iterator end() const { return Iterator(links, 0); }
template <typename Row>
kj::Maybe<size_t> insert(kj::ArrayPtr<Row> table, size_t pos) {
return insertImpl(pos);
}
template <typename Row>
void erase(kj::ArrayPtr<Row> table, size_t pos) {
eraseImpl(pos);
}
template <typename Row>
void move(kj::ArrayPtr<Row> table, size_t oldPos, size_t newPos) {
return moveImpl(oldPos, newPos);
}
private:
struct Link {
uint next;
uint prev;
};
uint capacity;
Link* links;
// links[0] is special: links[0].next points to the first link, links[0].prev points to the last.
// links[n+1] corresponds no row n.
kj::Maybe<size_t> insertImpl(size_t pos);
void eraseImpl(size_t pos);
void moveImpl(size_t oldPos, size_t newPos);
static const Link EMPTY_LINK;
};
} // namespace kj
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment