more broken work in progress master
authorJorge Gorbe <jgorbe@stcsl.es>
Mon, 14 Apr 2014 13:02:20 +0000 (15:02 +0200)
committerJorge Gorbe <jgorbe@stcsl.es>
Mon, 14 Apr 2014 13:02:20 +0000 (15:02 +0200)
fixed_capacity_vector.h
persistent_vector.h

index 57812e17ceca96f7d50fe0264e8d33de04235859..b526c01eb74087f540dcb7568cef1b6fc3ed7ae2 100644 (file)
@@ -1,6 +1,7 @@
 #ifndef FIXED_CAPACITY_VECTOR_H
 #define FIXED_CAPACITY_VECTOR_H
 
+#include <cstring>
 
 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<T*>(storage);
index d792b76d3aaec27802118df80f642716a7afe761..b683fd0b25f7841ecc5a22418e9d2fb9f37c97b9 100644 (file)
@@ -7,6 +7,7 @@
 #include <cstdint>
 #include <cassert>
 #include <stdexcept>
+// TODO: remove debug includes
 #include <iostream>
 
 namespace persistent {
@@ -21,38 +22,61 @@ 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)
@@ -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<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);
@@ -129,9 +152,9 @@ namespace persistent {
 
         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
@@ -139,12 +162,13 @@ namespace persistent {
             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);
             }
@@ -159,35 +183,35 @@ namespace persistent {
             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());
@@ -201,7 +225,7 @@ namespace persistent {
             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;
         }