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 Ilya Yaroshenko, 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 mir.appender: scopedBuffer;
116         import std.array: uninitializedArray;
117 
118         auto a = scopedBuffer!(Unqual!E);
119 
120         static if (isInputRange!Range)
121             for (; !r.empty; r.popFront)
122                 a.put(r.front);
123         else
124         static if (isPointer!Range)
125         {
126             foreach (e; *r)
127                 a.put(forward!e);
128         }
129         else
130         {
131             foreach (e; r)
132                 a.put(forward!e);
133         }
134 
135         return () @trusted {
136             auto ret = uninitializedArray!(Unqual!E[])(a.length);
137             a.moveDataAndEmplaceTo(ret);
138             return ret;
139         } ();
140     }
141 }
142 
143 ///
144 @safe pure nothrow version(mir_test) unittest
145 {
146     auto a = array([1, 2, 3, 4, 5][]);
147     assert(a == [ 1, 2, 3, 4, 5 ]);
148 }
149 
150 @safe pure nothrow version(mir_test) unittest
151 {
152     import mir.algorithm.iteration : equal;
153     struct Foo
154     {
155         int a;
156     }
157     auto a = array([Foo(1), Foo(2), Foo(3), Foo(4), Foo(5)][]);
158     assert(equal(a, [Foo(1), Foo(2), Foo(3), Foo(4), Foo(5)]));
159 }
160 
161 @safe pure nothrow version(mir_test) unittest
162 {
163     struct MyRange
164     {
165         enum front = 123;
166         enum empty = true;
167         void popFront() {}
168     }
169 
170     auto arr = (new MyRange).array;
171     assert(arr.empty);
172 }
173 
174 @system pure nothrow version(mir_test) unittest
175 {
176     immutable int[] a = [1, 2, 3, 4];
177     auto b = (&a).array;
178     assert(b == a);
179 }
180 
181 @system version(mir_test) unittest
182 {
183     import mir.algorithm.iteration : equal;
184     struct Foo
185     {
186         int a;
187         void opAssign(Foo)
188         {
189             assert(0);
190         }
191         auto opEquals(Foo foo)
192         {
193             return a == foo.a;
194         }
195     }
196     auto a = array([Foo(1), Foo(2), Foo(3), Foo(4), Foo(5)][]);
197     assert(equal(a, [Foo(1), Foo(2), Foo(3), Foo(4), Foo(5)]));
198 }
199 
200 @safe version(mir_test) unittest
201 {
202     // Issue 12315
203     static struct Bug12315 { immutable int i; }
204     enum bug12315 = [Bug12315(123456789)].array();
205     static assert(bug12315[0].i == 123456789);
206 }
207 
208 @safe version(mir_test) unittest
209 {
210     import mir.ndslice.topology: repeat;
211     static struct S{int* p;}
212     auto a = array(immutable(S).init.repeat(5));
213     assert(a.length == 5);
214 }
215 
216 ///
217 @safe version(mir_test) unittest
218 {
219     assert("Hello D".array == "Hello D");
220     assert("Hello D"w.array == "Hello D"w);
221     assert("Hello D"d.array == "Hello D"d);
222 }
223 
224 @system version(mir_test) unittest
225 {
226     // @system due to array!string
227     import std.conv : to;
228 
229     static struct TestArray { int x; string toString() @safe { return to!string(x); } }
230 
231     static struct OpAssign
232     {
233         uint num;
234         this(uint num) { this.num = num; }
235 
236         // Templating opAssign to make sure the bugs with opAssign being
237         // templated are fixed.
238         void opAssign(T)(T rhs) { this.num = rhs.num; }
239     }
240 
241     static struct OpApply
242     {
243         int opApply(scope int delegate(ref int) dg)
244         {
245             int res;
246             foreach (i; 0 .. 10)
247             {
248                 res = dg(i);
249                 if (res) break;
250             }
251 
252             return res;
253         }
254     }
255 
256     auto a = array([1, 2, 3, 4, 5][]);
257     assert(a == [ 1, 2, 3, 4, 5 ]);
258 
259     auto b = array([TestArray(1), TestArray(2)][]);
260     assert(b == [TestArray(1), TestArray(2)]);
261 
262     class C
263     {
264         int x;
265         this(int y) { x = y; }
266         override string toString() const @safe { return to!string(x); }
267     }
268     auto c = array([new C(1), new C(2)][]);
269     assert(c[0].x == 1);
270     assert(c[1].x == 2);
271 
272     auto d = array([1.0, 2.2, 3][]);
273     assert(is(typeof(d) == double[]));
274     assert(d == [1.0, 2.2, 3]);
275 
276     auto e = [OpAssign(1), OpAssign(2)];
277     auto f = array(e);
278     assert(e == f);
279 
280     assert(array(OpApply.init) == [0,1,2,3,4,5,6,7,8,9]);
281     assert(array("ABC") == "ABC");
282     assert(array("ABC".dup) == "ABC");
283 }
284 
285 //Bug# 8233
286 @safe version(mir_test) unittest
287 {
288     assert(array("hello world"d) == "hello world"d);
289     immutable a = [1, 2, 3, 4, 5];
290     assert(array(a) == a);
291     const b = a;
292     assert(array(b) == a);
293 
294     //To verify that the opAssign branch doesn't get screwed up by using Unqual.
295     //EDIT: array no longer calls opAssign.
296     struct S
297     {
298         ref S opAssign(S)(const ref S rhs)
299         {
300             assert(0);
301         }
302 
303         int i;
304     }
305 
306     alias AliasSeq(T...) = T;
307     foreach (T; AliasSeq!(S, const S, immutable S))
308     {
309         auto arr = [T(1), T(2), T(3), T(4)];
310         assert(array(arr) == arr);
311     }
312 }
313 
314 @safe version(mir_test) unittest
315 {
316     //9824
317     static struct S
318     {
319         @disable void opAssign(S);
320         int i;
321     }
322     auto arr = [S(0), S(1), S(2)];
323     arr.array;
324 }
325 
326 // Bugzilla 10220
327 @safe version(mir_test) unittest
328 {
329     import mir.algorithm.iteration : equal;
330     import std.exception;
331     import mir.ndslice.topology: repeat;
332 
333     static struct S
334     {
335         int val;
336 
337         @disable this();
338         this(int v) { val = v; }
339     }
340     static immutable r = S(1).repeat(2).array();
341     assert(equal(r, [S(1), S(1)]));
342 }
343 
344 @safe version(mir_test) unittest
345 {
346     //Turn down infinity:
347     static assert(!is(typeof(
348         repeat(1).array()
349     )));
350 }