1 /*
2  * Kiss - A refined core library for D programming language.
3  *
4  * Copyright (C) 2015-2018  Shanghai Putao Technology Co., Ltd
5  *
6  * Developer: HuntLabs.cn
7  *
8  * Licensed under the Apache-2.0 License.
9  *
10  */
11  
12 module kiss.container.Vector;
13 
14 import core.memory;
15 import std.exception;
16 import std.experimental.allocator;
17 import std.experimental.allocator.mallocator;
18 import std.experimental.allocator.gc_allocator;
19 import kiss.util.traits;
20 import core.stdc.string : memcpy, memset;
21 import kiss.container.common;
22 import kiss.container.array;
23 
24 @trusted struct Vector(T, Allocator = Mallocator, bool addInGC = true) if(is(T == Unqual!T))
25 {
26     enum addToGC = addInGC && hasIndirections!T && !is(Unqual!Allocator == GCAllocator);
27     enum needDestroy = (is(T == struct) && hasElaborateDestructor!T);
28     enum isNotCopy = isPointer!T || isRefType!T;
29     alias Data =  ArrayCOWData!(T, Allocator,addToGC);
30     static if (hasIndirections!T)
31         alias InsertT = T;
32     else
33         alias InsertT = const T;
34 
35     static if (StaticAlloc!Allocator)
36     {
37         this(size_t size)
38         {
39             reserve(size);
40         }
41 
42         this(S)(S[] data) if(is(S : InsertT))
43         {
44             assign(data);
45         }
46     }
47     else
48     {
49         @disable this();
50         this(size_t size,Allocator alloc)
51         {
52             _alloc = alloc;
53             reserve(size);
54         }
55 
56         this(S)(S[] data,Allocator alloc)  if(is(S : InsertT))
57         {
58             _alloc = alloc;
59             assign(data);
60         }
61 
62         this(Allocator alloc)
63         {
64             _alloc = alloc;
65         }
66     }
67     mixin AllocDefine!Allocator;
68     static if(isNotCopy){
69         alias DestroyFun = void function(ref Alloc alloc,ref T) nothrow;
70         private DestroyFun _fun;
71         @property void destroyFun(DestroyFun fun){_fun = fun;}
72         @property DestroyFun destroyFun(){return _fun;}
73 
74         private void doDestroy(ref T d){
75             if(_fun)
76                 _fun(_alloc, d);
77         }
78         private void doDestroy(T[] d){
79             if(_fun) {
80                 for(size_t i  = 0; i < d.length; ++i)
81                     _fun(_alloc, d[i]);
82             }
83         }
84     } else {
85         this(this)
86         {
87             Data.inf(_data);
88             static if(hasElaborateAssign!T)
89                 doCOW(0);
90         }
91     }
92 
93     ~this()
94     {
95         Data.deInf(_alloc, _data);
96     }
97 
98     void append(S)(auto ref S value) if(is(S : InsertT) || is(S : InsertT[]))
99     {
100         this.opOpAssign!("~",S)(value);
101     }
102 
103     alias insertBack = append;
104     alias put = append;
105     alias pushBack = append;
106 
107    size_t removeBack(size_t howMany = 1) {
108         if(howMany == 0 ) 
109             return 0;
110         if (howMany >= _array.length) {
111             size_t len = _array.length;
112             clear();
113             return len;
114         }
115         auto size = _array.length - howMany;
116         doInitVaule(_array[size .. $]);
117         _array = _array[0 .. size];
118         return howMany;
119     }
120 
121     void removeSite(size_t site) 
122     in {
123         assert(site < _array.length);
124     } body{
125         doCOW(0);
126         const size_t len = _array.length - 1;
127         doInitVaule(_array[site]);
128         for (size_t i = site; i < len; ++i) {
129             memcpy(&(_array[i]), &(_array[i + 1]), T.sizeof);
130         }
131         T v = T.init;
132         memcpy(&(_array[len]), &v, T.sizeof);
133         _array = _array[0..len];
134     }
135 
136     bool removeOne(S)(auto ref S value) if(is(Unqual!S == T)) {
137         doCOW(0);
138         for (size_t i = 0; i < _array.length; ++i) {
139             if (_array[i] == value) {
140                 removeSite(i);
141                 return true;
142             }
143         }
144         return false;
145     }
146 
147     size_t removeAny(S)(auto ref S value) if(is(Unqual!S == T)) {
148         doCOW(0);
149         size_t rm = 0;
150         size_t site = 0;
151         for (size_t j = site; j < _array.length; ++j) {
152             if (_array[j] != value) {
153                 if(site != j) 
154                     memcpy(&_array[site], &_array[j], T.sizeof);
155                 site++;
156             } else {
157                 doInitVaule(_array[j]);
158                 rm++;
159             }
160         }
161         if(rm > 0) {
162             auto size = _array.length - rm;
163             auto rmed = _array[size .. $];
164             _array = _array[0..size];
165             fillWithMemcpy(rmed, T.init);
166         }
167         return rm;
168     }
169 
170     alias removeIndex = removeSite;
171 
172     void clear(){
173         if(_data !is null && _data.count > 1){
174             Data.deInf(_alloc,_data);
175             _data = null;
176         } else {
177             doInitVaule(_array);
178         }
179         _array = null;
180     }
181 
182     void opIndexAssign(S)(auto ref S value,size_t index) if(is(Unqual!S == T))
183     in{
184         assert(index < _array.length);
185     }body{
186         doCOW(0);
187         _array[index] = value;
188     }
189 
190     auto opIndex(size_t index) const
191     in{
192         assert(index < _array.length);
193     } body{
194         return _array[index];
195     }
196 
197     auto opIndex(size_t index)
198     in{
199         assert(index < _array.length);
200     } body{
201         return _array[index];
202     }
203 
204     bool opEquals(S)(S other) const 
205 		if(is(S == Unqual!(typeof(this))) || is(S : InsertT[]))
206 	{
207 		if(_array.length == other.length){
208             for(size_t i = 0; i < _array.length; ++ i) {
209                 if(_array[i] != other[i]) 
210                     return false;
211             }
212             return true;
213         } else
214             return false;
215     }
216 
217     size_t opDollar(){return _array.length;}
218 
219     void opAssign(S)(auto ref S n) if((is(S == Unqual!(typeof(this))) && !(isNotCopy)) || is(S : InsertT[]))
220     {
221         static if(is(S : InsertT[])){
222             assign(n);
223         } else {
224             if(n._data !is _data){
225                 Data.deInf(_alloc,_data);
226                 _data = n._data;
227                 Data.inf(_data);
228             }
229             _array = n._array;
230             static if(hasElaborateAssign!T)
231                 doCOW(0);
232         }
233     }
234 
235     @property bool empty() const nothrow {
236             return _array.length == 0;
237     }
238 
239     @property size_t length()const nothrow {return _array.length;}
240 
241     int opApply(scope int delegate(ref T) dg)
242     {
243         int result = 0;
244 
245         for (size_t i = 0; i < _array.length; i++)
246         {
247             result = dg(_array[i]);
248             if (result)
249                 break;
250         }
251         return result;
252     }
253 
254     int opApply(scope int delegate(size_t, ref T) dg)
255     {
256         int result = 0;
257 
258         for (size_t i = 0; i < _array.length; i++)
259         {
260             result = dg(i, _array[i]);
261             if (result) break;
262         }
263         return result;
264     }
265     static if(!isNotCopy) {
266         @property typeof(this) dup() {
267             typeof(this) ret = this;
268             if(this._data !is null)
269                 ret.doCOW(0);
270             return ret;
271         }
272         T[] idup(){
273             return _array.dup;
274         }
275     }
276 
277     immutable (T)[] opCast(C)() nothrow
278         if(is(C == immutable (T)[]))
279     {
280         return data();
281     }
282 
283     immutable (T)[] data() nothrow
284     {
285         return cast(immutable (T)[])_array;
286     }
287 
288     @property const(T) * ptr() const  nothrow{
289         return _array.ptr;
290     }
291 
292     typeof(this) opBinary(string op,S)(auto ref S other) 
293 		if((is(S == Unqual!(typeof(this))) || is(S : InsertT[])) && op == "~")
294 	{
295 		typeof(this) ret = this;
296         ret ~= other;
297         return ret;
298     }
299 
300     void opOpAssign(string op,S)(auto ref S other) 
301         if((is(S == Unqual!(typeof(this))) || is(S : InsertT[]) || is(S : InsertT)) && op == "~") 
302     {
303         static if(is(Unqual!S == T)){
304             const size_t tmpLength = 1;
305         } else {
306             if(other.length == 0) return;
307             const size_t tmpLength = other.length;
308         }
309         doCOW(tmpLength);
310         T * tptr = _data.data.ptr + _array.length;
311         static if(is(Unqual!S == T)){
312             tptr[0] = other;
313         } else {
314             memcpy(tptr, other.ptr, (tmpLength * T.sizeof));
315         }
316         tptr = _data.data.ptr;
317         size_t len = _array.length + tmpLength;
318         _array = tptr[0..len];
319     }
320     
321      void reserve(size_t elements) {
322          if(elements < _array.length)
323             removeBack(_array.length - elements);
324         else if(elements > _array.length)
325             doCOW(elements - _array.length);
326      }
327 
328 private:
329     void assign(S)(S[] input) if(is(S : InsertT))
330     {
331         clear();
332         if(input.length == 0) return;
333         auto data = buildData();
334         Data.deInf(_alloc,data);
335         _data.reserve(input.length);
336         assign(_data.data.ptr,input);
337         _array = _data.data[0..input.length];
338     }
339 
340     pragma(inline)
341     void assign(S)(T * array,S[] data)if(is(S : InsertT))
342     {
343         static if(hasElaborateAssign!T){
344             for(size_t i  = 0; i < data.length; ++i)
345                 array[i] = data[i];
346         } else {
347             memcpy(array, data.ptr, (data.length * T.sizeof));
348         }
349     }
350 
351     void doCOW(size_t tmpLength = 0)
352     {
353         auto data = buildData();
354         if(data !is null) {
355             _data.reserve(extenSize(tmpLength));
356             if(_array.length > 0){
357                 assign(_data.data.ptr, _array);
358                 _array = _data.data[0.. _array.length];
359             }
360             Data.deInf(_alloc,data);
361         } else if(tmpLength > 0 && _data.reserve(extenSize(tmpLength))) {
362                 _array = _data.data[0.. _array.length];
363         }
364     }
365 
366     Data * buildData(){
367         Data* data  = null;
368         if(_data !is null && _data.count > 1){
369             data = _data;
370             _data = null;
371         }
372         if(_data is null) {
373             _data = Data.allocate(_alloc);
374             static if(!StaticAlloc!Allocator)
375                 _data._alloc = _alloc;
376         }
377         return data;
378     }
379 
380     size_t extenSize(size_t size) {
381         size += _array.length;
382         if (size > 0)
383             size = size > 128 ? size + ((size / 3) * 2) : size * 2;
384         else
385             size = 32;
386         return size;
387     }
388 
389     void doInitVaule(ref T v)
390     {
391         static if(isNotCopy){
392             doDestroy(v);
393         } else static if(needDestroy) {
394             destroy(v);
395         }
396         T tv = T.init;
397         memcpy(&v,&tv,T.sizeof);
398     }
399 
400     void doInitVaule(T[] v){
401         for(size_t i  = 0; i < v.length; ++i)
402             doInitVaule(v[i]);
403     }
404     
405 
406 private:
407     Data* _data;
408     T[] _array;
409 }
410 
411 unittest {
412     import std.stdio;
413     import std.experimental.allocator.mallocator;
414     import std.experimental.allocator;
415 
416     Vector!(int) vec; // = Vector!int(5);
417     int[] aa = [0, 1, 2, 3, 4, 5, 6, 7];
418     vec.insertBack(aa);
419     assert(vec.length == 8);
420 
421     vec.insertBack(10);
422     assert(vec.length == 9);
423 
424     Vector!(int) vec21;
425     vec21 ~= 15;
426     vec21 ~= vec;
427     assert(vec21.length == 10);
428 
429     assert(vec21.data == [15, 0, 1, 2, 3, 4, 5, 6, 7, 10]);
430 
431     vec21[1] = 500;
432     assert(vec21.data == [15, 500, 1, 2, 3, 4, 5, 6, 7, 10]);
433 
434     vec21.removeBack();
435     assert(vec21.length == 9);
436     assert(vec21.data == [15, 500, 1, 2, 3, 4, 5, 6, 7]);
437 
438     vec21.removeBack(3);
439     assert(vec21.length == 6);
440     assert(vec21.data == [15, 500, 1, 2, 3, 4]);
441 
442     vec21.insertBack(aa);
443     assert(vec21.data == [15, 500, 1, 2, 3, 4, 0, 1, 2, 3, 4, 5, 6, 7]);
444 
445     vec21.removeSite(1);
446     assert(vec21.data == [15, 1, 2, 3, 4, 0, 1, 2, 3, 4, 5, 6, 7]);
447 
448     vec21.removeOne(1);
449     assert(vec21.data == [15, 2, 3, 4, 0, 1, 2, 3, 4, 5, 6, 7]);
450 
451     vec21.removeAny(2);
452     assert(vec21.data == [15, 3, 4, 0, 1, 3, 4, 5, 6, 7]);
453 
454     Vector!(ubyte[], Mallocator) vec2;
455     vec2.insertBack(cast(ubyte[]) "hahaha");
456     vec2.insertBack(cast(ubyte[]) "huhuhu");
457     assert(vec2.length == 2);
458     assert(cast(string) vec2[0] == "hahaha");
459     assert(cast(string) vec2[1] == "huhuhu");
460 
461     // Vector!(int, IAllocator) vec22 = Vector!(int, IAllocator)(allocatorObject(Mallocator.instance));
462     // int[] aa22 = [0, 1, 2, 3, 4, 5, 6, 7];
463     // vec22.insertBack(aa22);
464     // assert(vec22.length == 8);
465 
466     // vec22.insertBack(10);
467     // assert(vec22.length == 9);
468 
469     // vec22.insertBack(aa22);
470     // vec22.insertBack([0, 1, 2, 1, 212, 1215, 1545, 1212, 154, 51515, 1545,
471     //     1545, 1241, 51, 45, 1215, 12415, 12415, 1545, 12415, 1545, 152415,
472     //     1541515, 15415, 1545, 1545, 1545, 1545, 15454, 0, 54154]);
473 
474     // vec22 ~=  [0, 1, 2, 1, 212];
475     // immutable(int)[] dt = cast(immutable(int)[])vec22;
476     // assert(dt.length == vec22.length);
477     //Vector!(shared int) vec2;
478 }