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 }