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