From: Jorge Gorbe Date: Mon, 14 Apr 2014 13:02:20 +0000 (+0200) Subject: more broken work in progress X-Git-Url: http://slack.codemaniacs.com/git/?a=commitdiff_plain;h=HEAD;p=persistent-data-structures.git more broken work in progress --- diff --git a/fixed_capacity_vector.h b/fixed_capacity_vector.h index 57812e1..b526c01 100644 --- a/fixed_capacity_vector.h +++ b/fixed_capacity_vector.h @@ -1,6 +1,7 @@ #ifndef FIXED_CAPACITY_VECTOR_H #define FIXED_CAPACITY_VECTOR_H +#include namespace persistent { namespace internal { @@ -12,6 +13,12 @@ namespace persistent { namespace internal { public: fixed_capacity_vector(): size(0) {} + fixed_capacity_vector(const fixed_capacity_vector& other) + : size(other.size) + { + memcpy(storage, other.storage, size*sizeof(T)); + } + ~fixed_capacity_vector() { // destroy valid elements T* p = reinterpret_cast(storage); diff --git a/persistent_vector.h b/persistent_vector.h index d792b76..b683fd0 100644 --- a/persistent_vector.h +++ b/persistent_vector.h @@ -7,6 +7,7 @@ #include #include #include +// TODO: remove debug includes #include namespace persistent { @@ -21,38 +22,61 @@ namespace persistent { template 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 NodePtr; - typedef std::vector ElementVector; - typedef std::vector NodePtrVector; + + // fixed-length array types for our nodes + typedef internal::fixed_capacity_vector NodePtrVector; // for inner nodes + typedef internal::fixed_capacity_vector ElementVector; // for leaf nodes // U can be either T (for leaf nodes) or NodePtr (for inner nodes) template struct node { - std::vector array; + internal::fixed_capacity_vector 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 &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 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 LeafNode; typedef node 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) @@ -60,7 +84,7 @@ namespace persistent { { } - persistent_vector() : persistent_vector(0, 5, EMPTY_NODE, ElementVector()) + persistent_vector() : persistent_vector(0, LEVEL_SHIFT, EMPTY_NODE) { } @@ -77,20 +101,19 @@ namespace persistent { 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; @@ -99,8 +122,8 @@ namespace persistent { } 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) { @@ -110,12 +133,12 @@ namespace persistent { return notFound; } - + // returns the new vector resulting from the operation v[i] = val persistent_vector assocN(uint64_t i, const T& val) { if (i < sz) { if (i >= tailoff()) { persistent_vector result(sz, shift, root, tail); - result.tail[i & 0x1f] = val; + result.tail[i & NODE_MASK] = val; return result; } return persistent_vector(sz, shift, do_assoc(shift, root, i, val), tail); @@ -129,9 +152,9 @@ namespace persistent { persistent_vector cons(const T& val) { uint64_t i = sz; - if (sz - tailoff() < 32) { + if (sz - tailoff() < NODE_CAPACITY) { persistent_vector result(sz+1, shift, root, tail); - result.tail.push_back(val); + result.tail[i & NODE_MASK] = val; return result; } // tail full, push into tree @@ -139,12 +162,13 @@ namespace persistent { NodePtr tail_node = std::make_shared(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 *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); } @@ -159,35 +183,35 @@ namespace persistent { if (shift == 0) { // leaf node const LeafNode &node = *((LeafNode*) pNode.get()); ret = std::make_shared(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(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(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()); @@ -201,7 +225,7 @@ namespace persistent { if (level == 0) return node; NodePtr ret = std::make_shared(); - ((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; }