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 }