/*++ Copyright (c) 2011 Microsoft Corporation Module Name: parray.h Abstract: Persistent Arrays. Author: Leonardo de Moura (leonardo) 2011-02-21. Revision History: --*/ #ifndef _PARRAY_H_ #define _PARRAY_H_ #include"vector.h" #include"trace.h" template class parray_manager { public: typedef typename C::value value; typedef typename C::value_manager value_manager; typedef typename C::allocator allocator; private: static size_t capacity(value * vs) { return vs == 0 ? 0 : (reinterpret_cast(vs))[-1]; } value * allocate_values(size_t c) { size_t * mem = static_cast(m_allocator.allocate(sizeof(value)*c + sizeof(size_t))); *mem = c; ++mem; value * r = reinterpret_cast(mem); SASSERT(capacity(r) == c); TRACE("parray_mem", tout << "allocated values[" << c << "]: " << r << "\n";); return r; } void deallocate_values(value * vs) { if (vs == 0) return; size_t c = capacity(vs); TRACE("parray_mem", tout << "deallocated values[" << c << "]: " << vs << "\n";); size_t * mem = reinterpret_cast(vs); --mem; m_allocator.deallocate(sizeof(value)*c + sizeof(size_t), mem); } enum ckind { SET, PUSH_BACK, POP_BACK, ROOT }; struct cell { unsigned m_ref_count:30; unsigned m_kind:2; union { unsigned m_idx; unsigned m_size; }; value m_elem; union { cell * m_next; value * m_values; }; ckind kind() const { return static_cast(m_kind); } unsigned idx() const { SASSERT(kind() != ROOT); return m_idx; } unsigned size() const { SASSERT(kind() == ROOT); return m_size; } cell * next() const { SASSERT(kind() != ROOT); return m_next; } value const & elem() const { SASSERT(kind() == SET || kind() == PUSH_BACK); return m_elem; } cell(ckind k):m_ref_count(1), m_kind(k), m_size(0), m_values(0) {} }; value_manager & m_vmanager; allocator & m_allocator; ptr_vector m_get_values_tmp; ptr_vector m_reroot_tmp; void inc_ref(value const & v) { if (C::ref_count) m_vmanager.inc_ref(v); } void dec_ref(value const & v) { if (C::ref_count) m_vmanager.dec_ref(v); } void dec_ref(unsigned sz, value * vs) { if (C::ref_count) for (unsigned i = 0; i < sz; i++) m_vmanager.dec_ref(vs[i]); } cell * mk(ckind k) { cell * r = new (m_allocator.allocate(sizeof(cell))) cell(k); TRACE("parray_mem", tout << "allocated cell: " << r << "\n";); return r; } void del(cell * c) { while (true) { cell * next = 0; switch (c->kind()) { case SET: case PUSH_BACK: dec_ref(c->elem()); next = c->next(); break; case POP_BACK: next = c->next(); break; case ROOT: dec_ref(c->size(), c->m_values); deallocate_values(c->m_values); break; } TRACE("parray_mem", tout << "deallocated cell: " << c << "\n";); c->~cell(); m_allocator.deallocate(sizeof(cell), c); if (next == 0) return; SASSERT(next->m_ref_count > 0); next->m_ref_count--; if (next->m_ref_count > 0) return; c = next; } } void inc_ref(cell * c) { if (!c) return; c->m_ref_count++; } void dec_ref(cell * c) { if (!c) return; TRACE("parray_mem", tout << "dec_ref(" << c << "), ref_count: " << c->m_ref_count << "\n";); SASSERT(c->m_ref_count > 0); c->m_ref_count--; if (c->m_ref_count == 0) del(c); } void expand(value * & vs) { size_t curr_capacity = capacity(vs); size_t new_capacity = curr_capacity == 0 ? 2 : (3 * curr_capacity + 1) >> 1; value * new_vs = allocate_values(new_capacity); if (curr_capacity > 0) { for (size_t i = 0; i < curr_capacity; i++) new_vs[i] = vs[i]; deallocate_values(vs); } vs = new_vs; } void rset(value * vs, unsigned i, value const & v) { inc_ref(v); dec_ref(vs[i]); vs[i] = v; } void rset(cell * c, unsigned i, value const & v) { SASSERT(c->kind() == ROOT); SASSERT(i < c->size()); rset(c->m_values, i, v); } void rpush_back(value * & vs, unsigned & sz, value const & v) { if (sz == capacity(vs)) expand(vs); SASSERT(sz < capacity(vs)); inc_ref(v); vs[sz] = v; sz++; } void rpush_back(cell * c, value const & v) { SASSERT(c->kind() == ROOT); rpush_back(c->m_values, c->m_size, v); } void rpop_back(value * vs, unsigned & sz) { sz--; dec_ref(vs[sz]); } void rpop_back(cell * c) { SASSERT(c->kind() == ROOT); rpop_back(c->m_values, c->m_size); } void copy_values(value * s, unsigned sz, value * & t) { SASSERT(t == 0); t = allocate_values(capacity(s)); for (unsigned i = 0; i < sz; i++) { t[i] = s[i]; inc_ref(t[i]); } } unsigned get_values(cell * s, value * & vs) { ptr_vector & cs = m_get_values_tmp; cs.reset(); cell * r = s; while (r->kind() != ROOT) { cs.push_back(r); r = r->next(); } SASSERT(r->kind() == ROOT); unsigned sz = r->m_size; vs = 0; copy_values(r->m_values, sz, vs); unsigned i = cs.size(); while (i > 0) { --i; cell * curr = cs[i]; switch (curr->kind()) { case SET: rset(vs, curr->m_idx, curr->m_elem); break; case POP_BACK: rpop_back(vs, sz); break; case PUSH_BACK: rpush_back(vs, sz, curr->m_elem); break; case ROOT: UNREACHABLE(); break; } } return sz; } void unfold(cell * c) { if (c->kind() == ROOT) return; value * vs; unsigned sz = get_values(c, vs); dec_ref(c->m_next); if (c->kind() == SET || c->kind() == PUSH_BACK) dec_ref(c->m_elem); c->m_next = 0; c->m_kind = ROOT; c->m_size = sz; c->m_values = vs; SASSERT(c->kind() == ROOT); } public: class ref { cell * m_ref; unsigned m_updt_counter; // counter for minimizing memory consumption when using preserve_roots option ref(cell * r):m_ref(r), m_updt_counter(0) {} bool root() const { return m_ref->kind() == ROOT; } bool unshared() const { return m_ref->m_ref_count == 1; } friend class parray_manager; public: ref():m_ref(0), m_updt_counter(0) {} }; public: parray_manager(value_manager & m, allocator & a): m_vmanager(m), m_allocator(a) { } value_manager & manager() { return m_vmanager; } void mk(ref & r) { dec_ref(r.m_ref); cell * new_c = mk(ROOT); r.m_ref = new_c; r.m_updt_counter = 0; SASSERT(new_c->m_ref_count == 1); } void del(ref & r) { dec_ref(r.m_ref); r.m_ref = 0; r.m_updt_counter = 0; } void copy(ref const & s, ref & t) { inc_ref(s.m_ref); dec_ref(t.m_ref); t.m_ref = s.m_ref; t.m_updt_counter = 0; } unsigned size(ref const & r) const { cell * c = r.m_ref; if (c == 0) return 0; while (true) { switch (c->kind()) { case SET: c = c->next(); break; case PUSH_BACK: return c->idx() + 1; case POP_BACK: return c->idx() - 1; case ROOT: return c->size(); } } } bool empty(ref const & r) const { return size(r) == 0; } value const & get(ref const & r, unsigned i) const { SASSERT(i < size(r)); unsigned trail_sz = 0; cell * c = r.m_ref; while (true) { if (trail_sz > C::max_trail_sz) { const_cast(this)->reroot(const_cast(r)); SASSERT(r.m_ref->kind() == ROOT); return r.m_ref->m_values[i]; } switch (c->kind()) { case SET: case PUSH_BACK: if (i == c->idx()) return c->elem(); trail_sz++; c = c->next(); break; case POP_BACK: trail_sz++; c = c->next(); break; case ROOT: return c->m_values[i]; } } } void set(ref & r, unsigned i, value const & v) { SASSERT(i < size(r)); if (r.root()) { if (r.unshared()) { rset(r.m_ref, i, v); return; } if (C::preserve_roots) { if (r.m_updt_counter > size(r)) { unshare(r); SASSERT(r.unshared()); SASSERT(r.m_updt_counter == 0); rset(r.m_ref, i, v); return; } r.m_updt_counter++; cell * c = r.m_ref; cell * new_c = mk(ROOT); new_c->m_size = c->m_size; new_c->m_values = c->m_values; inc_ref(new_c); c->m_kind = SET; c->m_idx = i; c->m_elem = c->m_values[i]; inc_ref(c->m_elem); c->m_next = new_c; dec_ref(c); r.m_ref = new_c; rset(new_c, i, v); SASSERT(new_c->m_ref_count == 2); return; } } cell * new_c = mk(SET); new_c->m_idx = i; inc_ref(v); new_c->m_elem = v; new_c->m_next = r.m_ref; r.m_ref = new_c; SASSERT(new_c->m_ref_count == 1); } void set(ref const & s, unsigned i, value const & v, ref & r) { SASSERT(i < size(s)); if (&s == &r) { set(r, i, v); return; } copy(s, r); set(r, i, v); } void push_back(ref & r, value const & v) { if (r.m_ref == 0) mk(r); if (r.root()) { if (r.unshared()) { rpush_back(r.m_ref, v); return; } if (C::preserve_roots) { if (r.m_updt_counter > size(r)) { unshare(r); SASSERT(r.unshared()); SASSERT(r.m_updt_counter == 0); rpush_back(r.m_ref, v); return; } r.m_updt_counter++; cell * c = r.m_ref; SASSERT(c->m_ref_count > 1); cell * new_c = mk(ROOT); new_c->m_size = c->m_size; new_c->m_values = c->m_values; inc_ref(new_c); c->m_kind = POP_BACK; c->m_idx = new_c->m_size + 1; c->m_next = new_c; dec_ref(c); r.m_ref = new_c; rpush_back(new_c, v); SASSERT(new_c->m_ref_count == 2); return; } } cell * new_c = mk(PUSH_BACK); new_c->m_idx = size(r.m_ref); inc_ref(v); new_c->m_elem = v; new_c->m_next = r.m_ref; r.m_ref = new_c; SASSERT(new_c->m_ref_count == 1); } void push_back(ref const & s, value const & v, ref & r) { if (&s == &r) { push_back(r, v); return; } copy(s, r); push_back(r, v); } void pop_back(ref & r) { SASSERT(!empty(r)); if (r.root()) { if (r.unshared()) { rpop_back(r.m_ref); return; } if (C::preserve_roots) { if (r.m_updt_counter > size(r)) { unshare(r); SASSERT(r.unshared()); SASSERT(r.m_updt_counter == 0); rpop_back(r.m_ref); return; } r.m_updt_counter++; cell * c = r.m_ref; SASSERT(c->m_ref_count > 1); cell * new_c = mk(ROOT); new_c->m_size = c->m_size; new_c->m_values = c->m_values; inc_ref(new_c); c->m_kind = PUSH_BACK; c->m_idx = new_c->m_size - 1; c->m_elem = new_c->m_values[c->m_idx]; inc_ref(c->m_elem); c->m_next = new_c; dec_ref(c); r.m_ref = new_c; rpop_back(new_c); SASSERT(new_c->m_ref_count == 2); return; } } cell * new_c = mk(POP_BACK); new_c->m_idx = size(r.m_ref); new_c->m_next = r.m_ref; r.m_ref = new_c; SASSERT(new_c->m_ref_count == 1); } void pop_back(ref const & s, ref & r) { SASSERT(!empty(s)); if (&s == &r) { pop_back(r); return; } copy(s, r); pop_back(r); } void unshare(ref & r) { if (r.root() && r.unshared()) return; cell * c = r.m_ref; cell * new_c = mk(ROOT); new_c->m_size = get_values(c, new_c->m_values); SASSERT(new_c->m_ref_count == 1); dec_ref(c); r.m_ref = new_c; r.m_updt_counter = 0; SASSERT(r.root()); SASSERT(r.unshared()); } void unfold(ref & r) { if (r.root()) return; unfold(r.m_ref); r.m_updt_counter = 0; SASSERT(r.root()); } void reroot(ref & r) { if (r.root()) return; ptr_vector & cs = m_reroot_tmp; cs.reset(); unsigned r_sz = size(r); unsigned trail_split_idx = r_sz / C::factor; unsigned i = 0; cell * c = r.m_ref; while (c->kind() != ROOT && i < trail_split_idx) { cs.push_back(c); c = c->next(); i++; } if (c->kind() != ROOT) { // root is too far away. unfold(c); } SASSERT(c->kind() == ROOT); i = cs.size(); while (i > 0) { --i; cell * p = cs[i]; SASSERT(c->m_kind == ROOT); unsigned sz = c->m_size; value * vs = c->m_values; SASSERT(p->m_kind != ROOT); SASSERT(p->m_next == c); switch (p->m_kind) { case SET: c->m_kind = SET; c->m_idx = p->m_idx; c->m_elem = vs[c->m_idx]; vs[p->m_idx] = p->m_elem; break; case PUSH_BACK: c->m_kind = POP_BACK; if (sz == capacity(vs)) expand(vs); c->m_idx = sz; vs[sz] = p->m_elem; sz++; break; case POP_BACK: c->m_kind = PUSH_BACK; --sz; c->m_idx = sz; c->m_elem = vs[sz]; break; case ROOT: UNREACHABLE(); break; } inc_ref(p); c->m_next = p; // p does not point to c anymore dec_ref(c); p->m_kind = ROOT; p->m_size = sz; p->m_values = vs; c = p; } SASSERT(c == r.m_ref); SASSERT(c->kind() == ROOT); SASSERT(c->m_size == r_sz); r.m_updt_counter = 0; SASSERT(r.root()); } void display_info(std::ostream & out, ref const & r) { cell * c = r.m_ref; if (c == 0) { out << ""; return; } while (true) { out << "cell[" << c << ", "; switch (c->kind()) { case SET: out << "set, " << c->m_idx; break; case PUSH_BACK: out << "push, " << c->m_idx; break; case POP_BACK: out << "pop, " << c->m_idx; break; case ROOT: out << "root, " << c->m_size << ", " << capacity(c->m_values); break; } out << "]#" << c->m_ref_count; if (c->kind() == ROOT) break; out << " -> "; c = c->next(); } } }; template struct dummy_value_manager { void inc_ref(T const &) {} void dec_ref(T const &) {} }; #endif