1 /**
2 A type-safe growable buffer designed for low overhead
3 */
4 module dgt.array;
5 import core.stdc.stdlib : malloc, realloc, free;
6 import dgt.io;
7 
8 /**
9 A growable buffer that attempts to keep as many operations amortized as possible
10 */
11 struct Array(T)
12 {
13 
14 	private void* backingBuffer = null;
15 
16     @disable this();
17 
18 	@nogc @trusted nothrow public:
19     /**
20     Create a buffer with a given initial capacity
21 
22     When a buffer runs out of capacity it has to reallocate, which is much slower than a normal add
23     */
24     this(in size_t initialCapacity)
25     {
26         ensureCapacity(initialCapacity);
27     }
28 
29     /**
30     Create a buffer from a fixed-size array of elements
31 
32     This is useful for initializing with an array literal
33     */
34     this(scope T[] array)
35     {
36         ensureCapacity(array.length);
37         foreach(item; array)
38             add(item);
39     }
40 
41     /**
42     Resizes the the array to be a given size manually
43 
44     Not generally a good idea unless you have a specific reason
45     */
46 	@system void ensureCapacity(in size_t newCapacity)
47 	{
48 		void* old = backingBuffer;
49 		backingBuffer = realloc(backingBuffer, size_t.sizeof * 2 + T.sizeof * newCapacity);
50 		*capacity = newCapacity;
51 		if (old == null) *count = 0;
52 	}
53 
54     /**
55     Appends a value to the end of the array
56 
57     If more memory is required, the array is resized
58     */
59 	void add(scope T val)
60 	{
61 		if (*count >= *capacity)
62 			ensureCapacity(*capacity * 2);
63 		buffer[*count] = val;
64 		*count += 1;
65 	}
66 
67     /**
68     Add many values in a compile-time list
69     */
70 	void addAll(A...)(scope A a)
71 	{
72 		foreach(val; a)
73 		{
74 			add(val);
75 		}
76 	}
77 
78     /*
79     Free the memory associated with the Array
80     */
81 	void destroy()
82 	{
83 		free(backingBuffer);
84 	}
85 
86 	int opApply(int delegate(T) @nogc nothrow dg) const
87     {
88 		for(size_t i = 0; i < length; i++)
89 			dg(buffer[i]);
90         return 0;
91     }
92 
93 	void print() const
94 	{
95 		dgt.io.print("Array!", T.stringof, "[");
96 		for(size_t i = 0; i < length; i++)
97 		{
98 			dgt.io.print(this[i]);
99 			if(i != length - 1)
100 			{
101 				dgt.io.print(", ");
102 			}
103 		}
104 		dgt.io.print("]");
105 	}
106 
107 	pure:
108     /**
109     Remove an element at an index by swapping it with the last element
110     */
111 	void remove(in size_t index)
112 	{
113 		buffer[index] = buffer[*count];
114 		*count -= 1;
115 	}
116 
117 	ref T opIndex(in size_t index) const
118 	{
119 		assert(index < *count);
120 		return buffer[index];
121 	}
122 
123 	ref T opIndexAssign(scope T value, in size_t index)
124 	{
125 		assert(index < *count);
126 		return buffer[index] = value;
127 	}
128 
129     /**
130     Reset the element count without changing the size of the allocated chunk
131     */
132 	void clear()
133 	{
134 		*count = 0;
135 	}
136     
137     /**
138     Get the pointer to the elements stored in the buffer
139     */
140     @property T* ptr() const { return buffer; }
141     /**
142     Find the number of valid elements currently in the array
143     */
144     @property size_t length() const { return *count; }
145     ///Get the Array data as a D array
146     @property T[] array() const { return buffer[0..length]; }
147 
148 	private:
149 	private @property size_t* count() const { return cast(size_t*) backingBuffer; }
150 	private @property size_t* capacity() const { return count + 1; }
151 	private @property T* buffer() const { return cast(T*)(capacity + 1); }
152 }
153 
154 @nogc nothrow:
155 unittest
156 {
157 	auto x = Array!int(4);
158 	for(int i = 0; i < 17; i++)
159 	{
160 		x.add(i);
161 	}
162 	assert(x[0] == 0);
163 	assert(x[16] == 16);
164     x.addAll(1, 2, 3, 4);
165     assert(x[x.length - 1] == 4);
166     assert(x.ptr[0] == 0);
167     x[0] = 5;
168     assert(x[0] == 5);
169     size_t length = x.length;
170     x.remove(0);
171     assert(x.length == length - 1);
172     int first = x[0];
173     auto other = Array!int(1);
174     foreach(val; x)
175     {
176         other.add(val);
177     }
178     assert(x.length == other.length);
179     assert(other[5] == x[5]);
180     println(x);
181     x.clear();
182     assert(x.length == 0);
183     auto array = x.array;
184     assert(array.length == x.length);
185     for(size_t i = 0; i < array.length; i++)
186         assert(array[i] == x[i]);
187 	x.destroy();
188 }