1 /** 2 Implementation of associative container that supports unique keys `FlatMap` (similar to c++ `boost::flat_map`). 3 4 License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). 5 Authors: Adam Búš 6 */ 7 module btl.vector.flat_map; 8 9 import btl.traits.common; 10 11 import btl.internal.traits; 12 import btl.internal.allocator; 13 14 import btl.vector; 15 16 17 /** 18 Type used in forward constructors. 19 */ 20 alias Forward = btl.traits.common.Forward; 21 22 23 /** 24 Default allcoator for `Vector`. 25 */ 26 public alias DefaultAllocator = Mallocator; 27 28 29 /** 30 True if `T` is a `Vector` or implicitly converts to one, otherwise false. 31 */ 32 template isFlatMap(T...) 33 if(T.length == 1){ 34 import std.traits : Unqual; 35 enum bool isFlatMap = is(Unqual!(T[0]) == FlatMap!Args, Args...); 36 } 37 38 39 /** 40 */ 41 template FlatMap( 42 _Key, 43 _Value, 44 size_t N = 0, 45 _Allocator = DefaultAllocator, 46 bool _supportGC = (shouldAddGCRange!_Key || shouldAddGCRange!_Value) 47 ){ 48 import core.lifetime : forward, move; 49 import std.traits : CopyConstness, isMutable; 50 import std.meta : AliasSeq; 51 52 53 alias Pair = .Pair!(_Key, _Value); 54 alias Storage = Vector!(Pair, N, _Allocator, _supportGC); 55 56 static _Key makeKey(K)(auto ref K key){ 57 static if(is(immutable K : immutable _Key)){ 58 return forward!key; 59 } 60 else{ 61 return _Key(forward!key); 62 } 63 } 64 65 struct FlatMap{ 66 /** 67 Type of key. 68 */ 69 public alias KeyType = _Key; 70 71 72 73 /** 74 Type of value. 75 */ 76 public alias ValueType = _Value; 77 78 79 80 /** 81 Type of internal vector. 82 */ 83 public alias VectorType = Storage; 84 85 86 87 /** 88 True if allocator doesn't have state. 89 */ 90 public alias hasStatelessAllocator = Storage.hasStatelessAllocator; 91 92 93 94 /** 95 Type of elements (pair of key and value). 96 */ 97 public alias ElementType = Storage.ElementType; 98 99 100 101 /** 102 Type of reference to elements. 103 */ 104 public alias ElementReferenceType = Storage.ElementReferenceType; 105 106 107 108 /** 109 Type of the allocator object used to define the storage allocation model. By default `DefaultAllocator` is used. 110 */ 111 public alias AllocatorType = Storage.AllocatorType; 112 113 114 115 /** 116 */ 117 public alias supportGC = Storage.supportGC; 118 119 120 121 /** 122 Allow heap (`false` only if `Allcoator` is void) 123 */ 124 public alias allowHeap = Storage.allowHeap; 125 126 127 128 /** 129 Maximal capacity of container, in terms of number of elements. 130 */ 131 public alias maximalCapacity = Storage.maximalCapacity; 132 133 134 135 /** 136 Minimal capacity of container, in terms of number of elements. 137 */ 138 public alias minimalCapacity = Storage.minimalCapacity; 139 140 141 142 /** 143 Returns copy of allocator. 144 */ 145 public @property auto allocator(this This)()scope{ 146 return this.storage.allcoator(); 147 } 148 149 150 151 /** 152 Returns whether the falt map is empty (i.e. whether its length is 0). 153 154 More: `btl.vector.Vector.empty` 155 */ 156 public @property bool empty()scope const pure nothrow @safe @nogc{ 157 return this.storage.empty; 158 } 159 160 161 162 /** 163 Returns the length of the flat map, in terms of number of elements. 164 165 More: `btl.vector.Vector.length` 166 */ 167 public @property size_t length()scope const pure nothrow @safe @nogc{ 168 return this.storage.length; 169 } 170 171 172 173 /** 174 Returns the size of the storage space currently allocated for the container. 175 176 More: `btl.vector.Vector.capacity` 177 */ 178 public @property size_t capacity()const scope pure nothrow @trusted @nogc{ 179 return this.storage.capacity; 180 } 181 182 183 184 /** 185 Constructs a `FlatMap` object from other flat map. 186 187 Parameters: 188 `rhs` other vector of `FlatMap` type. 189 190 `allocator` optional allocator parameter. 191 192 Examples: 193 -------------------- 194 TODO 195 -------------------- 196 */ 197 public this(Rhs, this This)(scope auto ref Rhs rhs)scope 198 if( isFlatMap!Rhs 199 && isConstructable!(Rhs, This) 200 && (isRef!Rhs || !is(immutable This == immutable Rhs)) 201 ){ 202 static if(isRef!rhs || !isMutable!Rhs) 203 this.storage = Storage(rhs.storage, wf); 204 else 205 this.storage = Storage(move(rhs.storage), wf); 206 } 207 208 209 210 /** 211 Forward constructor. 212 */ 213 public this(Rhs, this This)(scope auto ref Rhs rhs, Forward wf)scope 214 if(isFlatMap!Rhs && isConstructable!(Rhs, This)){ 215 static if(isRef!rhs || !isMutable!Rhs) 216 this.storage = Storage(rhs.storage, wf); 217 else 218 this.storage = Storage(move(rhs.storage), wf); 219 } 220 221 222 223 /** 224 Copy constructors 225 */ 226 static foreach(alias From; AliasSeq!( 227 typeof(this), 228 const typeof(this), 229 immutable typeof(this), 230 )){ 231 232 static if(hasCopyConstructor!(From, typeof(this))) 233 this(scope ref From rhs)scope{this(rhs, Forward.init);} 234 else 235 @disable this(scope ref From rhs)scope pure nothrow @safe; 236 237 static if(hasCopyConstructor!(From, const typeof(this))) 238 this(scope ref From rhs)const scope{this(rhs, Forward.init);} 239 else 240 @disable this(scope ref From rhs)const scope pure nothrow @safe; 241 242 static if(hasCopyConstructor!(From, immutable typeof(this))) 243 this(scope ref From rhs)immutable scope{this(rhs, Forward.init);} 244 else 245 @disable this(scope ref From rhs)immutable scope pure nothrow @safe; 246 247 static if(hasCopyConstructor!(From, shared typeof(this))) 248 this(scope ref From rhs)shared scope{this(rhs, Forward.init);} 249 else 250 @disable this(scope ref From rhs)shared scope pure nothrow @safe; 251 252 static if(hasCopyConstructor!(From, const shared typeof(this))) 253 this(scope ref From rhs)const shared scope{this(rhs, Forward.init);} 254 else 255 @disable this(scope ref From rhs)const shared scope pure nothrow @safe; 256 } 257 258 259 260 /** 261 Assigns a new value `rhs` to the flat map, replacing its current contents. 262 263 Examples: 264 -------------------- 265 TODO 266 -------------------- 267 */ 268 public void opAssign(Rhs)(scope auto ref Rhs rhs)scope 269 if(isFlatMap!Rhs && isAssignable!(Rhs, typeof(this)) ){ 270 static if(isRef!rhs || !isMutable!Rhs) 271 this.storage.opAssign(rhs.storage); 272 else 273 this.storage.opAssign(move(rhs.storage)); 274 } 275 276 277 278 /** 279 Returns pointer elements value with specified `key` or null if container doesn't contains `key`. 280 281 Examples: 282 -------------------- 283 TODO 284 -------------------- 285 */ 286 public CopyConstness!(This, ValueType)* at(K, this This)(scope auto ref K key)return scope pure nothrow @system @nogc 287 out(ret){ 288 scope const(ValueType)* tmp = null; 289 290 foreach(ref pair; this.storage[]){ 291 if(pair.key == key){ 292 tmp = &pair.value; 293 break; 294 } 295 } 296 297 assert(ret is tmp); 298 299 } 300 do{ 301 302 auto slice = (()@trusted => this.storage.opIndex() )(); 303 size_t begin = 0; 304 size_t end = slice.length; 305 306 while(begin < end){ 307 const diff = (end - begin) / 2; 308 const pos = (begin + diff); 309 310 if(slice[pos].key == key){ 311 return &slice[pos].value; 312 } 313 314 if(slice[pos].key > key) 315 end = pos; 316 else 317 begin = (pos + 1); 318 } 319 320 return null; 321 } 322 323 324 325 /** 326 Returns copy of element value with specified `key` or ValueType.init if container doesn't contains `key`. 327 328 Examples: 329 -------------------- 330 TODO 331 -------------------- 332 */ 333 public CopyConstness!(This, ValueType) atCopy(K, this This)(scope auto ref K key){ 334 if(auto val = this.at(forward!key)) 335 return *val; 336 337 return typeof(return).init; 338 } 339 340 341 342 /** 343 Returns reference of element value with specified `key`. 344 345 If container doesn't contains value with key `key` then ValueType.init is created for it. 346 347 Examples: 348 -------------------- 349 TODO 350 -------------------- 351 */ 352 public ref ValueType opIndex(K)(scope auto ref K key)return pure nothrow @system @nogc{ 353 354 auto slice = (()@trusted => this.storage.opIndex() )(); 355 size_t begin = 0; 356 size_t end = slice.length; 357 358 while(begin < end){ 359 const diff = (end - begin) / 2; 360 const pos = (begin + diff); 361 362 if(slice[pos].key == key){ 363 return slice[pos].value; 364 } 365 366 if(slice[pos].key > key) 367 end = pos; 368 else 369 begin = (pos + 1); 370 } 371 372 const pos = this.storage.insert( 373 begin, 374 Pair( 375 makeKey(forward!key), 376 ValueType.init, 377 ) 378 ); 379 380 return this.storage[pos].value; 381 } 382 383 384 385 /** 386 */ 387 public void opIndexAssign(V : ValueType, K)(auto ref V value, auto ref K key){ 388 auto slice = (()@trusted => this.storage.opIndex() )(); 389 size_t begin = 0; 390 size_t end = slice.length; 391 392 while(begin < end){ 393 const diff = (end - begin) / 2; 394 const pos = (begin + diff); 395 396 if(slice[pos].key == key){ 397 slice[pos].value = forward!value; 398 return; 399 } 400 401 if(slice[pos].key > key) 402 end = pos; 403 else 404 begin = (pos + 1); 405 } 406 407 this.storage.insert( 408 begin, 409 Pair( 410 makeKey(forward!key), 411 forward!value, 412 ) 413 ); 414 } 415 416 417 418 /** 419 */ 420 public CopyConstness!(This, .Pair!(const KeyType, ValueType))[] opIndex(this This)()return scope @system{ 421 return cast(typeof(return))this.storage[]; 422 } 423 424 425 426 /** 427 Returns the length of the container, in terms of number of elements. 428 429 Same as `length()`. 430 */ 431 public size_t opDollar()const scope pure nothrow @safe @nogc{ 432 return this.length; 433 } 434 435 436 437 /** 438 Operator `in` 439 440 441 Examples: 442 -------------------- 443 TODO 444 -------------------- 445 */ 446 public bool opBinaryRight(string op : "in", K)(scope auto ref K key)scope const pure nothrow @trusted @nogc{ 447 return this.at(forward!key) !is null; 448 } 449 450 451 452 /** 453 Compares the contents of a container with another container, range or null. 454 455 Returns `true` if they are equal, `false` otherwise 456 457 Examples: 458 -------------------- 459 TODO 460 -------------------- 461 */ 462 public bool opEquals(R)(scope R rhs)const scope nothrow 463 if(isBtlInputRange!R){ 464 return this.storage.opEquals(forward!rhs); 465 } 466 467 /// ditto 468 public bool opEquals(V)(scope const auto ref V rhs)const scope nothrow 469 if(isVector!V){ 470 return this.storage.opEquals(rhs); 471 } 472 473 /// ditto 474 public bool opEquals(FM)(scope const auto ref FM rhs)const scope nothrow 475 if(isFlatMap!FM){ 476 return this.storage.opEquals(rhs.storage); 477 } 478 479 480 481 /** 482 Compares the contents of a vector with another vector or range. 483 484 Examples: 485 -------------------- 486 auto a1 = Vector!(int, 6).build(1, 2, 3); 487 auto a2 = Vector!(int, 6).build(1, 2, 3, 4); 488 auto b = Vector!(int, 6).build(3, 2, 1); 489 490 assert(a1 < b); 491 assert(a1 < a2); 492 assert(a2 < b); 493 assert(a1 <= a1); 494 -------------------- 495 */ 496 public int opCmp(R)(scope R rhs)const scope nothrow 497 if(isBtlInputRange!R){ 498 return this.storage.opCmp(forward!rhs); 499 } 500 501 /// ditto 502 public int opCmp(V)(scope const auto ref V rhs)const scope nothrow 503 if(isVector!V){ 504 return this.storage.opCmp(rhs); 505 } 506 507 /// ditto 508 public int opCmp(FM)(scope const auto ref FM rhs)const scope nothrow 509 if(isFlatMap!FM){ 510 return this.storage.opCmp(rhs.storage); 511 } 512 513 514 515 /** 516 Swaps the contents of `this` and `rhs`. 517 518 More: `btl.vector.Vector.proxySwap` 519 */ 520 public void proxySwap()(ref scope typeof(this) rhs)scope{ 521 this.storage.proxySwap(rhs.storage); 522 } 523 524 525 526 /** 527 Swaps the contents of `this` and `rhs` only if both are heap allocated. 528 529 Return `true` if swap successed or `false` if not (`this` or `rhs` is non heap allocated). 530 531 More: `btl.vector.Vector.heapSwap` 532 */ 533 public bool heapSwap()(ref scope typeof(this) rhs)scope{ 534 return this.storage.heapSwap(rhs.storage); 535 } 536 537 538 539 /** 540 Erases the contents of the container, which becomes an empty (with a length of 0 elements). 541 542 Doesn't change capacity of container. 543 544 More: `btl.vector.Vector.clear` 545 */ 546 public void clear()()scope nothrow{ 547 this.storage.clear(); 548 } 549 550 551 552 /** 553 Erases and deallocate the contents of the container, which becomes an empty (with a length of 0 elements). 554 555 More: `btl.vector.Vector.release` 556 */ 557 public void release()()scope nothrow{ 558 this.storage.release(); 559 } 560 561 562 563 /** 564 Requests that the container capacity be adapted to a planned change in size to a length of up to `n` elements. 565 566 More: `btl.vector.Vector.reserve` 567 */ 568 public void reserve()(const size_t n)scope nothrow{ 569 this.storage.reserve(n); 570 } 571 572 573 574 /** 575 Requests the `Vector` to reduce its capacity to fit its length. 576 577 More: `btl.vector.Vector.shrinkToFit` 578 */ 579 public void shrinkToFit(const bool reallocate = true)scope{ 580 this.storage.shrinkToFit(reallocate); 581 } 582 583 584 585 /** 586 */ 587 public bool remove(scope const ValueType* val)@trusted{ 588 if(val is null) 589 return false; 590 591 scope const Pair* pair = cast(const Pair*)((cast(const void*)val) - Pair.value.offsetof); 592 593 if(!this.storage.ownsElement(pair)) 594 return false; 595 596 this.storage.erase(pair[0 .. 1]); 597 return true; 598 } 599 600 601 602 /** 603 */ 604 public bool remove(K)(scope auto ref K key)scope{ 605 return this.remove((()@trusted => this.at(forward!key) )()); 606 } 607 608 609 610 /** 611 */ 612 public void remove(size_t index)scope{ 613 this.storage.erase(index, 1); 614 } 615 616 617 618 private Storage storage; 619 } 620 621 622 } 623 624 625 private struct Pair(_Key, _Value){ 626 public alias KeyType = _Key; 627 public alias ValueType = _Value; 628 629 public KeyType key; 630 public ValueType value; 631 632 this(ref typeof(this) rhs)scope{ 633 this.key = rhs.key; 634 this.value = rhs.value; 635 } 636 637 void opAssign(Rhs : typeof(this))(auto ref Rhs rhs){ 638 this.key = rhs.key; 639 this.value = rhs.value; 640 } 641 } 642 643 //local traits: 644 package{ 645 646 enum bool safeAllcoate(A) = __traits(compiles, (ref A allcoator)@safe{ 647 const size_t size; 648 allcoator.allocate(size); 649 }(*cast(A*)null)); 650 651 652 //copy ctor: 653 template hasCopyConstructor(From, To) 654 if(is(immutable From == immutable To)){ 655 enum bool hasCopyConstructor = btl.vector.hasCopyConstructor!( 656 GetVectorType!From, 657 GetVectorType!To 658 ); 659 660 } 661 662 //Constructable: 663 template isConstructable(From, To){ 664 enum isConstructable = btl.vector.isConstructable!( 665 GetVectorType!From, 666 GetVectorType!To 667 ); 668 } 669 670 //Assignable: 671 template isAssignable(From, To){ 672 enum isAssignable = btl.vector.isAssignable!( 673 GetVectorType!From, 674 GetVectorType!To 675 ); 676 } 677 678 679 template GetVectorType(FM){ 680 import std.traits : CopyTypeQualifiers; 681 alias GetVectorType = CopyTypeQualifiers!(FM, FM.VectorType); 682 } 683 684 685 686 } 687 688 689