1 // Written in the D programming language.
2 /**
3 This module provides an implementation of DList
4 Copyright: Copyright 2019 Ernesto Castellotti <erny.castell@gmail.com>
5 License:   $(HTTP https://www.mozilla.org/en-US/MPL/2.0/, Mozilla Public License - Version 2.0).
6 Authors:   $(HTTP github.com/ErnyTech, Ernesto Castellotti)
7 */
8 module dlist.arraylist;
9 import stdx.allocator.mallocator : Mallocator;
10 import dlist.list : List;
11 import dlist.baselist : AllocatorInit, UseGC;
12 
13 enum DEFAULT_CAPACITY = 1;
14 enum BIG_ARRAY = 512;
15 enum BIG_ARRAY_INCREASE_CAPACITY = 1024;
16 /**
17  * ArrayList is a DList implementation of an array with resizable dimensions without using the GC
18  */
19 class ArrayList(T, Allocator = Mallocator) : List!T {
20     private T[] data;
21     private size_t size = 0;
22     mixin AllocatorInit!Allocator;
23     mixin UseGC!T;
24 
25     /**
26      * Initialize ArrayList with the default capacity size (10).
27      *
28      * This function cause a memory allocation.
29      */
30     this() @nogc {
31         import stdx.allocator : makeArray;
32 
33         this.data = this.allocator.makeArray!T(DEFAULT_CAPACITY);
34 
35         static if (useGC) {
36             import core.memory : GC;
37             GC.addRange(this.data.ptr, this.length * T.sizeof);
38         }
39     }
40 
41     /**
42      * Initialize ArrayList by adding the specified array to the top of the list, the capacity of the list will be equal to the length of the specified array.
43      *
44      * This function cause a memory allocation.
45      *
46      * Params:
47      *      initialArray = the array containing the initial elements
48      */
49     this(T[] initialArray) @nogc {
50         import stdx.allocator : makeArray;
51 
52         this.data = this.allocator.makeArray!T(initialArray.length);
53         this.data[0..initialArray.length] = initialArray[0..initialArray.length];
54         this.size += initialArray.length;
55 
56         static if (useGC) {
57             import core.memory : GC;
58             GC.addRange(this.data.ptr, this.length * T.sizeof);
59         }
60     }
61 
62     /**
63      * Initialize ArrayList by adding the specified DList to the top of the list, the capacity of the list will be equal to the length of the specified DList.
64      *
65      * This function cause a memory allocation.
66      *
67      * Params:
68      *      initialList = the DList containing the initial elements
69      */
70     this(List!T initialList) @nogc {
71         import stdx.allocator : makeArray;
72 
73         this.data = this.allocator.makeArray!T(initialList.length);
74         this.data[0..initialList.length] = initialList.toArray[0..initialList.length];
75         this.size += initialList.length;
76 
77         static if (useGC) {
78             import core.memory : GC;
79             GC.addRange(this.data.ptr, this.length * T.sizeof);
80         }
81     }
82 
83     /**
84      * Initialize ArrayList with the specified capacity size.
85      *
86      * This function cause a memory allocation.
87      *
88      * Params:
89      *      initialCapacity = the initial capacity of the list
90      */
91     this(size_t initialCapacity) @nogc {
92         import stdx.allocator : makeArray;
93 
94         this.data = this.allocator.makeArray!T(initialCapacity);
95 
96         static if (useGC) {
97             import core.memory : GC;
98             GC.addRange(this.data.ptr, this.length * T.sizeof);
99         }
100     }
101 
102     /**
103      * Destroys the list and disallocates all previously allocated memory.
104      */
105     ~this() @nogc {
106         clear();
107     }
108 
109     void add(T elem) @nogc {
110         ensureCapacity(this.size + 1);
111         this.data[this.size] = elem;
112         this.size += 1;
113     }
114 
115     void add(size_t index, T elem) @nogc {
116         import core.stdc..string : memcpy;
117 
118         if (index < this.size) {
119             ensureCapacity(this.size + 1);
120             memcpy(&(this.data[index + 1]), &(this.data[index]), (this.size - index) * T.sizeof);
121             this.data[index] = elem;
122             this.size += 1;
123 
124             static if (useGC) {
125                 import core.memory: GC;
126                 GC.removeRange(this.data.ptr);
127             }
128 
129         } else {
130             ensureCapacity(index + 1);
131             this.data[index] = elem;
132             this.size = index + 1;
133         }
134     }
135 
136     void addAll(T[] otherArray) @nogc {
137         ensureCapacity(this.size + otherArray.length);
138         this.data[this.size .. this.size + otherArray.length] = otherArray[0.. otherArray.length];
139         this.size += otherArray.length;
140     }
141 
142     void addAll(size_t index, T[] otherArray) @nogc {
143         foreach(i, elem; otherArray) {
144             add(index + i, elem);
145         }
146     }
147 
148     void addAll(List!T otherList) @nogc {
149         addAll(otherList.toArray);
150     }
151 
152     void addAll(size_t index, List!T otherList) @nogc {
153         addAll(index, otherList.toArray);
154     }
155 
156     size_t capacity() @nogc {
157         return this.data.length;
158     }
159 
160     void clear() @nogc {
161         import stdx.allocator : dispose;
162         import stdx.allocator.mallocator : Mallocator;
163 
164         static if (useGC) {
165             import core.memory : GC;
166             GC.removeRange(this.data.ptr);
167         }
168 
169         this.size = 0;
170         this.allocator.dispose(this.data);
171     }
172 
173     bool contains(T elem) @nogc {
174         return indexOf(elem) >= 0;
175     }
176 
177     bool containsAll(T[] otherArray) @nogc {
178         foreach(elem; otherArray) {
179             if(!contains(elem)) {
180                 return false;
181             }
182         }
183 
184         return true;
185     }
186 
187     bool containsAll(List!T otherList) @nogc {
188         return containsAll(otherList.toArray);
189     }
190 
191     T get(size_t index) @nogc {
192         return this.data[index];
193     }
194 
195     ref T getRef(size_t index) @nogc {
196         return this.data[index];
197     }
198 
199     long indexOf(T elem) @nogc {
200         foreach(i, arrayElem; this.data) {
201             if(arrayElem == elem) {
202                 return i;
203             }
204         }
205 
206         return -1;
207     }
208 
209     bool isEmpty() @nogc {
210         return this.size == 0;
211     }
212 
213     long lastIndexOf(T elem) @nogc {
214         foreach_reverse(i, arrayElem; this.data) {
215             if(arrayElem == elem) {
216                 return i;
217             }
218         }
219 
220         return -1;
221     }
222 
223     size_t length() @nogc {
224         return this.size;
225     }
226 
227     void remove(size_t index) @nogc {
228         import stdx.allocator : shrinkArray;
229         import std.algorithm.mutation : remove;
230 
231         this.data.remove(index);
232         this.size -= 1;
233         this.allocator.shrinkArray(this.data, 1);
234 
235         static if (useGC) {
236             import core.memory: GC;
237 
238             GC.removeRange(this.data.ptr);
239             GC.addRange(this.data.ptr, this.data.length * T.sizeof);
240         }
241     }
242 
243     void removeAll(T[] otherArray) @nogc {
244         foreach(elem; otherArray) {
245             auto index = indexOf(elem);
246 
247             if(index >= 0) {
248                 remove(index);
249             }
250         }
251     }
252 
253     void removeAll(List!T otherList) @nogc {
254         removeAll(otherList.toArray);
255     }
256 
257     void set(size_t index, T elem) @nogc {
258         this.data[index] = elem;
259     }
260 
261     void reserve(size_t numReserve) @nogc {
262         ensureCapacity(this.size + numReserve);
263     }
264  
265     T[] subArray(size_t indexFrom, size_t toIndex) @nogc {
266         return this.data[indexFrom .. toIndex];
267     }
268 
269     T[] toArray() @nogc {
270         return subArray(0, this.size);
271     }
272 
273     private void ensureCapacity(size_t minCapacity) @nogc {
274         import stdx.allocator : expandArray;
275 
276         if (minCapacity <= this.data.length) {
277             return;
278         }
279 
280         auto newCapacity = this.data.length > BIG_ARRAY ? this.data.length + BIG_ARRAY_INCREASE_CAPACITY : this.data.length << 1;
281 
282         if (minCapacity > newCapacity) {
283             newCapacity = minCapacity;
284         }
285 
286         auto deltaSize = newCapacity - this.data.length;
287         this.allocator.expandArray(this.data, deltaSize);
288 
289         static if (useGC) {
290             import core.memory: GC;
291 
292             GC.addRange(this.data.ptr, this.data.length * T.sizeof);
293         }
294     }
295 }