source: src/Common/PersistentMap.h @ b21c77a

new-env
Last change on this file since b21c77a was 982f95d, checked in by Aaron Moss <a3moss@…>, 6 years ago

Start of new environment implementation; terribly broken

  • Property mode set to 100644
File size: 10.7 KB
Line 
1//
2// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
3//
4// The contents of this file are covered under the licence agreement in the
5// file "LICENCE" distributed with Cforall.
6//
7// PersistentMap.h --
8//
9// Author           : Aaron B. Moss
10// Created On       : Wed Jun 13 16:31:00 2018
11// Last Modified By : Aaron B. Moss
12// Last Modified On : Wed Jun 13 16:31:00 2018
13// Update Count     : 1
14//
15
16#pragma once
17
18#include <cassert>
19#include <cstddef>
20#include <functional>
21#include <unordered_map>
22#include <utility>
23
24#include "GC.h"
25
26/// Wraps a hash table in a persistent data structure, using a technique based
27/// on the persistent array in Conchon & Filliatre "A Persistent Union-Find
28/// Data Structure"
29
30template<typename Key, typename Val, 
31         typename Hash = std::hash<Key>, typename Eq = std::equal_to<Key>>
32class PersistentMap : public GC_Object {
33public:
34        /// Type of this class
35        using Self = PersistentMap<Key, Val, Hash, Eq>;
36
37        /// Types of version nodes
38        enum Mode { 
39                BASE,  ///< Root node of version tree
40                REM,   ///< Key removal node
41                INS,   ///< Key update node
42                UPD    ///< Key update node
43        };
44
45private:
46        /// Type of underlying hash table
47        using Base = std::unordered_map<Key, Val, Hash, Eq>;
48       
49        /// Node inserted into underlying map
50        struct Ins {
51                Self* base;  ///< Modified map
52                Key key;     ///< Key inserted
53                Val val;     ///< Value stored
54
55                template<typename K, typename V>
56                Ins(Self* b, K&& k, V&& v) : base(b), key(std::forward<K>(k)), val(std::forward<V>(v)) {}
57        };
58
59        /// Node removed from underlying map
60        struct Rem {
61                Self* base;  ///< Modified map
62                Key key;     ///< Key removed
63
64                template<typename K>
65                Rem(Self* b, K&& k) : base(b), key(std::forward<K>(k)) {}
66        };
67
68        /// Underlying storage
69        union Data {
70                char def;
71                Base base;
72                Ins ins;
73                Rem rem;
74
75                Data() : def('\0') {}
76                ~Data() {}
77        } data;
78
79        // Mode of node
80        mutable Mode mode;
81
82        /// get mutable reference as T
83        template<typename T>
84        T& as() { return reinterpret_cast<T&>(data); }
85
86        /// get const reference as T
87        template<typename T>
88        const T& as() const { return reinterpret_cast<const T&>(data); }
89
90        /// get rvalue reference as T
91        template<typename T>
92        T&& take_as() { return std::move(as<T>()); }
93
94        /// initialize as T
95        template<typename T, typename... Args>
96        void init( Args&&... args ) {
97                new( &as<T>() ) T { std::forward<Args>(args)... };
98        }
99
100        /// reset as current mode
101        void reset() {
102                switch( mode ) {
103                        case BASE:          as<Base>().~Base(); break;
104                        case REM:           as<Rem>().~Rem();   break;
105                        case INS: case UPD: as<Ins>().~Ins();   break;
106                        default: assertf(false, "invalid mode");
107                }
108        }
109
110        /// reset as base
111        void reset_as_base() { 
112                assertf( mode == BASE, "can only reset_as_base() on BASE" );
113                as<Base>().~Base();
114        }
115
116        PersistentMap( Mode m, Base&& b ) : data(), mode(m) {
117                assertf(m == BASE, "invalid mode");
118                init<Base>(std::move(b));
119        }
120
121        template<typename K, typename V>
122        PersistentMap( Mode m, const Self* o, K&& k, V&& v ) : data(), mode(m) {
123                assertf(m == INS || m == UPD, "invalid mode");
124                init<Ins>(o, std::forward<K>(k), std::forward<V>(v));
125        }
126
127        template<typename K>
128        PersistentMap( Mode m, const Self* o, K&& k ) : data(), mode(m) {
129                assertf(m == REM, "invalid mode");
130                init<Rem>(o, std::forward<K>(k));
131        }
132
133protected:
134        void trace(const GC& gc) const {
135                switch( mode ) {
136                        case BASE: {
137                                for (const auto& entry : as<Base>()) {
138                                        gc << entry.first << entry.second;
139                                }
140                                return;
141                        }
142                        case REM: {
143                                const Rem& self = as<Rem>();
144                                gc << self.base << self.key;
145                                return;
146                        }
147                        case INS: case UPD: {
148                                const Ins& self = as<Ins>();
149                                gc << self.base << self.key << self.val;
150                                return;
151                        }
152                        default: assertf(false, "invalid mode");
153                }
154        }
155
156public:
157        using size_type = std::size_t;
158
159        using iterator = typename Base::const_iterator;
160
161        PersistentMap() : data(), mode(BASE) { init<Base>(); }
162
163        PersistentMap( const Self& o ) = delete;
164
165        Self& operator= ( const Self& o ) = delete;
166
167        ~PersistentMap() { reset(); }
168
169        /// reroot persistent map at current node
170        void reroot() const {
171                // recursive base case
172                if ( mode == BASE ) return;
173               
174                // reroot base
175                Self* mut_this = const_cast<Self*>(this);
176                Self* base = ( mode == REM ) ? mut_this->as<Rem>().base : mut_this->as<Ins>().base;
177                base->reroot();
178                assertf(base->mode == BASE, "reroot results in base");
179
180                // take map out of base
181                Base base_map = base->take_as<Base>();
182                base->reset_as_base();
183
184                // switch base to inverse of self and mutate base map
185                switch ( mode ) {
186                        case REM: {
187                                Rem& self = mut_this->as<Rem>();
188                                auto it = base_map.find( self.key );
189                                assertf( it != base_map.end(), "removed node must exist in base");
190
191                                base->init<Ins>( mut_this, std::move(self.key), std::move(it->second) );
192                                base->mode = INS;
193
194                                base_map.erase( it );
195                                break;
196                        }
197                        case INS: {
198                                Ins& self = mut_this->as<Ins>();
199
200                                base->init<Rem>( mut_this, self.key );
201                                base->mode = REM;
202
203                                base_map.emplace( std::move(self.key), std::move(self.val) );
204                                break;
205                        }
206                        case UPD: {
207                                Ins& self = mut_this->as<Ins>();
208                                auto it = base_map.find( self.key );
209                                assertf( it != base_map.end(), "updated node must exist in base");
210
211                                base->init<Ins>( mut_this, std::move(self.key), std::move(it->second) );
212                                base->mode = UPD;
213
214                                it->second = std::move(self.val);
215                                break;
216                        }
217                        default: assertf(false, "invalid mode");
218                }
219
220                // set base map into self
221                mut_this->reset();
222                mut_this->init<Base>( std::move(base_map) );
223                mode = BASE;
224        }
225
226private:
227        /// Gets the base after rerooting at the current node
228        const Base& rerooted() const {
229                reroot();
230                return as<Base>();
231        }
232
233public:
234        /// true iff the map is empty
235        bool empty() const { return rerooted().empty(); }
236
237        /// Get number of entries in map
238        size_type size() const { return rerooted().size(); }
239
240        /// Get begin iterator for map; may be invalidated by calls to non-iteration functions
241        /// or functions on other maps in the same chain
242        iterator begin() const { return rerooted().begin(); }
243
244        /// Get end iterator for map; may be invalidated by calls to non-iteration functions
245        /// or functions on other maps in the same chain
246        iterator end() const { return rerooted().end(); }
247
248        /// Get underlying map iterator for value
249        iterator find(const Key& k) const { return rerooted().find( k ); }
250
251        /// Check if value is present
252        size_type count(const Key& k) const { return rerooted().count( k ); }
253
254        /// Get value; undefined behavior if not present
255        const Val& get(const Key& k) const {
256                const Base& self = rerooted();
257                auto it = self.find( k );
258                assertf(it != self.end(), "get target not present");
259                return it->second;
260        }
261
262        /// Get value; returns default if not present
263        template<typename V>
264        Val get_or_default(const Key& k, V&& d) const {
265                const Base& self = rerooted();
266                auto it = self.find( k );
267                if ( it == self.end() ) return d;
268                else return it->second;
269        }
270
271        /// Set value, storing new map in output variable
272        template<typename K, typename V>
273        Self* set(K&& k, V&& v) {
274                reroot();
275                assertf(mode == BASE, "reroot results in base");
276
277                // transfer map to new node
278                Self* ret = new Self{ BASE, take_as<Base>() };
279                reset_as_base();
280                Base& base_map = ret->as<Base>();
281
282                // check if this is update or insert
283                auto it = base_map.find( k );
284                if ( it == base_map.end() ) {
285                        // set self to REM node and insert into base
286                        init<Rem>( ret, k );
287                        mode = REM;
288
289                        base_map.emplace_hint( it, std::forward<K>(k), std::forward<V>(v) );
290                } else {
291                        // set self to UPD node and modify base
292                        init<Ins>( ret, std::forward<K>(k), std::move(it->second) );
293                        mode = UPD;
294
295                        it->second = std::forward<V>(v);
296                }
297
298                return ret;
299        }
300
301        /// Remove value, storing new map in output variable; does nothing if key not in map
302        Self* erase(const Key& k) {
303                reroot();
304                assertf(mode == BASE, "reroot results in base");
305
306                // exit early if key does not exist in map
307                if ( ! as<Base>().count( k ) ) return this;
308
309                // transfer map to new node
310                Self* ret = new Self{ BASE, take_as<Base>() };
311                reset_as_base();
312                Base& base_map = ret->as<Base>();
313
314                // set self to INS node and remove from base
315                init<Ins>( ret, k, base_map[k] );
316                mode = INS;
317               
318                base_map.erase( k );
319
320                return ret;
321        }
322
323        /// smart reference for indexing interface
324        class Entry {
325        friend PersistentMap;
326                Self* base;
327                const Key& key;
328
329        public:
330                Entry(Self* b, const Key& k) : base(b), key(k) {}
331
332                /// Gets the underlying map instance
333                Self* get_base() const { return base; }
334
335                /// Gets the key
336                const Key& get_key() const { return key; }
337
338                /// Checks if the key exists in the map
339                bool exists() const { return base->count(key); }
340
341                /// Gets the value for the key (if it exists)
342                const Val& get() const { return base->get(key); }
343
344                /// Cast version of get
345                operator const Val& () const { return base->get(key); }
346
347                /// Sets the value into the key; returns entry pointing to new set
348                template<typename V>
349                Entry& set(V&& v) {
350                        base = base->set(key, std::forward<V>(v));
351                        return *this;
352                }
353
354                /// Assignment version of set
355                template<typename V>
356                Entry& operator= (V&& v) {
357                        base = base->set(key, std::forward<V>(v));
358                        return *this;
359                }
360
361                /// Gets value or initializes to new value from args
362                template<typename... Args>
363                Entry& get_or(Args&&... args) {
364                        base = base->get_or(key, std::forward<Args>(args)...);
365                        return *this;
366                }
367        };
368
369        /// Gets smart reference to slot with given key
370        Entry operator[] (const Key& k) { return { this, k }; }
371
372        /// Gets Entry for key, initializing from args if not present
373        template<typename... Args>
374        Entry get_or_insert(const Key& k, Args&&... args) {
375                Base& base_map = rerooted();
376
377                // check already present
378                if ( base_map.count(k) ) return { this, k };
379
380                // otherwise insert based on parameters
381                base_map.emplace( k, Val{ std::forward<Args>(args)... } );
382                Self* ret = new Self{ BASE, std::move(base_map) };
383
384                // update self to point to new base
385                reset_as_base();
386                init<Rem>( ret, k );
387                mode = REM;
388
389                // return entry for new base
390                return { ret, k };
391        }
392
393        /// Get version node type
394        Mode get_mode() const { return mode; }
395
396        /// Get next version up the revision tree (self if base node)
397        const Self* get_base() const {
398                switch ( mode ) {
399                        case BASE:          return this;
400                        case REM:           return as<Rem>().base;
401                        case INS: case UPD: return as<Ins>().base;
402                        default: assertf(false, "invalid mode");
403                }
404        }
405
406        /// Get key of revision node (undefined if called on base)
407        const Key& get_key() const {
408                switch ( mode ) {
409                        case REM:           return as<Rem>().key;
410                        case INS: case UPD: return as<Ins>().key;
411                        default: assertf(false, "invalid mode for get_key()");
412                }
413        }
414
415        /// Get value of insert/update revision node (undefined otherwise)
416        const Val& get_val() const {
417                switch ( mode ) {
418                        case INS: case UPD: return as<Ins>().val;
419                        default: assertf(false, "invalid mode for get_val()");
420                }
421        }
422};
423
424// Local Variables: //
425// tab-width: 4 //
426// mode: c++ //
427// compile-command: "make install" //
428// End: //
Note: See TracBrowser for help on using the repository browser.