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 }