1 /**
2  * Dynamic array.
3  *
4  * This module contains a simple dynamic array implementation for use in the
5  * Naive Garbage Collector. Standard D dynamic arrays can't be used because
6  * they rely on the GC itself.
7  *
8  * See_Also:  gc module
9  * Copyright: Public Domain
10  * License:   Public Domain
11  * Authors:   Leandro Lucarella <llucax@gmail.com>
12  */
13 
14 module rt.gc.cdgc.dynarray;
15 
16 import tango.stdc.stdlib: realloc;
17 import tango.stdc.string: memmove;
18 
19 
20 private void Invariant(T)(DynArray!(T)* a)
21 {
22         assert ((a._data && a._capacity)
23                     || ((a._data is null) && (a._capacity == 0)));
24         assert (a._capacity >= a._size);
25 }
26 
27 
28 package:
29 
30 /**
31  * Dynamic array.
32  *
33  * This is a simple dynamic array implementation. D dynamic arrays can't be
34  * used because they rely on the GC, and we are implementing the GC.
35  */
36 struct DynArray(T)
37 {
38 
39 private:
40 
41     /// Memory block to hold the array.
42     T* _data = null;
43 
44     /// Total array capacity, in number of elements.
45     size_t _capacity = 0;
46 
47     /// Current array size, in number of elements.
48     size_t _size = 0;
49 
50 
51 public:
52 
53     invariant()
54     {
55         .Invariant(this);
56     }
57 
58     /**
59      * Check the structure invariant.
60      */
61     void Invariant()
62     {
63         .Invariant(this);
64     }
65 
66     /**
67      * Get the array size.
68      */
69     size_t length()
70     {
71         return this._size;
72     }
73 
74     /**
75      * Get the array capacity.
76      */
77     size_t capacity()
78     {
79         return this._capacity;
80     }
81 
82     /**
83      * Get the total ammount of bytes the elements consumes (capacity included).
84      */
85     size_t elements_sizeof()
86     {
87         return this._capacity * (T.sizeof + (T*).sizeof);
88     }
89 
90     /**
91      * Get the pointer to the array's data.
92      *
93      * Use with care, the data belongs to the array and you should not
94      * realloc() or free() it, you should only use it a as a read-only chunk of
95      * memory.
96      */
97     T* ptr()
98     {
99         return this._data;
100     }
101 
102     /**
103      * Access an element by index.
104      *
105      * Bear in mind that a copy of the element is returned. If you need
106      * a pointer, you can always get it through a.ptr + i (but be careful if
107      * you use the insert_sorted() method).
108      */
109     T opIndex(size_t i)
110     {
111         assert (i < this._size);
112         return this._data[i];
113     }
114 
115     /**
116      * Append a copy of the element x at the end of the array.
117      *
118      * This can trigger an allocation if the array is not big enough.
119      *
120      * Returns a pointer to the newly appended element if the append was
121      * successful, null otherwise (i.e. an allocation was triggered but the
122      * allocation failed) in which case the internal state is not changed.
123      */
124     T* append(in T x)
125     {
126         if (this._size == this._capacity)
127             if (!this.resize())
128                 return null;
129         this._data[this._size] = x;
130         this._size++;
131         return this._data + this._size - 1;
132     }
133 
134     /**
135      * Insert an element preserving the array sorted.
136      *
137      * This assumes the array was previously sorted. The "cmp" template
138      * argument can be used to specify a custom comparison expression as "a"
139      * string (where a is the element in the array and "b" is the element
140      * passed as a parameter "x"). Using a template to specify the comparison
141      * method is a hack to cope with compilers that have trouble inlining
142      * a delegate (i.e. DMD).
143      *
144      * This can trigger an allocation if the array is not big enough and moves
145      * memory around, so you have to be specially careful if you are taking
146      * pointers into the array's data.
147      *
148      * Returns a pointer to the newly inserted element if the append was
149      * successful, null otherwise (i.e. an allocation was triggered but the
150      * allocation failed) in which case the internal state is not changed.
151      */
152     T* insert_sorted(char[] cmp = "a < b")(in T x)
153     {
154         size_t i = 0;
155         for (; i < this._size; i++) {
156             T a = this._data[i];
157             alias x b;
158             if (mixin(cmp))
159                 continue;
160             break;
161         }
162         if ((this._size == this._capacity) && !this.resize())
163             return null;
164         memmove(this._data + i + 1, this._data + i,
165                 (this._size - i) * T.sizeof);
166         this._data[i] = x;
167         this._size++;
168         return this._data + i;
169     }
170 
171     /**
172      * Remove the element at position pos.
173      */
174     void remove_at(size_t pos)
175     {
176         this._size--;
177         // move the rest of the items one place to the front
178         memmove(this._data + pos, this._data + pos + 1,
179                 (this._size - pos) * T.sizeof);
180     }
181 
182     /**
183      * Remove the first occurrence of the element x from the array.
184      */
185     bool remove(in T x)
186     {
187         for (size_t i = 0; i < this._size; i++) {
188             if (this._data[i] == x) {
189                 this.remove_at(i);
190                 return true;
191             }
192         }
193         return false;
194     }
195 
196     /**
197      * Change the current capacity of the array to new_capacity.
198      *
199      * This can enlarge or shrink the array, depending on the current capacity.
200      * If new_capacity is 0, the array is enlarged to hold double the current
201      * size. If new_capacity is less than the current size, the current size is
202      * truncated, and the (size - new_capacity) elements at the end are lost.
203      *
204      * Returns true if the resize was successful, false otherwise (and the
205      * internal state is not changed).
206      */
207     bool resize(in size_t new_capacity=0)
208     {
209         // adjust new_capacity if necessary
210         if (new_capacity == 0)
211             new_capacity = this._size * 2;
212             if (new_capacity == 0)
213                 new_capacity = 16;
214         // reallocate the memory with the new_capacity
215         T* new_data = cast(T*) realloc(this._data, new_capacity * T.sizeof);
216         if (new_data is null)
217             return false;
218         this._data = new_data;
219         this._capacity = new_capacity;
220         // truncate the size if necessary
221         if (this._size > this._capacity)
222             this._size = this._capacity;
223         return true;
224     }
225 
226     /**
227      * Remove all the elements of the array and set the capacity to 0.
228      */
229     void free()
230     {
231         this._data = cast(T*) realloc(this._data, 0);
232         assert (this._data is null);
233         this._size = 0;
234         this._capacity = 0;
235     }
236 
237 }
238 
239 
240 unittest // DynArray
241 {
242     DynArray!(int) array;
243     assert (array.length == 0);
244     assert (array.capacity == 0);
245     assert (array.ptr is null);
246     assert (array.append(5));
247     assert (array.length == 1);
248     assert (array.capacity >= 1);
249     assert (array.ptr !is null);
250     for (auto i = 0; i < array.length; i++)
251         assert (*array[i] == 5);
252     assert (array.append(6));
253     assert (array.length == 2);
254     assert (array.capacity >= 2);
255     assert (array.ptr !is null);
256     int j = 0;
257     while (j < array.length)
258         assert (*array[j] == (5 + j++));
259     assert (j == 2);
260     array.remove(5);
261     assert (array.length == 1);
262     assert (array.capacity >= 1);
263     assert (array.ptr !is null);
264     for (auto i = 0; i < array.length; i++)
265         assert (*array[i] == 6);
266     assert (array.resize(100));
267     assert (array.length == 1);
268     assert (array.capacity >= 100);
269     assert (array.ptr !is null);
270     array.free();
271     assert (array.length == 0);
272     assert (array.capacity == 0);
273     assert (array.ptr is null);
274 }
275 
276 
277 // vim: set et sw=4 sts=4 :