1 module evael.lib.containers.array;
2 
3 import std.experimental.allocator : makeArray, expandArray, shrinkArray;
4 
5 public import evael.lib.memory;
6 
7 struct Array(T)
8 {
9     private T[] m_array;
10 
11     private size_t m_capacity;
12     private size_t m_length;
13 
14     /**
15      * Array constructor.
16      * Creates an array.
17      * Params:
18      * 		capacity : capacity of the arrray
19      */
20     @nogc
21     public this(in size_t capacity)
22     {
23         this.m_array = cast(T[]) defaultAllocator.allocate(T.sizeof * capacity);
24         this.m_capacity = capacity;
25     }
26 
27     /**
28      * Array constructor.
29      * Creates an array with a specific default value.
30      * Params:
31      * 		capacity : capacity of the array
32      *		defaultValue : default value
33      */
34     @nogc
35     public this(in size_t capacity, T defaultValue)
36     {
37         this.m_array = cast(T[]) defaultAllocator.allocate(T.sizeof * capacity);
38         this.m_array[] = defaultValue;
39         this.m_capacity = capacity;
40         this.m_length = capacity;
41     }		
42 
43     @nogc
44     public void dispose()
45     {
46         if (this.m_array !is null)
47         {
48             defaultAllocator.deallocate(this.m_array);
49             this.m_array = null;
50         }
51     }
52 
53     /**
54      * Inserts element at the end of array.
55      * Params:
56      *		element : element to insert
57      */
58     @nogc
59     public void insert(T element)
60     {
61         if (this.m_array is null)
62         {
63             this.m_array = cast(T[])defaultAllocator.allocate(T.sizeof * 32);
64             this.m_capacity = 32;
65         }
66         else if (this.m_length >= this.m_array.length)
67         {
68             immutable delta = this.m_array.length > 512 ? 1024 : this.m_array.length << 1;
69             
70             if (!defaultAllocator.expandArray(this.m_array, delta))
71             {
72                 this.m_capacity += delta;
73                 assert(0);
74             }
75         }
76 
77         this.m_array[this.m_length++] = element;
78     }
79 
80     /**
81      * Removes the item at the given index from the array.
82      * Params:
83      *		i : indexs
84      */
85     @nogc
86     public void removeAt(in size_t i)
87     in (i < this.m_length)
88     {
89         static if (is(T == class) || is(T == interface))
90         {
91             MemoryHelper.dispose(this.m_array[i]);
92         }
93 
94         auto next = i + 1;
95 
96         while (next < this.m_length)
97         {
98             this.m_array[next - 1] = this.m_array[next];
99             ++next;
100         }
101 
102         this.m_length -= 1;
103     }
104 
105     /**
106      * Removes the last element from the array.
107      */
108     pragma(inline, true)
109     @nogc
110     public void removeBack()
111     {
112         this.removeAt(this.m_length - 1);
113     }
114 
115     /**
116      * Index operator overload.
117      */
118     pragma(inline, true)
119     @nogc
120     public ref T opIndex(size_t i)
121     in (i < this.m_length)
122     {
123         return this.m_array[i];
124     }
125     
126     /**
127      * Index assignment support.
128      */
129     pragma(inline, true)
130     @nogc
131     public void opIndexAssign(T value)
132     {
133         opSliceAssign(value);
134     }
135 
136     /**
137      * Index assignment support.
138      */
139     pragma(inline, true)
140     @nogc
141     public void opIndexAssign(T value, size_t i)
142     {
143         opIndex(i) = value;
144     }
145 
146     /**
147      * Slice assignment support.
148      */
149     pragma(inline, true)
150     @nogc
151     public void opSliceAssign(T value)
152     {
153         this.m_array[0 .. this.m_length] = value;
154     }
155 
156     pragma(inline, true)
157     @nogc
158     public void opSliceAssign(T[] values)
159     {
160         this.m_array[] = values[];
161     }
162 
163     pragma(inline, true)
164     @nogc
165     public void opSliceAssign(T value, size_t i, size_t j)
166     {
167         this.m_array[i .. j] = value;
168     }
169 
170     /**
171      * Slice operator overload.
172      */
173     pragma(inline, true)
174     @nogc
175     public T[] opSlice()
176     {
177         return opSlice(0, this.m_length);
178     }
179 
180     pragma(inline, true)
181     @nogc
182     public T[] opSlice(size_t a, size_t b)
183     {
184         return this.m_array[a .. b];
185     }
186 
187     /**
188      * @nogc foreach support.
189      */
190     @nogc
191     public int opApply(scope int delegate(size_t i, ref T) @nogc operation)
192     {
193         return opApplyIndexImpl(operation);
194     }
195 
196     @nogc
197     public int opApply(scope int delegate(ref T) @nogc operation)
198     {
199         return opApplyImpl(operation);
200     }
201 
202     /**
203      * foreach support.
204      */
205     public int opApply(scope int delegate(size_t i, ref T) operation)
206     {
207         return opApplyIndexImpl(operation);
208     }
209 
210     public int opApply(scope int delegate(ref T) operation)
211     {
212         return opApplyImpl(operation);
213     }
214 
215     public int opApplyIndexImpl(O)(O operation)
216     {
217         int result;
218 
219         foreach (i, ref v; this.m_array[0..this.m_length])
220         {
221             result = operation(i, v);
222 
223             if (result)
224             {
225                 break;
226             }
227         }
228 
229         return result;
230     }
231 
232     public int opApplyImpl(O)(O operation)
233     {
234         int result;
235 
236         foreach (ref v; this.m_array[0..this.m_length])
237         {
238             result = operation(v);
239 
240             if (result)
241             {
242                 break;
243             }
244         }
245 
246         return result;
247     }
248 
249     @nogc
250     @property
251     {
252         public T[] data()
253         {
254             return this.m_array;
255         }
256 
257         public size_t length()
258         {
259             return this.m_length;
260         }
261 
262         public void length(in size_t value)
263         in (value <= capacity)
264         {
265             this.m_length = value;
266         }
267         
268         public size_t capacity()
269         {
270             return this.m_capacity;
271         }
272 
273         public auto ref T front()
274         {
275             return this.m_array[0];
276         }
277 
278         public auto ref T back()
279         {
280             return this.m_array[this.m_length - 1];
281         }
282 
283         public bool empty()
284         {
285             return this.m_length == 0;
286         }
287     }
288 }