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