#define PERSISTENT_VECTOR_H
#include <memory>
+#include <vector>
+#include <utility>
#include <cstdint>
+#include <cassert>
#include <stdexcept>
+#include <iostream>
namespace persistent {
class index_out_of_bounds_exception : public std::runtime_error {
+ public:
index_out_of_bounds_exception(): std::runtime_error("Index out of bounds")
{
}
private:
typedef std::shared_ptr<void> NodePtr;
typedef std::vector<T> ElementVector;
- typedef std::vector<NodePtr> NodeVector;
+ typedef std::vector<NodePtr> NodePtrVector;
// U can be either T (for leaf nodes) or NodePtr (for inner nodes)
template<typename U> struct node {
node() : array() {
array.reserve(32);
+ 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() {
+ std::cerr << "Node destroyed" << std::endl;
}
};
+ typedef node<T> LeafNode;
+ typedef node<NodePtr> InnerNode;
- uint64_t size;
+ uint64_t sz;
uint32_t shift;
NodePtr root;
ElementVector tail;
- static std::shared_ptr<void> EMPTY_NODE;
+ static NodePtr EMPTY_NODE;
public:
- persistent_vector(uint64_t size, uint32_t shift, std::shared_ptr<void> root, std::vector<std::shared_ptr<T>> &&tail)
- : size(size)
+ persistent_vector(uint64_t size, uint32_t shift, NodePtr root, const ElementVector &tail)
+ : sz(size)
, shift(shift)
, root(root)
, tail(tail)
{
}
- persistent_vector() : persistent_vector(0, 5, EMPTY_NODE, std::vector<std::shared_ptr<T>>())
+ persistent_vector() : persistent_vector(0, 5, EMPTY_NODE, ElementVector())
+ {
+ }
+
+ persistent_vector(const persistent_vector& other)
+ : sz(other.sz)
+ , shift(other.shift)
+ , root(other.root)
+ , tail(other.tail)
{
}
+ persistent_vector &operator=(persistent_vector other)
+ {
+ swap(other);
+ }
+
uint64_t tailoff() {
- if (size < 32)
+ if (sz < 32)
return 0;
- return ((cnt-1) >> 5) << 5;
+ return ((sz-1) >> 5) << 5;
}
ElementVector &array_for(uint64_t i) {
- if (i < size) {
+ if (i < sz) {
if (i >= tailoff()) return tail;
NodePtr node = root;
for (uint32_t level = shift; level > 0; level -= 5) {
- node = (node) node->array[(i >> level) & 0x1f];
+ InnerNode *p = (InnerNode *) node.get();
+ node = p->array[(i >> level) & 0x1f];
}
- return node->array;
+ LeafNode *p = (LeafNode *) node.get();
+ return p->array;
}
throw index_out_of_bounds_exception();
}
}
const T& nth(uint64_t i, const T& notFound) {
- if (i < size) {
+ if (i < sz) {
return nth(i);
}
return notFound;
}
+
+ 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;
+ return result;
+ }
+ return persistent_vector<T>(sz, shift, do_assoc(shift, root, i, val), tail);
+ }
+ throw index_out_of_bounds_exception();
+ }
+
+ int size() {
+ return sz;
+ }
+
+ persistent_vector<T> cons(const T& val) {
+ uint64_t i = sz;
+ if (sz - tailoff() < 32) {
+ persistent_vector<T> result(sz+1, shift, root, tail);
+ result.tail.push_back(val);
+ return result;
+ }
+ // tail full, push into tree
+ NodePtr new_root;
+ NodePtr tail_node = std::make_shared<LeafNode>(tail);
+ uint32_t new_shift = shift;
+ // overflow root?
+ if ((sz >> 5) > (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;
+ } else {
+ new_root = push_tail(shift, root, tail_node);
+ }
+
+ return persistent_vector<T>(sz + 1, new_shift, new_root, {val}); // old tail full, new tail contains only val
+ }
+
+
+ private:
+ static NodePtr do_assoc(uint32_t shift, NodePtr pNode, uint64_t i, const T& val) {
+ NodePtr ret;
+ if (shift == 0) { // leaf node
+ const LeafNode &node = *((LeafNode*) pNode.get());
+ ret = std::make_shared<LeafNode>(node);
+ ((LeafNode*)ret.get())->array[i & 0x1f] = 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);
+ }
+ return ret;
+ }
+
+ NodePtr push_tail(uint32_t level, NodePtr parent, NodePtr tail_node) {
+ uint64_t subidx = ((sz - 1) >> level) & 0x1f;
+ 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) {
+ 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);
+ } else {
+ node_to_insert = new_path(level-5, tail_node);
+ }
+ }
+
+ if (subidx < array.size()) {
+ array[subidx] = node_to_insert;
+ } else {
+ assert(subidx == array.size());
+ array.push_back(node_to_insert);
+ }
+
+ return ret;
+ }
+
+ static NodePtr new_path(uint32_t level, NodePtr node) {
+ if (level == 0)
+ return node;
+ NodePtr ret = std::make_shared<InnerNode>();
+ ((InnerNode*) ret.get())->array.push_back(new_path(level-5, node));
+ return ret;
+ }
+
+
+ void swap(persistent_vector &other) {
+ using std::swap;
+ swap(sz, other.sz);
+ swap(shift, other.shift);
+ swap(root, other.root);
+ swap(tail, other.tail);
+ }
+
};
- template<typename T> std::shared_ptr<void> persistent_vector<T>::EMPTY_NODE = std::make_shared<node<void>>();
+ template<typename T> std::shared_ptr<void> persistent_vector<T>::EMPTY_NODE = std::make_shared<InnerNode>();
}