1 /// Author: Aziz Köksal
2 /// License: GPL3
3 /// $(Maturity low)
4 module dil.Array;
5 
6 import core.stdc.stdlib,
7        core.memory,
8        core.exception;
9 import std.traits : hasIndirections;
10 import common;
11 
12 /// The system size of a memory page. Default to 4k.
13 static size_t PAGESIZE = 4096;
14 
15 extern (C) void* memcpy(void*, const void*, size_t);
16 extern (C) void onOutOfMemoryError();
17 
18 static this()
19 {
20   version(linux)
21   {
22     import core.sys.posix.unistd;
23     PAGESIZE = cast(size_t)sysconf(_SC_PAGE_SIZE);
24   }
25 }
26 
27 /// An array implementation that uses D's Garbage Collector functions.
28 struct DArray(E)
29 {
30   E* ptr;
31   E* cur;
32   E* end;
33 
34   /// A block needs to be scanned by the GC, if E as pointers.
35   enum scanIfPtrs = hasIndirections!E ? 0 : GC.BlkAttr.NO_SCAN;
36 
37   /// Constructs a DArray and reserves space for n elements.
38   this(size_t n = 0)
39   {
40     if (n)
41       if (auto p = cast(E*)GC.malloc(n * E.sizeof, scanIfPtrs))
42         end = (ptr = cur = p) + n;
43   }
44 
45   invariant()
46   {
47     if (!ptr)
48       assert(cur is null && end is null, "!(ptr == cur == end == 0)");
49     else
50       assert(ptr <= cur && cur <= end,
51         Format("!({} <= {} <= {})", ptr, cur, end));
52   }
53 
54   /// Returns the size of the array.
55   size_t len() @property const
56   {
57     return cur - ptr;
58   }
59 
60   /// Sets the size of the array.
61   /// Resizes space if necessary. Does not deallocate if n is zero.
62   void len(size_t n) @property
63   {
64     if ((ptr + n) > end)
65       reserve(n);
66     cur = ptr + n;
67   }
68 
69   /// Returns the remaining space before a reallocation is needed.
70   size_t rem() @property const
71   {
72     return end - cur;
73   }
74 
75   /// Sets the capacity so that space for n elements remain.
76   void rem(size_t n) @property
77   {
78     cap = len + n;
79   }
80 
81   /// Returns the total capacity of the array.
82   size_t cap() @property const
83   {
84     return end - ptr;
85   }
86 
87   /// Sets the capacity to exactly n elements.
88   void cap(size_t n) @property
89   {
90     reserve(n);
91   }
92 
93   /// Reserves memory for at least n elements.
94   void reserve(size_t n)
95   {
96     auto len = this.len;
97     len = (len < n ? len : n); // min(len, n)
98     auto addBytes = (n <= cap ? 0 : n - cap) * E.sizeof;
99     if (!ptr || GC.extend(ptr, addBytes, addBytes) == 0)
100     { // Couldn't extend. Allocate new memory.
101       auto newPtr = cast(E*)GC.malloc(n * E.sizeof, scanIfPtrs);
102       if (len) // Copy to the new block if there are elements.
103         newPtr[0..len] = ptr[0..len];
104       ptr = newPtr;
105     }
106     cur = ptr + len;
107     end = ptr + n;
108   }
109 
110   /// Calls the GC to free the memory and clears this DArray.
111   void free()
112   {
113     GC.free(ptr);
114     ptr = cur = end = null;
115   }
116 
117   /// Appends x to the array.
118   /// Appends the elements if X itself is an array.
119   void opOpAssign(string op : "~", X)(const X x)
120   {
121     static if (is(typeof(cur[0] == x[0])))
122     {
123       auto n = x.length;
124       ensureOrGrow(n);
125       cur[0..n] = x;
126       cur += n;
127     }
128     else
129     {
130       ensureOrGrow(1);
131       *cur++ = x;
132     }
133     assert(cur <= end);
134   }
135 
136   /// Appends several items to the array.
137   void put(Xs...)(Xs xs)
138   {
139     foreach (x; xs)
140       this ~= x;
141   }
142 
143   /// Appends several items to the array,
144   /// without checking for sufficient space.
145   void putUnsafe(Xs...)(Xs xs)
146   {
147     foreach (x; xs)
148       static if (is(typeof(cur[0] == x[0])))
149       {
150         auto n = x.length;
151         cur[0..n] = x;
152         cur += n;
153       }
154       else
155         *cur++ = x;
156     assert(cur <= end);
157   }
158 
159   /// Sets: cap = next_pow_of_2(cap)
160   void growP2()
161   {
162     import core.bitop : bsr;
163     auto x = cap;
164     if (x & (x - 1))
165     { // Round up to the next power of 2.
166       x = 2 << bsr(x);
167       if (x == 0) // Did it overflow?
168         x = size_t.max / 2 + 1; // Set the highest bit.
169     }
170     cap = x;
171   }
172 
173   /// Ensure space for 'ne' number of elements or grow by 1.5.
174   void ensureOrGrow(size_t ne)
175   {
176     auto mincap = len + ne;
177     if (mincap > cap)
178       cap = (mincap << 1) - (mincap >> 1);
179   }
180 
181   /// Sets: cap *= 1.5
182   void growX1_5()
183   {
184     cap = (cap << 1) - (cap >> 1);
185   }
186 
187   /// Sets: cap *= 2
188   void growX2()
189   {
190     cap = cap * 2;
191   }
192 
193   /// Adds x to ptr or subtracts from end.
194   inout(E)* indexPtr(T)(T x) inout
195   {
196     static if (is(T : Neg))
197       auto p = cur - x;
198     else
199       auto p = ptr + x;
200     assert(ptr <= p && p <= cur);
201     return p;
202   }
203 
204   /// Returns a slice.
205   inout(E)[] opSlice() inout
206   {
207     return ptr[0..len];
208   }
209   /// ditto
210   alias array = opSlice;
211 
212   /// Returns a slice from x to y.
213   inout(E)[] opSlice(X, Y)(X x, Y y) inout
214   {
215     auto p = indexPtr!X(x), cur = indexPtr!Y(y);
216     return p[0..cur-p];
217   }
218 
219   /// Returns the character at position x.
220   inout(E) opIndex(T)(T x) inout
221   {
222     return *indexPtr!T(x);
223   }
224 
225   /// Assigns e at position x.
226   ref DArray opIndexAssign(T)(E e, T x)
227   {
228     *indexPtr!T(x) = e;
229     return this;
230   }
231 
232   /// Returns a copy.
233   E[] dup()
234   {
235     return this[].dup;
236   }
237 
238   /// Returns the contents and clears this DArray.
239   E[] take()
240   {
241     auto contents = this[];
242     ptr = cur = end = null;
243     return contents;
244   }
245 }
246 
247 alias CharArray = DArray!char;
248 
249 /// Fast, mutable, resizable array implementation.
250 struct Array
251 {
252   alias E = ubyte; /// Alias to ubyte. Dereferencing void* gives no value.
253   E* ptr; /// Points to the start of the buffer.
254   E* cur; /// Points to the end of the contents. Only dereference if cur < end.
255   E* end; /// Points to the end of the reserved space.
256 
257   /// Constructs an Array and reserves space of n bytes.
258   this(size_t n = 0)
259   {
260     if (n)
261       if (auto p = cast(E*)malloc(n))
262         end = (ptr = cur = p) + n;
263       else
264         onOutOfMemoryError();
265   }
266 
267   invariant()
268   {
269     if (!ptr)
270       assert(cur is null && end is null, "!(ptr == cur == end == 0)");
271     else
272       assert(ptr <= cur && cur <= end,
273         Format("!({} <= {} <= {})", ptr, cur, end));
274   }
275 
276   /// Returns the size of the Array in bytes.
277   size_t len() @property const
278   {
279     return cur - ptr;
280   }
281 
282   /// Sets the size of the Array in bytes.
283   /// Resizes space if necessary. Does not deallocate if n is zero.
284   void len(size_t n) @property
285   {
286     if ((ptr + n) > end)
287       reserve(n);
288     cur = ptr + n;
289   }
290 
291   /// Returns the remaining space in bytes before a reallocation is needed.
292   size_t rem() @property const
293   {
294     return end - cur;
295   }
296 
297   /// Sets the capacity so that n bytes of space remain.
298   void rem(size_t n) @property
299   {
300     cap = len + n;
301   }
302 
303   /// Returns the total capacity of the Array.
304   size_t cap() @property const
305   {
306     return end - ptr;
307   }
308 
309   /// Sets the capacity to exactly n bytes.
310   void cap(size_t n) @property
311   {
312     reserve(n);
313   }
314 
315   /// Allocates exactly n bytes. May shrink or extend in place if possible.
316   /// Does not zero out memory. Destroys if n is zero.
317   /// Throws: OutOfMemoryError.
318   void reserve(size_t n)
319   {
320     if (n == 0)
321       return destroy();
322     auto new_ptr = ptr;
323     new_ptr = cast(E*)(new_ptr ? realloc(new_ptr, n) : malloc(n));
324     if (!new_ptr)
325       onOutOfMemoryError();
326     auto len = this.len;
327     ptr = new_ptr;
328     cur = new_ptr + (len < n ? len : n); // min(len, n)
329     end = new_ptr + n;
330   }
331 
332   /// Frees the allocated memory.
333   void destroy()
334   {
335     free(ptr);
336     ptr = cur = end = null;
337   }
338 
339   /// Grows the capacity by n or cap * 1.5.
340   void growcap(size_t n = 0)
341   {
342     if (!n)
343       n = (cap << 1) - (cap >> 1); // cap *= 1.5
344     else
345       n += cap;
346     reserve(n);
347   }
348 
349   /// Shrinks the Array by n bytes.
350   void shrinkby(size_t n)
351   {
352     reserve(n < cap ? cap - n : 0);
353   }
354 
355   /// Compacts the capacity to the actual length of the Array.
356   /// Destroys if the length is zero.
357   void compact()
358   {
359     reserve(len);
360   }
361 
362   /// Appends x of any type to the Array.
363   /// Appends the elements if X is an array.
364   void opOpAssign(string op : "~", X)(const X x)
365   {
366     static if (is(X : Elem[], Elem))
367     {
368       auto n = x.length * Elem.sizeof;
369       if (cur + n >= end)
370         rem = n;
371       memcpy(cur, x.ptr, n);
372       cur += n;
373     }
374     else
375     {
376       enum n = X.sizeof;
377       if (cur + n >= end)
378         rem = n;
379       static if (n <= size_t.sizeof)
380       {
381         string unroll(string s, size_t times) {
382           return times == 1 ? s : s ~ unroll(s, times-1);
383         }
384 
385         auto p = cast(E*)&x, c = cur;
386         mixin(unroll("*c++ = *p++;", n));
387         cur = c;
388       }
389       else
390       {
391         memcpy(cur, &x, n);
392         cur += n;
393       }
394     }
395     assert(cur <= end);
396   }
397 
398   /// Returns a copy allocated using the GC and destroys this Array.
399   A get(A = E)()
400   {
401     auto result = this[].dup;
402     destroy();
403     return *cast(A*)&result;
404   }
405 
406   /// Returns a slice into the Array.
407   /// Warning: The memory may leak if not freed, or destroyed prematurely.
408   E[] opSlice(size_t i, size_t j)
409   {
410     assert(i <= j && j < len);
411     return ptr[i..j];
412   }
413 
414   /// ditto
415   E[] opSlice()
416   {
417     return ptr[0..len];
418   }
419 
420   /// Returns the Array as a dynamic array.
421   A elems(A = E[])()
422   {
423     return cast(A)this[];
424   }
425 }
426 
427 void testArray()
428 {
429 }