#include <cstdint>
#include <cassert>
#include <stdexcept>
+// TODO: remove debug includes
#include <iostream>
namespace persistent {
template<typename T>
class persistent_vector {
private:
+ static const int LEVEL_SHIFT = 5;
+ static const int NODE_CAPACITY = 1 << LEVEL_SHIFT; // how many elements per node
+ static const int NODE_MASK = NODE_CAPACITY - 1;
+ static const uint64_t TAIL_MASK = ~NODE_MASK; // all 1s but last LEVEL_SHIFT bits, which are 0
+
typedef std::shared_ptr<void> NodePtr;
- typedef std::vector<T> ElementVector;
- typedef std::vector<NodePtr> NodePtrVector;
+
+ // fixed-length array types for our nodes
+ typedef internal::fixed_capacity_vector<NodePtr, NODE_CAPACITY> NodePtrVector; // for inner nodes
+ typedef internal::fixed_capacity_vector<T, NODE_CAPACITY> ElementVector; // for leaf nodes
// U can be either T (for leaf nodes) or NodePtr (for inner nodes)
template<typename U> struct node {
- std::vector<U> array;
+ internal::fixed_capacity_vector<U, NODE_CAPACITY> vector;
- node() : array() {
- array.reserve(32);
+ node() : vector() {
std::cerr << "Empty node created" << std::endl;
}
- node(const node& other): array(other.array) { std::cerr << "Node copied" << std::endl; }
- node(const std::vector<U> &array): array(array) { std::cerr << "Node constructed from array" << std::endl; }
+ node(const node& other): vector(other.vector) { std::cerr << "Node copied" << std::endl; }
+ node(const internal::fixed_capacity_vector<U, NODE_CAPACITY> vector)
+ : vector(vector)
+ {
+ std::cerr << "Node constructed from vector" << std::endl;
+ }
+
~node() {
std::cerr << "Node destroyed" << std::endl;
}
+
+ void push_back(const U& value) { vector.push_back(value); }
+
};
typedef node<T> LeafNode;
typedef node<NodePtr> InnerNode;
- uint64_t sz;
- uint32_t shift;
+ uint64_t sz; // vector size
+ uint32_t shift; // LEVEL_SHIFT * number of levels in the structure
NodePtr root;
- ElementVector tail;
+ // optimization: the last (partially filled) node content is stored as a tail here,
+ // this way, we only perform allocations for full nodes
+ T tail[NODE_CAPACITY];
static NodePtr EMPTY_NODE;
public:
- persistent_vector(uint64_t size, uint32_t shift, NodePtr root, const ElementVector &tail)
+ persistent_vector(uint64_t size, uint32_t shift, NodePtr root)
+ : sz(size)
+ , shift(shift)
+ , root(root)
+ {
+ }
+
+ persistent_vector(uint64_t size, uint32_t shift, NodePtr root, const ElementArray &tail)
: sz(size)
, shift(shift)
, root(root)
{
}
- persistent_vector() : persistent_vector(0, 5, EMPTY_NODE, ElementVector())
+ persistent_vector() : persistent_vector(0, LEVEL_SHIFT, EMPTY_NODE)
{
}
swap(other);
}
+ // index of the first element in the tail vector
uint64_t tailoff() {
- if (sz < 32)
- return 0;
- return ((sz-1) >> 5) << 5;
+ return sz < NODE_CAPACITY ? 0 : (sz-1) & TAIL_MASK;
}
- ElementVector &array_for(uint64_t i) {
+ LeafNode* node_for(uint64_t i) {
if (i < sz) {
if (i >= tailoff()) return tail;
NodePtr node = root;
- for (uint32_t level = shift; level > 0; level -= 5) {
+ for (uint32_t level = shift; level > 0; level -= LEVEL_SHIFT) {
InnerNode *p = (InnerNode *) node.get();
- node = p->array[(i >> level) & 0x1f];
+ node = p->array[(i >> level) & NODE_MASK];
}
LeafNode *p = (LeafNode *) node.get();
return p->array;
}
const T& nth(uint64_t i) {
- ElementVector &v = array_for(i);
- return v[i & 0x1f];
+ T *v = array_for(i);
+ return v[i & NODE_MASK];
}
const T& nth(uint64_t i, const T& notFound) {
return notFound;
}
-
+ // returns the new vector resulting from the operation v[i] = val
persistent_vector<T> assocN(uint64_t i, const T& val) {
if (i < sz) {
if (i >= tailoff()) {
persistent_vector<T> result(sz, shift, root, tail);
- result.tail[i & 0x1f] = val;
+ result.tail[i & NODE_MASK] = val;
return result;
}
return persistent_vector<T>(sz, shift, do_assoc(shift, root, i, val), tail);
persistent_vector<T> cons(const T& val) {
uint64_t i = sz;
- if (sz - tailoff() < 32) {
+ if (sz - tailoff() < NODE_CAPACITY) {
persistent_vector<T> result(sz+1, shift, root, tail);
- result.tail.push_back(val);
+ result.tail[i & NODE_MASK] = val;
return result;
}
// tail full, push into tree
NodePtr tail_node = std::make_shared<LeafNode>(tail);
uint32_t new_shift = shift;
// overflow root?
- if ((sz >> 5) > (1 << shift)) {
+ if ((sz >> LEVEL_SHIFT) > (1 << shift)) {
new_root = std::make_shared<InnerNode>();
InnerNode *p = (InnerNode *) new_root.get();
- p->array.push_back(root);
- p->array.push_back(new_path(shift, tail_node));
- new_shift += 5;
+ p->array[0] = root;
+ p->array[1] = new_path(shift, tail_node);
+ p->size = 2;
+ new_shift += LEVEL_SHIFT;
} else {
new_root = push_tail(shift, root, tail_node);
}
if (shift == 0) { // leaf node
const LeafNode &node = *((LeafNode*) pNode.get());
ret = std::make_shared<LeafNode>(node);
- ((LeafNode*)ret.get())->array[i & 0x1f] = val;
+ ((LeafNode*)ret.get())->array[i & NODE_MASK] = val;
} else { // inner node
const InnerNode &node = *((InnerNode*) pNode.get());
ret = std::make_shared<InnerNode>(node);
- uint64_t subidx = (i >> shift) & 0x1f;
- ((InnerNode*)ret.get())->array[subidx] = do_assoc(shift-5, node.array[subidx], i, val);
+ uint64_t subidx = (i >> shift) & NODE_MASK;
+ ((InnerNode*)ret.get())->array[subidx] = do_assoc(shift-LEVEL_SHIFT, node.array[subidx], i, val);
}
return ret;
}
NodePtr push_tail(uint32_t level, NodePtr parent, NodePtr tail_node) {
- uint64_t subidx = ((sz - 1) >> level) & 0x1f;
+ uint64_t subidx = ((sz - 1) >> level) & NODE_MASK;
NodePtr node_to_insert;
const InnerNode &parentRef = *((InnerNode*) parent.get());
NodePtr ret = std::make_shared<InnerNode>(parentRef);
- NodePtrVector &array = ((InnerNode*) ret.get())->array;
- if (level == 5) {
+ InnerNode *n = (InnerNode*) ret.get();
+ if (level == LEVEL_SHIFT) {
node_to_insert = tail_node;
} else {
- if (subidx < parentRef.array.size()) {
- NodePtr child = parentRef.array[subidx];
- node_to_insert = push_tail(level-5, child, tail_node);
+ NodePtr child = parentRef.array[subidx];
+ if (child) {
+ node_to_insert = push_tail(level-LEVEL_SHIFT, child, tail_node);
} else {
- node_to_insert = new_path(level-5, tail_node);
+ node_to_insert = new_path(level-LEVEL_SHIFT, tail_node);
}
}
- if (subidx < array.size()) {
+ if (subidx < n->size()) {
array[subidx] = node_to_insert;
} else {
assert(subidx == array.size());
if (level == 0)
return node;
NodePtr ret = std::make_shared<InnerNode>();
- ((InnerNode*) ret.get())->array.push_back(new_path(level-5, node));
+ ((InnerNode*) ret.get())->array.push_back(new_path(level-LEVEL_SHIFT, node));
return ret;
}