1 /**
2 This module implements an associative array.
3 @nogc associative array, replacement for std::map and std::set.
4 
5 Difference with Phobos is that the .init are valid and it uses a B-Tree underneath
6 which makes it faster.
7 
8 Copyright: Guillaume Piolat 2015-2024.
9 Copyright: Copyright (C) 2008- by Steven Schveighoffer. Other code
10 Copyright: 2010- Andrei Alexandrescu. All rights reserved by the respective holders.
11 License:   $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
12 Authors:   Authors: Steven Schveighoffer, $(HTTP erdani.com, Andrei Alexandrescu), Guillaume Piolat
13 */
14 module dplug.core.map;
15 
16 import std.functional: binaryFun;
17 
18 import dplug.core.nogc;
19 import dplug.core.btree;
20 
21 nothrow:
22 @nogc:
23 
24 /// Creates a new empty `Map`.
25 Map!(K, V, less, allowDuplicates) makeMap(K, V, alias less = "a < b", bool allowDuplicates = false)()
26 {
27     return Map!(K, V, less, allowDuplicates)(42);
28 }
29 
30 /// Tree-map, designed to replace std::map usage.
31 /// The API should looks closely like the builtin associative arrays.
32 /// O(lg(n)) insertion, removal, and search time.
33 /// `Map` is designed to operate even without initialization through `makeMap`.
34 struct Map(K, V, alias less = "a < b", bool allowDuplicates = false)
35 {
36 public:
37 nothrow:
38 @nogc:
39 
40     this(int dummy)
41     {
42     }
43 
44     @disable this(this);
45 
46     ~this()
47     {
48     }
49 
50     /// Insert an element in the container, if the container doesn't already contain 
51     /// an element with equivalent key. 
52     /// Returns: `true` if the insertion took place.
53     bool insert(K key, V value)
54     {
55         return _tree.insert(key, value);
56     }
57 
58     /// Removes an element from the container.
59     /// Returns: `true` if the removal took place.
60     bool remove(K key)
61     {
62         return _tree.remove(key) != 0;
63     }
64 
65     /// Removes all elements from the map.
66     void clearContents()
67     {
68         destroyNoGC(_tree);
69         // _tree reset to .init, still valid
70     }
71 
72     /// Returns: A pointer to the value corresponding to this key, or null if not available.
73     ///          Live builtin associative arrays.
74     inout(V)* opBinaryRight(string op)(K key) inout if (op == "in")
75     {
76         return key in _tree;
77     }
78 
79     /// Returns: A reference to the value corresponding to this key.
80     ///          Null pointer crash if key doesn't exist. 
81     ref inout(V) opIndex(K key) inout
82     {
83         inout(V)* p = key in _tree;
84         assert(p !is null);
85         return *p;
86     }
87 
88     /// Updates a value associated with a key, creates it if necessary.
89     void opIndexAssign(V value, K key)
90     {
91         V* p = key in _tree;
92         if (p is null)
93         {
94             insert(key, value); // PERF: this particular call can assume no-dupe
95         }
96         else
97             *p = value;
98     }
99 
100     /// Returns: `true` if this key is contained.
101     bool contains(K key) const
102     {
103         return _tree.contains(key);
104     }
105 
106     /// Returns: Number of elements in the map.
107     size_t length() const
108     {
109         return _tree.length;
110     }
111 
112     /// Returns: `ttue` is the map has no element.
113     bool empty() const
114     {
115         return _tree.empty;
116     }
117 
118     // Iterate by value only
119 
120     /// Fetch a forward range on all values.
121     auto byValue()
122     {
123         return _tree.byValue();
124     }
125 
126     /// ditto
127     auto byValue() const
128     {
129         return _tree.byValue();
130     }
131 
132     // default opSlice is like byValue for builtin associative arrays
133     alias opSlice = byValue;
134 
135     // Iterate by key only
136 
137     /// Fetch a forward range on all keys.
138     auto byKey()
139     {
140         return _tree.byKey();
141     }
142 
143     /// ditto
144     auto byKey() const
145     {
146         return _tree.byKey();
147     }
148 
149     // Iterate by key-value
150     auto byKeyValue()
151     {
152         return _tree.byKeyValue();
153     }
154 
155     /// ditto
156     auto byKeyValue() const
157     {
158         return _tree.byKeyValue();
159     }
160 
161     /+
162     // Iterate by single value (return a range where all elements have equal key)
163 
164     /// Fetch a forward range on all elements with given key.
165     Range!(MapRangeType.value) byGivenKey(K key)
166     {
167        if (!isInitialized)
168             return Range!(MapRangeType.value).init;
169 
170         auto kv = KeyValue(key, V.init);
171         return Range!(MapRangeType.value)(_rbt.range(kv));
172     }
173 
174     /// ditto
175     ConstRange!(MapRangeType.value) byGivenKey(K key) const
176     {
177         if (!isInitialized)
178             return ConstRange!(MapRangeType.value).init;
179 
180         auto kv = KeyValue(key, V.init);
181         return ConstRange!(MapRangeType.value)(_rbt.range(kv));
182     }
183 
184     /// ditto
185     ImmutableRange!(MapRangeType.value) byGivenKey(K key) immutable
186     {
187         if (!isInitialized)
188             return ImmutableRange!(MapRangeType.value).init;
189 
190         auto kv = KeyValue(key, V.init);
191         return ImmutableRange!(MapRangeType.value)(_rbt.range(kv));
192     }*
193     +/
194 
195 
196 private:
197 
198     alias InternalTree = BTree!(K, V, less, allowDuplicates, false);
199     InternalTree _tree;
200 }
201 
202 unittest
203 {
204     // It should be possible to use most function of an uninitialized Map
205     // All except functions returning a range will work.
206     Map!(int, string) m;
207 
208     assert(m.length == 0);
209     assert(m.empty);
210     assert(!m.contains(7));
211 
212     auto range = m.byKey();
213     assert(range.empty);
214     foreach(e; range)
215     {
216     }
217 
218     m[1] = "fun";
219 }
220 
221 unittest
222 {
223     void test(bool removeKeys) nothrow @nogc
224     {
225         {
226             auto test = makeMap!(int, string);
227             int N = 100;
228             foreach(i; 0..N)
229             {
230                 int key = (i * 69069) % 65536;
231                 test.insert(key, "this is a test");
232             }
233             foreach(i; 0..N)
234             {
235                 int key = (i * 69069) % 65536;
236                 assert(test.contains(key));
237             }
238         
239             if (removeKeys)
240             {
241                 foreach(i; 0..N)
242                 {
243                     int key = (i * 69069) % 65536;
244                     test.remove(key);
245                 }
246             }
247             foreach(i; 0..N)
248             {
249                 int key = (i * 69069) % 65536;
250                 assert(removeKeys ^ test.contains(key)); // either keys are here or removed
251             }            
252         }
253     }
254     test(true);
255     test(false);
256 }
257 
258 unittest
259 {
260     Map!(string, int) aa = makeMap!(string, int);   // Associative array of ints that are
261     // indexed by string keys.
262     // The KeyType is string.
263     aa["hello"] = 3;  // set value associated with key "hello" to 3
264     int value = aa["hello"];  // lookup value from a key
265     assert(value == 3);    
266 
267     int* p;
268 
269     p = ("hello" in aa);
270     if (p !is null)
271     {
272         *p = 4;  // update value associated with key
273         assert(aa["hello"] == 4);
274     }
275 
276     aa.remove("hello");
277 }
278 
279 /// Creates a new empty `Set`.
280 Set!(K, less) makeSet(K, alias less = "a < b", bool allowDuplicates = false)()
281 {
282     return Set!(K, less, allowDuplicates)(42);
283 }
284 
285 
286 /// Set, designed to replace std::set usage.
287 /// O(lg(n)) insertion, removal, and search time.
288 /// `Set` is designed to operate even without initialization through `makeSet`.
289 struct Set(K, alias less = "a < b", bool allowDuplicates = false)
290 {
291 public:
292 nothrow:
293 @nogc:
294 
295     this(int dummy)
296     {
297     }
298 
299     @disable this(this);
300 
301     ~this()
302     {
303     }
304 
305     /// Insert an element in the container. 
306     /// If allowDuplicates is false, this can fail and return `false` 
307     /// if the already contains an element with equivalent key. 
308     /// Returns: `true` if the insertion took place.
309     bool insert(K key)
310     {
311         ubyte whatever = 0;
312         return _tree.insert(key, whatever);
313     }
314 
315     /// Removes an element from the container.
316     /// Returns: `true` if the removal took place.
317     bool remove(K key)
318     {
319         return _tree.remove(key) != 0;
320     }
321 
322     /// Removes all elements from the set.
323     void clearContents()
324     {
325         destroyNoGC(_tree);
326         // _tree reset to .init, still valid
327     }
328 
329     /// Returns: `true` if the element is present.
330     bool opBinaryRight(string op)(K key) inout if (op == "in")
331     {
332         return (key in _tree) !is null;
333     }
334 
335     /// Returns: `true` if the element is present.
336     bool opIndex(K key) const
337     {
338         return (key in _tree) !is null;
339     }
340 
341     /// Returns: `true` if the element is present.
342     bool contains(K key) const
343     {
344         return (key in _tree) !is null;
345     }
346 
347     /// Returns: Number of elements in the set.
348     size_t length() const
349     {
350         return _tree.length();
351     }
352 
353     /// Returns: `ttue` is the set has no element.
354     bool empty() const
355     {
356         return _tree.empty();
357     }
358 
359     // Iterate by value only
360 
361     /// Fetch a forward range on all keys.
362     auto byKey()
363     {
364         return _tree.byKey();
365     }
366 
367     /// ditto
368     auto byKey() const
369     {
370         return _tree.byKey();
371     }
372 
373     // default opSlice is like byKey for sets, since the value is a fake value.
374     alias opSlice = byKey;
375 
376 private:
377 
378     // dummy type
379     alias V = ubyte;
380 
381     alias InternalTree = BTree!(K, V, less, allowDuplicates, false);
382     InternalTree _tree;
383 
384 }
385 
386 unittest
387 {
388     // It should be possible to use most function of an uninitialized Set
389     // All except functions returning a range will work.
390     Set!(string) set;
391 
392     assert(set.length == 0);
393     assert(set.empty);
394     set.clearContents();
395     assert(!set.contains("toto"));
396     
397     auto range = set[];
398     assert(range.empty);
399     foreach(e; range)
400     {
401     }
402 
403     // Finally create the internal state
404     set.insert("titi");
405     assert(set.contains("titi"));
406 }
407 
408 
409 unittest
410 {
411     Set!(string) keywords = makeSet!string;
412 
413     assert(keywords.insert("public"));
414     assert(keywords.insert("private"));
415     assert(!keywords.insert("private"));
416 
417     assert(keywords.remove("public"));
418     assert(!keywords.remove("non-existent"));
419 
420     assert(keywords.contains("private"));
421     assert(!keywords.contains("public"));
422 }