1 module collections.linkedhashmap; 2 3 import collections.commons; 4 import std.functional : binaryFun; 5 import std.traits; 6 7 import collections.linkedmap; 8 9 /** 10 * Implements a linked hashmap. 11 * If retainKeys enabled, the map will retain the original keys and hash collisions can be resorted, but can be disabled 12 * by setting keyEqual to null. 13 * It is a modified linked map, and the `ovrwrtBhvr` still works as with the linked map. 14 */ 15 public struct LinkedHashMap(K, E, alias hashFunc = defaultHash128!(K), alias equal = "a == b", bool retainKeys = false, 16 alias keyEqual = "a == b", bool ovrwrtBhvr = true) { 17 ///true if nogc Indexing is enabled. 18 static enum bool nogcIndexing = hasFunctionAttributes!(hashFunc, "@nogc"); 19 alias HashType = ReturnType!hashFunc; 20 21 private struct Node { 22 HashType hashCode; ///Identifier hash 23 static if (retainKeys) { 24 K key; ///The key for this node 25 } 26 E elem; ///Element stored within the node 27 Node* next; ///Next node, null if none exists 28 Node* prev; ///Previous node, null if none exists 29 ///Keturns a string representation of the node. 30 string toString() const { 31 import std.conv : to; 32 string result = "hashCode: " ~ to!string(hashCode) ~ " ; elem: " ~ to!string(elem); 33 static if (retainKeys) result ~= " ; key: " ~ to!string(key); 34 if (next) result ~= " ; next: [" ~ next.toString ~ "]"; 35 return result; 36 } 37 } 38 39 protected Node* root; ///Root element. 40 protected Node* last; ///Last element. 41 protected size_t _length;///N. of currently stored elements 42 static if (retainKeys) { 43 /** 44 * opApply override for foreach 45 */ 46 int opApply(scope int delegate(ref E) dg) { 47 Node* crnt = root; 48 while (crnt) { 49 if (dg(crnt.elem)) return 1; 50 crnt = crnt.next; 51 } 52 return 0; 53 } 54 /** 55 * opApply override for foreach 56 */ 57 int opApply(scope int delegate(K, ref E) dg) { 58 Node* crnt = root; 59 while (crnt) { 60 if (dg(crnt.key, crnt.elem)) return 1; 61 crnt = crnt.next; 62 } 63 return 0; 64 } 65 /** 66 * opApplyReverse override for foreach_reverse 67 */ 68 int opApplyReverse(scope int delegate(ref E) dg) { 69 Node* crnt = last; 70 while (crnt) { 71 if (dg(crnt.elem)) return 1; 72 crnt = crnt.prev; 73 } 74 return 0; 75 } 76 /** 77 * opApplyReverse override for foreach_reverse 78 */ 79 int opApplyReverse(scope int delegate(K, ref E) dg) { 80 Node* crnt = last; 81 while (crnt) { 82 if (dg(crnt.key, crnt.elem)) return 1; 83 crnt = crnt.prev; 84 } 85 return 0; 86 } 87 package static string makeFunc() { 88 string makeFuncIndiv(string args) { 89 return ` 90 int opApply(scope int delegate(ref E) ` ~ args ~ ` dg) ` ~ args ~ ` { 91 Node* crnt = root; 92 while (crnt) { 93 if (dg(crnt.elem)) return 1; 94 crnt = crnt.next; 95 } 96 return 0; 97 } 98 int opApply(scope int delegate(K, ref E) ` ~ args ~ ` dg) ` ~ args ~ ` { 99 Node* crnt = root; 100 while (crnt) { 101 if (dg(crnt.key, crnt.elem)) return 1; 102 crnt = crnt.next; 103 } 104 return 0; 105 } 106 int opApplyReverse(scope int delegate(ref E) ` ~ args ~ ` dg) ` ~ args ~ ` { 107 Node* crnt = last; 108 while (crnt) { 109 if (dg(crnt.elem)) return 1; 110 crnt = crnt.prev; 111 } 112 return 0; 113 } 114 int opApplyReverse(scope int delegate(K, ref E) ` ~ args ~ ` dg) ` ~ args ~ ` { 115 Node* crnt = last; 116 while (crnt) { 117 if (dg(crnt.key, crnt.elem)) return 1; 118 crnt = crnt.prev; 119 } 120 return 0; 121 }`; 122 } 123 string result; 124 foreach (attr; attrList) { 125 result ~= makeFuncIndiv(attr); 126 } 127 return result; 128 } 129 mixin(makeFunc); 130 /** 131 * Assigns a value to the given key. 132 */ 133 auto opIndexAssign(E value, K key) @safe pure nothrow { 134 const HashType hashCode = hashFunc(key); 135 Node** crnt = &root; 136 while (*crnt) { 137 static if (ovrwrtBhvr) { 138 static if (keyEqual !is null) { 139 if (binaryFun!equal((*crnt).hashCode, hashCode) && binaryFun!keyEqual((*crnt).key, key)) { 140 return (*crnt).elem = value; 141 } 142 } else { 143 if (binaryFun!equal((*crnt).hashCode, hashCode)) { 144 (*crnt).key = key; 145 return (*crnt).elem = value; 146 } 147 } 148 } else { 149 if (binaryFun!equal((*crnt).hashCode, hashCode)) { 150 if ((*crnt).prev) (*crnt).prev.next = (*crnt).next; 151 *crnt = (*crnt).next; 152 _length--; 153 } 154 } 155 crnt = &(*crnt).next; 156 } 157 //prev.next = new Node(key, value, null); 158 *crnt = new Node(hashCode, key, value, null, *crnt); 159 last = *crnt; 160 _length++; 161 return value; 162 } 163 static if (nogcIndexing) { 164 165 /** 166 * Returns true if key is found. 167 */ 168 bool has(K key) @nogc @safe pure nothrow { 169 const HashType hash = hashFunc(key); 170 Node* crnt = root; 171 while (crnt) { 172 static if (keyEqual !is null) { 173 if (binaryFun!equal(crnt.hashCode, hash) && binaryFun!keyEqual(crnt.key , key)) { 174 return true; 175 } 176 } else { 177 if (binaryFun!equal(crnt.hashCode, hash)) { 178 return true; 179 } 180 } 181 crnt = crnt.next; 182 } 183 return false; 184 } 185 auto opBinaryRight(string op)(const K key) @nogc @safe pure nothrow { 186 static if (op == "in") { 187 return has(key); 188 } else static assert(0, "Operator not supported!"); 189 } 190 } else { 191 /** 192 * Returns true if key is found. 193 */ 194 bool has(K key) @safe pure nothrow { 195 const HashType hash = hashFunc(key); 196 Node* crnt = root; 197 while (crnt) { 198 static if (keyEqual !is null) { 199 if (binaryFun!equal(crnt.hashCode, hash) && binaryFun!keyEqual(crnt.key , key)) { 200 return true; 201 } 202 } else { 203 if (binaryFun!equal(crnt.hashCode, hash)) { 204 return true; 205 } 206 } 207 crnt = crnt.next; 208 } 209 return false; 210 } 211 auto opBinaryRight(string op)(const K key) @safe pure nothrow { 212 static if (op == "in") { 213 return has(key); 214 } else static assert(0, "Operator not supported!"); 215 } 216 } 217 } else { 218 /** 219 * Removes the element with the specified key. 220 * Returns the removed element. 221 */ 222 E remove(K key) @safe pure nothrow { 223 return remove(hashFunc(key)); 224 } 225 /** 226 * Removes the element with the specified hashcode. 227 * Returns the removed element. 228 */ 229 E remove(HashType hashcode) @safe pure nothrow { 230 Node** crnt = &root; 231 while (*crnt) { 232 if (binaryFun!equal((*crnt).hashCode, hashcode)) { 233 E result = (*crnt).elem; 234 //if ((*crnt).prev) (*crnt).prev.next = (*crnt).next; 235 if ((*crnt).next is null) last = (*crnt).prev; 236 else (*crnt).next.prev = (*crnt).prev; 237 *crnt = (*crnt).next; 238 _length--; 239 return result; 240 } 241 crnt = &(*crnt).next; 242 } 243 return E.init; 244 } 245 /** 246 * opApply override for foreach 247 */ 248 int opApply(scope int delegate(ref E) dg) { 249 Node* crnt = root; 250 while (crnt) { 251 if (dg(crnt.elem)) return 1; 252 crnt = crnt.next; 253 } 254 return 0; 255 } 256 /** 257 * opApply override for foreach 258 */ 259 int opApply(scope int delegate(HashType, ref E) dg) { 260 Node* crnt = root; 261 while (crnt) { 262 if (dg(crnt.hashCode, crnt.elem)) return 1; 263 crnt = crnt.next; 264 } 265 return 0; 266 } 267 /** 268 * opApplyReverse override for foreach_reverse 269 */ 270 int opApplyReverse(scope int delegate(ref E) dg) { 271 Node* crnt = last; 272 while (crnt) { 273 if (dg(crnt.elem)) return 1; 274 crnt = crnt.prev; 275 } 276 return 0; 277 } 278 /** 279 * opApplyReverse override for foreach_reverse 280 */ 281 int opApplyReverse(scope int delegate(HashType, ref E) dg) { 282 Node* crnt = last; 283 while (crnt) { 284 if (dg(crnt.hashCode, crnt.elem)) return 1; 285 crnt = crnt.prev; 286 } 287 return 0; 288 } 289 package static string makeFunc() { 290 string makeFuncIndiv(string args) { 291 return ` 292 int opApply(scope int delegate(ref E) ` ~ args ~ ` dg) ` ~ args ~ ` { 293 Node* crnt = root; 294 while (crnt) { 295 if (dg(crnt.elem)) return 1; 296 crnt = crnt.next; 297 } 298 return 0; 299 } 300 int opApply(scope int delegate(HashType, ref E) ` ~ args ~ ` dg) ` ~ args ~ ` { 301 Node* crnt = root; 302 while (crnt) { 303 if (dg(crnt.hashCode, crnt.elem)) return 1; 304 crnt = crnt.next; 305 } 306 return 0; 307 } 308 int opApplyReverse(scope int delegate(ref E) ` ~ args ~ ` dg) ` ~ args ~ ` { 309 Node* crnt = last; 310 while (crnt) { 311 if (dg(crnt.elem)) return 1; 312 crnt = crnt.prev; 313 } 314 return 0; 315 } 316 int opApplyReverse(scope int delegate(HashType, ref E) ` ~ args ~ ` dg) ` ~ args ~ ` { 317 Node* crnt = last; 318 while (crnt) { 319 if (dg(crnt.hashCode, crnt.elem)) return 1; 320 crnt = crnt.prev; 321 } 322 return 0; 323 }`; 324 } 325 string result; 326 foreach (attr; attrList) { 327 result ~= makeFuncIndiv(attr); 328 } 329 return result; 330 } 331 mixin(makeFunc); 332 /** 333 * Assigns a value to the given key. 334 */ 335 auto opIndexAssign(E value, K key) @safe pure nothrow { 336 const HashType hashCode = hashFunc(key); 337 Node** crnt = &root; 338 while (*crnt) { 339 static if (ovrwrtBhvr) { 340 if (binaryFun!equal((*crnt).hashCode, hashCode)) return (*crnt).elem = value; 341 } else { 342 if (binaryFun!equal((*crnt).hashCode, hashCode)) { 343 if (&(*crnt).prev) (*crnt).prev.next = (*crnt).next; 344 *crnt = &(*crnt).next; 345 length--; 346 } 347 } 348 crnt = &(*crnt).next; 349 } 350 //prev.next = new Node(key, value, null); 351 *crnt = new Node(hashCode, value, null, last); 352 last = *crnt; 353 _length++; 354 return value; 355 } 356 static if (nogcIndexing) { 357 /** 358 * Returns true if key is found. 359 */ 360 bool has(K key) @nogc @safe pure nothrow { 361 return has(hashFunc(key)); 362 } 363 auto opBinaryRight(string op, K)(const K key) @nogc @safe pure nothrow { 364 static if (op == "in") { 365 return has(key); 366 } else static assert(0, "Operator not supported!"); 367 } 368 } else { 369 /** 370 * Returns true if key is found. 371 */ 372 bool has(K key) @safe pure { 373 return has(hashFunc(key)); 374 } 375 auto opBinaryRight(string op, K)(const K key) @safe pure { 376 static if (op == "in") { 377 return has(key); 378 } else static assert(0, "Operator not supported!"); 379 } 380 } 381 } 382 static if (!retainKeys || keyEqual !is null) { 383 static if (nogcIndexing) { 384 /** 385 * Returns the element with the given key, or E.init if key is not found. 386 */ 387 E opIndex(K key) @nogc @safe pure nothrow { 388 const HashType hashCode = hashFunc(key); 389 Node* crnt = root; 390 while (crnt) { 391 if (binaryFun!equal(crnt.hashCode, hashCode)) return crnt.elem; 392 crnt = crnt.next; 393 } 394 return E.init; 395 } 396 /** 397 * Returns the element with the given key, or E.init if key is not found. 398 */ 399 E opIndex(HashType hashCode) @nogc @safe pure nothrow { 400 Node* crnt = root; 401 while (crnt) { 402 if (binaryFun!equal(crnt.hashCode, hashCode)) return crnt.elem; 403 crnt = crnt.next; 404 } 405 return E.init; 406 } 407 /** 408 * Returns the pointer to the element with the given key, of null if key is not found. 409 */ 410 E* getPtr(K key) @nogc @safe pure nothrow { 411 const HashType hashCode = hashFunc(key); 412 Node* crnt = root; 413 while (crnt) { 414 if (binaryFun!equal(crnt.hashCode, hashCode)) return &(crnt.elem); 415 crnt = crnt.next; 416 } 417 return null; 418 } 419 /** 420 * Returns the pointer to the element with the given key, of null if key is not found. 421 */ 422 E* getPtr(HashType hashCode) @nogc @safe pure nothrow { 423 Node* crnt = root; 424 while (crnt) { 425 if (binaryFun!equal(crnt.hashCode, hashCode)) return &(crnt.elem); 426 crnt = crnt.next; 427 } 428 return null; 429 } 430 } else { 431 /** 432 * Returns the element with the given key, or E.init if key is not found. 433 */ 434 ref E opIndex(K key) @safe pure { 435 const HashType hashCode = hashFunc(key); 436 Node* crnt = root; 437 while (crnt) { 438 if (binaryFun!equal(crnt.hashCode, hashCode)) return crnt.elem; 439 crnt = crnt.next; 440 } 441 throw new ElementNotFoundException("Key not found!"); 442 } 443 /** 444 * Returns the element with the given key, or E.init if key is not found. 445 */ 446 ref E opIndex(HashType hashCode) @safe pure { 447 Node* crnt = root; 448 while (crnt) { 449 if (binaryFun!equal(crnt.hashCode, hashCode)) return crnt.elem; 450 crnt = crnt.next; 451 } 452 throw new ElementNotFoundException("Key not found!"); 453 } 454 } 455 } else { 456 static if (nogcIndexing) { 457 /** 458 * Returns the element with the given key, or E.init if key is not found. 459 */ 460 E opIndex(K key) @nogc @safe pure nothrow { 461 const HashType hashCode = hashFunc(key); 462 Node* crnt = root; 463 while (crnt) { 464 if (binaryFun!equal(crnt.hashCode, hashCode) && binaryFun!keyEqual(crnt.key, key)) return crnt.elem; 465 crnt = crnt.next; 466 } 467 return E.init; 468 } 469 /** 470 * Returns the pointer to the element with the given key, of null if key is not found. 471 */ 472 E* getPtr(K key) @nogc @safe pure nothrow { 473 const HashType hashCode = hashFunc(key); 474 Node* crnt = root; 475 while (crnt) { 476 if (binaryFun!equal(crnt.hashCode, hashCode) && binaryFun!keyEqual(crnt.key, key)) return &(crnt.elem); 477 crnt = crnt.next; 478 } 479 return null; 480 } 481 } else { 482 /** 483 * Returns the element with the given key, or E.init if key is not found. 484 */ 485 ref E opIndex(K key) @safe pure { 486 const HashType hashCode = hashFunc(key); 487 Node* crnt = root; 488 while (crnt) { 489 if (binaryFun!equal(crnt.hashCode, hashCode) && binaryFun!keyEqual(crnt.key, key)) return crnt.elem; 490 crnt = crnt.next; 491 } 492 throw new ElementNotFoundException("Key not found!"); 493 } 494 } 495 } 496 /** 497 * Returns true if key is found. 498 */ 499 bool has(HashType hashCode) @nogc @safe pure nothrow { 500 Node* crnt = root; 501 while (crnt) { 502 if (binaryFun!equal(crnt.hashCode, hashCode)) return true; 503 crnt = crnt.next; 504 } 505 return false; 506 } 507 auto opBinaryRight(string op)(const HashType key) @nogc @safe pure nothrow { 508 static if (op == "in") { 509 return has(key); 510 } else static assert(0, "Operator not supported!"); 511 } 512 /** 513 * Returns the number of elements in the LinkedMap. 514 */ 515 @property size_t length() @nogc @safe pure nothrow const { 516 return _length; 517 } 518 ///Returns the string representation of this container format 519 string toString() const { 520 if (root) return root.toString; 521 else return "empty"; 522 } 523 } 524 525 unittest { 526 import std.random : uniform; 527 import std.exception : assertThrown; 528 import std.stdio : writeln; 529 { 530 alias StringMap = LinkedHashMap!(string, string); 531 StringMap d; 532 d["AAAAAAAAA"] = "AAAAAAAAA"; 533 d["Hello World!"] = "Hello Vilag!"; 534 assert(d["AAAAAAAAA"] == "AAAAAAAAA"); 535 assert(d["Hello World!"] == "Hello Vilag!"); 536 assert(d.remove("AAAAAAAAA") == "AAAAAAAAA"); 537 assert(d.remove("a") == string.init); 538 foreach (key; d) {} 539 } 540 { 541 alias StringMap = LinkedHashMap!(string, string, defaultHash128!(string), "a == b", true, "a == b", true); 542 StringMap d; 543 d["AAAA"] = "AAAA"; 544 d["BBBB"] = "BBBB"; 545 d["CCCC"] = "CCCC"; 546 d["DDDD"] = "DDDD"; 547 assert("AAAA" == "AAAA"); 548 assert("BBBB" == "BBBB"); 549 assert("CCCC" == "CCCC"); 550 assert("DDDD" == "DDDD"); 551 writeln(d); 552 d["CCCC"] = "eeee"; 553 writeln(d); 554 foreach (key, elem; d) {} 555 foreach_reverse (key, elem; d) {} 556 foreach (key; d) {} 557 foreach_reverse (key; d) {} 558 } 559 { 560 alias StringMap = LinkedHashMap!(string, string, defaultHash128!(string), "a == b", true, "a == b", false); 561 StringMap d; 562 d["AAAA"] = "AAAA"; 563 d["BBBB"] = "BBBB"; 564 d["CCCC"] = "CCCC"; 565 d["DDDD"] = "DDDD"; 566 assert("AAAA" == "AAAA"); 567 assert("BBBB" == "BBBB"); 568 assert("CCCC" == "CCCC"); 569 assert("DDDD" == "DDDD"); 570 writeln(d); 571 d["CCCC"] = "eeee"; 572 writeln(d); 573 foreach (key, elem; d) {} 574 foreach_reverse (key, elem; d) {} 575 foreach (key; d) {} 576 foreach_reverse (key; d) {} 577 } 578 }