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 } else { 186 /** 187 * Returns true if key is found. 188 */ 189 bool has(K key) @safe pure nothrow { 190 const HashType hash = hashFunc(key); 191 Node* crnt = root; 192 while (crnt) { 193 static if (keyEqual !is null) { 194 if (binaryFun!equal(crnt.hashCode, hash) && binaryFun!keyEqual(crnt.key , key)) { 195 return true; 196 } 197 } else { 198 if (binaryFun!equal(crnt.hashCode, hash)) { 199 return true; 200 } 201 } 202 crnt = crnt.next; 203 } 204 return false; 205 } 206 } 207 } else { 208 /** 209 * Removes the element with the specified key. 210 * Returns the removed element. 211 */ 212 E remove(K key) @safe pure nothrow { 213 return remove(hashFunc(key)); 214 } 215 /** 216 * Removes the element with the specified hashcode. 217 * Returns the removed element. 218 */ 219 E remove(HashType hashcode) @safe pure nothrow { 220 Node** crnt = &root; 221 while (*crnt) { 222 if (binaryFun!equal((*crnt).hashCode, hashcode)) { 223 E result = (*crnt).elem; 224 //if ((*crnt).prev) (*crnt).prev.next = (*crnt).next; 225 if ((*crnt).next is null) last = (*crnt).prev; 226 else (*crnt).next.prev = (*crnt).prev; 227 *crnt = (*crnt).next; 228 _length--; 229 return result; 230 } 231 crnt = &(*crnt).next; 232 } 233 return E.init; 234 } 235 /** 236 * opApply override for foreach 237 */ 238 int opApply(scope int delegate(ref E) dg) { 239 Node* crnt = root; 240 while (crnt) { 241 if (dg(crnt.elem)) return 1; 242 crnt = crnt.next; 243 } 244 return 0; 245 } 246 /** 247 * opApply override for foreach 248 */ 249 int opApply(scope int delegate(HashType, ref E) dg) { 250 Node* crnt = root; 251 while (crnt) { 252 if (dg(crnt.hashCode, crnt.elem)) return 1; 253 crnt = crnt.next; 254 } 255 return 0; 256 } 257 /** 258 * opApplyReverse override for foreach_reverse 259 */ 260 int opApplyReverse(scope int delegate(ref E) dg) { 261 Node* crnt = last; 262 while (crnt) { 263 if (dg(crnt.elem)) return 1; 264 crnt = crnt.prev; 265 } 266 return 0; 267 } 268 /** 269 * opApplyReverse override for foreach_reverse 270 */ 271 int opApplyReverse(scope int delegate(HashType, ref E) dg) { 272 Node* crnt = last; 273 while (crnt) { 274 if (dg(crnt.hashCode, crnt.elem)) return 1; 275 crnt = crnt.prev; 276 } 277 return 0; 278 } 279 package static string makeFunc() { 280 string makeFuncIndiv(string args) { 281 return ` 282 int opApply(scope int delegate(ref E) ` ~ args ~ ` dg) ` ~ args ~ ` { 283 Node* crnt = root; 284 while (crnt) { 285 if (dg(crnt.elem)) return 1; 286 crnt = crnt.next; 287 } 288 return 0; 289 } 290 int opApply(scope int delegate(HashType, ref E) ` ~ args ~ ` dg) ` ~ args ~ ` { 291 Node* crnt = root; 292 while (crnt) { 293 if (dg(crnt.hashCode, crnt.elem)) return 1; 294 crnt = crnt.next; 295 } 296 return 0; 297 } 298 int opApplyReverse(scope int delegate(ref E) ` ~ args ~ ` dg) ` ~ args ~ ` { 299 Node* crnt = last; 300 while (crnt) { 301 if (dg(crnt.elem)) return 1; 302 crnt = crnt.prev; 303 } 304 return 0; 305 } 306 int opApplyReverse(scope int delegate(HashType, ref E) ` ~ args ~ ` dg) ` ~ args ~ ` { 307 Node* crnt = last; 308 while (crnt) { 309 if (dg(crnt.hashCode, crnt.elem)) return 1; 310 crnt = crnt.prev; 311 } 312 return 0; 313 }`; 314 } 315 string result; 316 foreach (attr; attrList) { 317 result ~= makeFuncIndiv(attr); 318 } 319 return result; 320 } 321 mixin(makeFunc); 322 /** 323 * Assigns a value to the given key. 324 */ 325 auto opIndexAssign(E value, K key) @safe pure nothrow { 326 const HashType hashCode = hashFunc(key); 327 Node** crnt = &root; 328 while (*crnt) { 329 static if (ovrwrtBhvr) { 330 if (binaryFun!equal((*crnt).hashCode, hashCode)) return (*crnt).elem = value; 331 } else { 332 if (binaryFun!equal((*crnt).hashCode, hashCode)) { 333 if (&(*crnt).prev) (*crnt).prev.next = (*crnt).next; 334 *crnt = &(*crnt).next; 335 length--; 336 } 337 } 338 crnt = &(*crnt).next; 339 } 340 //prev.next = new Node(key, value, null); 341 *crnt = new Node(hashCode, value, null, last); 342 last = *crnt; 343 _length++; 344 return value; 345 } 346 static if (nogcIndexing) { 347 /** 348 * Returns true if key is found. 349 */ 350 bool has(K key) @nogc @safe pure nothrow { 351 return has(hashFunc(key)); 352 } 353 } else { 354 /** 355 * Returns true if key is found. 356 */ 357 bool has(K key) @safe pure { 358 return has(hashFunc(key)); 359 } 360 } 361 } 362 static if (!retainKeys || keyEqual !is null) { 363 static if (nogcIndexing) { 364 /** 365 * Returns the element with the given key, or E.init if key is not found. 366 */ 367 E opIndex(K key) @nogc @safe pure nothrow { 368 const HashType hashCode = hashFunc(key); 369 Node* crnt = root; 370 while (crnt) { 371 if (binaryFun!equal(crnt.hashCode, hashCode)) return crnt.elem; 372 crnt = crnt.next; 373 } 374 return E.init; 375 } 376 /** 377 * Returns the element with the given key, or E.init if key is not found. 378 */ 379 E opIndex(HashType hashCode) @nogc @safe pure nothrow { 380 Node* crnt = root; 381 while (crnt) { 382 if (binaryFun!equal(crnt.hashCode, hashCode)) return crnt.elem; 383 crnt = crnt.next; 384 } 385 return E.init; 386 } 387 /** 388 * Returns the pointer to the element with the given key, of null if key is not found. 389 */ 390 E* getPtr(K key) @nogc @safe pure nothrow { 391 const HashType hashCode = hashFunc(key); 392 Node* crnt = root; 393 while (crnt) { 394 if (binaryFun!equal(crnt.hashCode, hashCode)) return &(crnt.elem); 395 crnt = crnt.next; 396 } 397 return null; 398 } 399 /** 400 * Returns the pointer to the element with the given key, of null if key is not found. 401 */ 402 E* getPtr(HashType hashCode) @nogc @safe pure nothrow { 403 Node* crnt = root; 404 while (crnt) { 405 if (binaryFun!equal(crnt.hashCode, hashCode)) return &(crnt.elem); 406 crnt = crnt.next; 407 } 408 return null; 409 } 410 } else { 411 /** 412 * Returns the element with the given key, or E.init if key is not found. 413 */ 414 ref E opIndex(K key) @safe pure { 415 const HashType hashCode = hashFunc(key); 416 Node* crnt = root; 417 while (crnt) { 418 if (binaryFun!equal(crnt.hashCode, hashCode)) return crnt.elem; 419 crnt = crnt.next; 420 } 421 throw new ElementNotFoundException("Key not found!"); 422 } 423 /** 424 * Returns the element with the given key, or E.init if key is not found. 425 */ 426 ref E opIndex(HashType hashCode) @safe pure { 427 Node* crnt = root; 428 while (crnt) { 429 if (binaryFun!equal(crnt.hashCode, hashCode)) return crnt.elem; 430 crnt = crnt.next; 431 } 432 throw new ElementNotFoundException("Key not found!"); 433 } 434 } 435 } else { 436 static if (nogcIndexing) { 437 /** 438 * Returns the element with the given key, or E.init if key is not found. 439 */ 440 E opIndex(K key) @nogc @safe pure nothrow { 441 const HashType hashCode = hashFunc(key); 442 Node* crnt = root; 443 while (crnt) { 444 if (binaryFun!equal(crnt.hashCode, hashCode) && binaryFun!keyEqual(crnt.key, key)) return crnt.elem; 445 crnt = crnt.next; 446 } 447 return E.init; 448 } 449 /** 450 * Returns the pointer to the element with the given key, of null if key is not found. 451 */ 452 E* getPtr(K key) @nogc @safe pure nothrow { 453 const HashType hashCode = hashFunc(key); 454 Node* crnt = root; 455 while (crnt) { 456 if (binaryFun!equal(crnt.hashCode, hashCode) && binaryFun!keyEqual(crnt.key, key)) return &(crnt.elem); 457 crnt = crnt.next; 458 } 459 return null; 460 } 461 } else { 462 /** 463 * Returns the element with the given key, or E.init if key is not found. 464 */ 465 ref E opIndex(K key) @safe pure { 466 const HashType hashCode = hashFunc(key); 467 Node* crnt = root; 468 while (crnt) { 469 if (binaryFun!equal(crnt.hashCode, hashCode) && binaryFun!keyEqual(crnt.key, key)) return crnt.elem; 470 crnt = crnt.next; 471 } 472 throw new ElementNotFoundException("Key not found!"); 473 } 474 } 475 } 476 /** 477 * Returns true if key is found. 478 */ 479 bool has(HashType hashCode) @nogc @safe pure nothrow { 480 Node* crnt = root; 481 while (crnt) { 482 if (binaryFun!equal(crnt.hashCode, hashCode)) return true; 483 crnt = crnt.next; 484 } 485 return false; 486 } 487 /** 488 * Returns the number of elements in the LinkedMap. 489 */ 490 @property size_t length() @nogc @safe pure nothrow const { 491 return _length; 492 } 493 ///Returns the string representation of this container format 494 string toString() const { 495 if (root) return root.toString; 496 else return "empty"; 497 } 498 } 499 500 unittest { 501 import std.random : uniform; 502 import std.exception : assertThrown; 503 import std.stdio : writeln; 504 { 505 alias StringMap = LinkedHashMap!(string, string); 506 StringMap d; 507 d["AAAAAAAAA"] = "AAAAAAAAA"; 508 d["Hello World!"] = "Hello Vilag!"; 509 assert(d["AAAAAAAAA"] == "AAAAAAAAA"); 510 assert(d["Hello World!"] == "Hello Vilag!"); 511 assert(d.remove("AAAAAAAAA") == "AAAAAAAAA"); 512 assert(d.remove("a") == string.init); 513 foreach (key; d) {} 514 } 515 { 516 alias StringMap = LinkedHashMap!(string, string, defaultHash128!(string), "a == b", true, "a == b", true); 517 StringMap d; 518 d["AAAA"] = "AAAA"; 519 d["BBBB"] = "BBBB"; 520 d["CCCC"] = "CCCC"; 521 d["DDDD"] = "DDDD"; 522 assert("AAAA" == "AAAA"); 523 assert("BBBB" == "BBBB"); 524 assert("CCCC" == "CCCC"); 525 assert("DDDD" == "DDDD"); 526 writeln(d); 527 d["CCCC"] = "eeee"; 528 writeln(d); 529 foreach (key, elem; d) {} 530 foreach_reverse (key, elem; d) {} 531 foreach (key; d) {} 532 foreach_reverse (key; d) {} 533 } 534 { 535 alias StringMap = LinkedHashMap!(string, string, defaultHash128!(string), "a == b", true, "a == b", false); 536 StringMap d; 537 d["AAAA"] = "AAAA"; 538 d["BBBB"] = "BBBB"; 539 d["CCCC"] = "CCCC"; 540 d["DDDD"] = "DDDD"; 541 assert("AAAA" == "AAAA"); 542 assert("BBBB" == "BBBB"); 543 assert("CCCC" == "CCCC"); 544 assert("DDDD" == "DDDD"); 545 writeln(d); 546 d["CCCC"] = "eeee"; 547 writeln(d); 548 foreach (key, elem; d) {} 549 foreach_reverse (key, elem; d) {} 550 foreach (key; d) {} 551 foreach_reverse (key; d) {} 552 } 553 }