The OpenD Programming Language

1 /**
2 Functions and types that manipulate built-in arrays and associative arrays.
3 
4 This module provides all kinds of functions to create, manipulate or convert arrays:
5 
6 $(SCRIPT inhibitQuickIndex = 1;)
7 $(BOOKTABLE ,
8 $(TR $(TH Function Name) $(TH Description)
9 )
10     $(TR $(TD $(LREF _array))
11         $(TD Returns a copy of the input in a newly allocated dynamic _array.
12     ))
13 )
14 
15 Copyright: 2020 Ilia Ki, Kaleidic Associates Advisory Limited, Symmetry Investments
16 
17 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
18 
19 Authors: $(HTTP erdani.org, Andrei Alexandrescu) and Jonathan M Davis
20 
21 Source: $(PHOBOSSRC std/_array.d)
22 */
23 module mir.array.allocation;
24 
25 import mir.functional;
26 import mir.primitives;
27 import std.traits;
28 
29 /**
30  * Allocates an array and initializes it with copies of the elements
31  * of range $(D r).
32  *
33  * Narrow strings are handled as a special case in an overload.
34  *
35  * Params:
36  *      r = range (or aggregate with $(D opApply) function) whose elements are copied into the allocated array
37  * Returns:
38  *      allocated and initialized array
39  */
40 auto array(Range)(Range r)
41 if ((isInputRange!Range || isIterable!Range) && !isInfinite!Range && !__traits(isStaticArray, Range) || isPointer!Range && (isInputRange!(PointerTarget!Range) || isIterable!(PointerTarget!Range)))
42 {
43     static if (isIterable!Range)
44         alias E = ForeachType!Range;
45     else
46     static if (isPointer!Range && isIterable!(PointerTarget!Range))
47         alias E = ForeachType!(PointerTarget!Range);
48     else
49         alias E = ElementType!Range;
50 
51     if (__ctfe)
52     {
53         // Compile-time version to avoid memcpy calls.
54         // Also used to infer attributes of array().
55         E[] result;
56         static if (isInputRange!Range)
57             for (; !r.empty; r.popFront)
58                 result ~= r.front;
59         else
60         static if (isPointer!Range)
61             foreach (e; *r)
62                 result ~= e;
63         else
64             foreach (e; r)
65                 result ~= e;
66         return result;
67     }
68 
69     import mir.primitives: hasLength;
70 
71     static if (hasLength!Range)
72     {
73         auto length = r.length;
74         if (length == 0)
75             return null;
76 
77         import mir.conv : emplaceRef;
78         import std.array: uninitializedArray;
79 
80         auto result = (() @trusted => uninitializedArray!(Unqual!E[])(length))();
81 
82         static if (isInputRange!Range)
83         {
84             foreach(ref e; result)
85             {
86                 emplaceRef!E(e, r.front);
87                 r.popFront;
88             }
89         }
90         else
91         static if (isPointer!Range)
92         {
93             auto it = result;
94             foreach(ref f; *r)
95             {
96                 emplaceRef!E(it[0], f);
97                 it = it[1 .. $];
98             }
99         }
100         else
101         {
102             auto it = result;
103             foreach (f; r)
104             {
105                 import mir.functional: forward;
106                 emplaceRef!E(it[0], forward!f);
107                 it = it[1 .. $];
108             }
109         }
110 
111         return (() @trusted => cast(E[]) result)();
112     }
113     else
114     {
115         import std.array: std_appender = appender;
116 
117         auto a = std_appender!(E[]);
118 
119         static if (isInputRange!Range)
120             for (; !r.empty; r.popFront)
121                 a.put(r.front);
122         else
123         static if (isPointer!Range)
124         {
125             foreach (e; *r)
126                 a.put(forward!e);
127         }
128         else
129         {
130             foreach (e; r)
131                 a.put(forward!e);
132         }
133         return a.data;
134     }
135 }
136 
137 ///
138 @safe pure nothrow version(mir_test) unittest
139 {
140     auto a = array([1, 2, 3, 4, 5][]);
141     assert(a == [ 1, 2, 3, 4, 5 ]);
142 }
143 
144 @safe pure nothrow version(mir_test) unittest
145 {
146     import mir.algorithm.iteration : equal;
147     struct Foo
148     {
149         int a;
150     }
151     auto a = array([Foo(1), Foo(2), Foo(3), Foo(4), Foo(5)][]);
152     assert(equal(a, [Foo(1), Foo(2), Foo(3), Foo(4), Foo(5)]));
153 }
154 
155 @safe pure nothrow version(mir_test) unittest
156 {
157     struct MyRange
158     {
159         enum front = 123;
160         enum empty = true;
161         void popFront() {}
162     }
163 
164     auto arr = (new MyRange).array;
165     assert(arr.empty);
166 }
167 
168 @system pure nothrow version(mir_test) unittest
169 {
170     immutable int[] a = [1, 2, 3, 4];
171     auto b = (&a).array;
172     assert(b == a);
173 }
174 
175 @system version(mir_test) unittest
176 {
177     import mir.algorithm.iteration : equal;
178     struct Foo
179     {
180         int a;
181         void opAssign(Foo)
182         {
183             assert(0);
184         }
185         auto opEquals(Foo foo)
186         {
187             return a == foo.a;
188         }
189     }
190     auto a = array([Foo(1), Foo(2), Foo(3), Foo(4), Foo(5)][]);
191     assert(equal(a, [Foo(1), Foo(2), Foo(3), Foo(4), Foo(5)]));
192 }
193 
194 @safe version(mir_test) unittest
195 {
196     // Issue 12315
197     static struct Bug12315 { immutable int i; }
198     enum bug12315 = [Bug12315(123456789)].array();
199     static assert(bug12315[0].i == 123456789);
200 }
201 
202 @safe version(mir_test) unittest
203 {
204     import mir.ndslice.topology: repeat;
205     static struct S{int* p;}
206     auto a = array(immutable(S).init.repeat(5));
207     assert(a.length == 5);
208 }
209 
210 ///
211 @safe version(mir_test) unittest
212 {
213     assert("Hello D".array == "Hello D");
214     assert("Hello D"w.array == "Hello D"w);
215     assert("Hello D"d.array == "Hello D"d);
216 }
217 
218 @system version(mir_test) unittest
219 {
220     // @system due to array!string
221     import std.conv : to;
222 
223     static struct TestArray { int x; string toString() scope const @safe { return to!string(x); } }
224 
225     static struct OpAssign
226     {
227         uint num;
228         this(uint num) { this.num = num; }
229 
230         // Templating opAssign to make sure the bugs with opAssign being
231         // templated are fixed.
232         void opAssign(T)(T rhs) { this.num = rhs.num; }
233     }
234 
235     static struct OpApply
236     {
237         int opApply(scope int delegate(ref int) dg)
238         {
239             int res;
240             foreach (i; 0 .. 10)
241             {
242                 res = dg(i);
243                 if (res) break;
244             }
245 
246             return res;
247         }
248     }
249 
250     auto a = array([1, 2, 3, 4, 5][]);
251     assert(a == [ 1, 2, 3, 4, 5 ]);
252 
253     auto b = array([TestArray(1), TestArray(2)][]);
254     assert(b == [TestArray(1), TestArray(2)]);
255 
256     class C
257     {
258         int x;
259         this(int y) { x = y; }
260         override string toString() const @safe { return to!string(x); }
261     }
262     auto c = array([new C(1), new C(2)][]);
263     assert(c[0].x == 1);
264     assert(c[1].x == 2);
265 
266     auto d = array([1.0, 2.2, 3][]);
267     assert(is(typeof(d) == double[]));
268     assert(d == [1.0, 2.2, 3]);
269 
270     auto e = [OpAssign(1), OpAssign(2)];
271     auto f = array(e);
272     assert(e == f);
273 
274     assert(array(OpApply.init) == [0,1,2,3,4,5,6,7,8,9]);
275     assert(array("ABC") == "ABC");
276     assert(array("ABC".dup) == "ABC");
277 }
278 
279 //Bug# 8233
280 @safe version(mir_test) unittest
281 {
282     assert(array("hello world"d) == "hello world"d);
283     immutable a = [1, 2, 3, 4, 5];
284     assert(array(a) == a);
285     const b = a;
286     assert(array(b) == a);
287 
288     //To verify that the opAssign branch doesn't get screwed up by using Unqual.
289     //EDIT: array no longer calls opAssign.
290     struct S
291     {
292         ref S opAssign(S)(const ref S rhs)
293         {
294             assert(0);
295         }
296 
297         int i;
298     }
299 
300     alias AliasSeq(T...) = T;
301     foreach (T; AliasSeq!(S, const S, immutable S))
302     {
303         auto arr = [T(1), T(2), T(3), T(4)];
304         assert(array(arr) == arr);
305     }
306 }
307 
308 @safe version(mir_test) unittest
309 {
310     //9824
311     static struct S
312     {
313         @disable void opAssign(S);
314         int i;
315     }
316     auto arr = [S(0), S(1), S(2)];
317     arr.array;
318 }
319 
320 // Bugzilla 10220
321 @safe version(mir_test) unittest
322 {
323     import mir.algorithm.iteration : equal;
324     import std.exception;
325     import mir.ndslice.topology: repeat;
326 
327     static struct S
328     {
329         int val;
330 
331         @disable this();
332         this(int v) { val = v; }
333     }
334     static immutable r = S(1).repeat(2).array();
335     assert(equal(r, [S(1), S(1)]));
336 }
337 
338 @safe version(mir_test) unittest
339 {
340     //Turn down infinity:
341     static assert(!is(typeof(
342         repeat(1).array()
343     )));
344 }