1 module ut.unique_array;
2 
3 import ut;
4 import automem.unique_array;
5 
6 mixin TestUtils;
7 
8 @("default TestAllocator")
9 @system unittest {
10     defaultTest!TestAllocator;
11 }
12 
13 
14 @("default Mallocator")
15 @system unittest {
16     import stdx.allocator.mallocator: Mallocator;
17     defaultTest!Mallocator;
18 }
19 
20 ///
21 @("@nogc")
22 @system @nogc unittest {
23 
24     import stdx.allocator.mallocator: Mallocator;
25 
26     auto arr = UniqueArray!(NoGcStruct, Mallocator)(2);
27     assert(arr.length == 2);
28 
29     arr[0] = NoGcStruct(1);
30     arr[1] = NoGcStruct(3);
31 
32     {
33         NoGcStruct[2] expected = [NoGcStruct(1), NoGcStruct(3)];
34         assert(arr[] == expected[]);
35     }
36 
37     auto arr2 = UniqueArray!(NoGcStruct, Mallocator)(1);
38     arr ~= arr2.unique;
39 
40     {
41         NoGcStruct[3] expected = [NoGcStruct(1), NoGcStruct(3), NoGcStruct()];
42         assert(arr[] == expected[]);
43     }
44 }
45 
46 @("@nogc @safe")
47 @safe @nogc unittest {
48     auto allocator = SafeAllocator();
49     auto arr = UniqueArray!(NoGcStruct, SafeAllocator)(SafeAllocator(), 6);
50     assert(arr.length == 6);
51 }
52 
53 
54 @("init TestAllocator")
55 @system unittest {
56     auto allocator = TestAllocator();
57     auto arr = UniqueArray!(Struct, TestAllocator*)(&allocator, 2, Struct(7));
58     arr[].shouldEqual([Struct(7), Struct(7)]);
59 }
60 
61 @("init Mallocator")
62 @system unittest {
63     import stdx.allocator.mallocator: Mallocator;
64     alias allocator = Mallocator.instance;
65     auto arr = UniqueArray!(Struct, Mallocator)(2, Struct(7));
66     arr[].shouldEqual([Struct(7), Struct(7)]);
67 }
68 
69 
70 @("range TestAllocator")
71 @system unittest {
72     auto allocator = TestAllocator();
73     auto arr = UniqueArray!(Struct, TestAllocator*)(&allocator, [Struct(1), Struct(2)]);
74     arr[].shouldEqual([Struct(1), Struct(2)]);
75 }
76 
77 @("range Mallocator")
78 @system unittest {
79     import stdx.allocator.mallocator: Mallocator;
80     auto arr = UniqueArray!(Struct, Mallocator)([Struct(1), Struct(2)]);
81     arr[].shouldEqual([Struct(1), Struct(2)]);
82 }
83 
84 
85 @("theAllocator")
86 @system unittest {
87     with(theTestAllocator) {
88         auto arr = UniqueArray!Struct(2);
89         arr[].shouldEqual([Struct(), Struct()]);
90     }
91 }
92 
93 @("issue 1 array")
94 @system unittest {
95     import stdx.allocator.mallocator;
96     UniqueArray!(int, Mallocator) a;
97     a ~= [0, 1];
98 }
99 
100 @("issue 1 value")
101 @system unittest {
102     import stdx.allocator.mallocator;
103     UniqueArray!(int, Mallocator) a;
104     a ~= 7;
105 }
106 
107 @("issue 1 UniqueArray")
108 @system unittest {
109     import stdx.allocator.mallocator;
110     UniqueArray!(int, Mallocator) a;
111     a ~= UniqueArray!(int, Mallocator)([1, 2, 3]);
112 }
113 
114 @("dereference")
115 unittest {
116     import stdx.allocator.mallocator;
117     UniqueArray!(int, Mallocator) a;
118     a ~= [0, 1];
119     (*a).shouldEqual([0, 1]);
120 }
121 
122 @("reserve from nothing")
123 @system unittest {
124     auto allocator = TestAllocator();
125     auto a = UniqueArray!(int, TestAllocator*)(&allocator);
126     a.reserve(10); //allocates here
127     a ~= [1, 2, 3]; // should not allocate
128     a ~= [4, 5, 6, 7, 8, 9]; //should not allocate
129     a[].shouldEqual([1, 2, 3, 4, 5, 6, 7, 8, 9]);
130     allocator.numAllocations.shouldEqual(1);
131 }
132 
133 @("reserve from existing expand")
134 @system unittest {
135     auto allocator = TestAllocator();
136     auto a = UniqueArray!(int, TestAllocator*)(&allocator, [1, 2]); //allocates here
137     a.reserve(10); //allocates here
138     a ~= [3, 4]; // should not allocate
139     a ~= [5, 6, 7, 8, 9]; //should not allocate
140     a[].shouldEqual([1, 2, 3, 4, 5, 6, 7, 8, 9]);
141     allocator.numAllocations.shouldEqual(2);
142 }
143 
144 @("reserve from existing reduce")
145 @system unittest {
146     auto allocator = TestAllocator();
147     auto a = UniqueArray!(int, TestAllocator*)(&allocator, [1, 2, 3, 4, 5]); //allocates here
148     a.reserve(2); // should not allocate, changes length to 2
149     a ~= [5, 6];  // should not allocate
150     a[].shouldEqual([1, 2, 5, 6]);
151     allocator.numAllocations.shouldEqual(1);
152 }
153 
154 @("Append 2 arrays")
155 @system unittest {
156     auto allocator = TestAllocator();
157     auto a = UniqueArray!(int, TestAllocator*)(&allocator, [1, 2, 3]) ~
158              UniqueArray!(int, TestAllocator*)(&allocator, [4, 5]);
159     a[].shouldEqual([1, 2, 3, 4, 5]);
160 }
161 
162 @("ptr")
163 @system unittest {
164     auto allocator = TestAllocator();
165     auto a = UniqueArray!(int, TestAllocator*)(&allocator, [1, 2, 3, 4, 5]);
166     auto ptr = a.ptr;
167     ++ptr;
168     (*ptr).shouldEqual(2);
169 }
170 
171 @("dup TestAllocator")
172 @system unittest {
173     auto allocator = TestAllocator();
174     auto a = UniqueArray!(int, TestAllocator*)(&allocator, [1, 2, 3, 4, 5]);
175     auto b = a.dup;
176     allocator.numAllocations.shouldEqual(2);
177     b[].shouldEqual([1, 2, 3, 4, 5]);
178 }
179 
180 @("dup Mallocator")
181 @system unittest {
182     import stdx.allocator.mallocator: Mallocator;
183     auto a = UniqueArray!(int, Mallocator)([1, 2, 3, 4, 5]);
184     auto b = a.dup;
185     b[].shouldEqual([1, 2, 3, 4, 5]);
186 }
187 
188 @("dup TestAllocator indirections")
189 @system unittest {
190     auto allocator = TestAllocator();
191     struct String { string s; }
192     auto a = UniqueArray!(String, TestAllocator*)(&allocator, [String("foo"), String("bar")]);
193     auto b = a.dup;
194     a[0] = String("quux");
195     a[1] = String("toto");
196     allocator.numAllocations.shouldEqual(2);
197     a[].shouldEqual([String("quux"), String("toto")]);
198     b[].shouldEqual([String("foo"), String("bar")]);
199 }
200 
201 @("Set length to the same length")
202 unittest {
203     auto allocator = TestAllocator();
204     auto a = UniqueArray!(Struct, TestAllocator*)(&allocator, [Struct(2), Struct(3)]);
205     a.length = 2;
206 }
207 
208 
209 void defaultTest(T)() {
210     import std.algorithm: move;
211 
212     mixin AllocatorAlias!T;
213 
214     auto ptr = makeUniqueArray!(Struct, Allocator)(allocator, 3);
215     ptr.length.shouldEqual(3);
216 
217     ptr[2].twice.shouldEqual(0);
218     ptr[2] = Struct(5);
219     ptr[2].twice.shouldEqual(10);
220 
221     ptr[1..$].shouldEqual([Struct(), Struct(5)]);
222 
223     typeof(ptr) ptr2 = ptr.move;
224 
225     ptr.length.shouldEqual(0);
226     (cast(bool)ptr).shouldBeFalse;
227     ptr2.length.shouldEqual(3);
228     (cast(bool)ptr2).shouldBeTrue;
229 
230     // not copyable
231     static assert(!__traits(compiles, ptr2 = ptr1));
232 
233     auto ptr3 = ptr2.unique;
234     ptr3.length.shouldEqual(3);
235     ptr3[].shouldEqual([Struct(), Struct(), Struct(5)]);
236     (*ptr3).shouldEqual([Struct(), Struct(), Struct(5)]);
237 
238     ptr3 ~= Struct(10);
239     ptr3[].shouldEqual([Struct(), Struct(), Struct(5), Struct(10)]);
240 
241     ptr3 ~= [Struct(11), Struct(12)];
242     ptr3[].shouldEqual([Struct(), Struct(), Struct(5), Struct(10), Struct(11), Struct(12)]);
243 
244     ptr3.length = 3;
245     ptr3[].shouldEqual([Struct(), Struct(), Struct(5)]);
246 
247     ptr3.length = 4;
248     ptr3[].shouldEqual([Struct(), Struct(), Struct(5), Struct()]);
249 
250     ptr3.length = 1;
251 
252     ptr3 ~= makeUniqueArray!(Struct, Allocator)(allocator, 1);
253 
254     ptr3[].shouldEqual([Struct(), Struct()]);
255 
256     auto ptr4 = makeUniqueArray!(Struct, Allocator)(allocator, 1);
257 
258     ptr3 ~= ptr4.unique;
259     ptr3[].shouldEqual([Struct(), Struct(), Struct()]);
260 
261     ptr3 = [Struct(7), Struct(9)];
262     ptr3[].shouldEqual([Struct(7), Struct(9)]);
263 }
264 
265 mixin template AllocatorAlias(T) {
266     import std.traits: hasMember;
267 
268     enum isGlobal = hasMember!(T, "instance");
269 
270     static if(isGlobal) {
271         alias allocator = T.instance;
272         alias Allocator = T;
273     } else {
274         auto allocator = T();
275         alias Allocator = T*;
276     }
277 }
278 
279 
280 auto makeUniqueArray(T, A1, A2, Args...)(ref A2 allocator, Args args) {
281 
282     import std.traits: isPointer, hasMember;
283 
284     enum isGlobal = hasMember!(A1, "instance");
285 
286     static if(isGlobal)
287         return UniqueArray!(T, A1)(args);
288     else static if(isPointer!A1)
289         return UniqueArray!(T, A1)(&allocator, args);
290     else
291         return UniqueArray!(T, A1)(allocator, args);
292 }