1 /** Additions to $(STDMODULE _array).
2 
3 Copyright: Denis Shelomovskij 2011-2013
4 
5 License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
6 
7 Authors: Denis Shelomovskij
8 */
9 module unstd.array;
10 
11 
12 import core.stdc..string;
13 public import std.array;
14 
15 import unstd.generictuple;
16 import unstd.traits;
17 
18 
19 /**
20 Represents value as a flat (single-dimension) static array.
21 Considers $(D T) to be an $(D n)-dimensional static array type.
22 
23 See also
24 $(DPREF traits, MultidimStaticArrayElementType),
25 $(DPREF traits, multidimStaticArrayElementCount).
26 */
27 ref asFlatStaticArray(T, size_t n = staticArrayDims!T)(ref T t) @trusted pure nothrow @nogc
28 {
29 	return *(cast(MultidimStaticArrayElementType!(T, n)
30 		[multidimStaticArrayElementCount!(T, n)]*) &t);
31 }
32 
33 /// ditto
34 ref asFlatStaticArray(size_t n, T)(ref T t) @safe pure nothrow @nogc
35 {
36 	return asFlatStaticArray!(T, n)(t);
37 }
38 
39 ///
40 @safe pure nothrow unittest
41 {
42 	int i;
43 	static assert(is(typeof(asFlatStaticArray(i)) == int[1]));
44 	asFlatStaticArray(i)[] = 5;
45 	assert(i == 5);
46 
47 	int[1][2][3] mdimSArr;
48 	static assert(is(typeof(asFlatStaticArray(mdimSArr)) == int[6]));
49 	asFlatStaticArray(mdimSArr) = [1, 2, 3, 4, 5, 6];
50 	assert(mdimSArr == [[[1], [2]], [[3], [4]], [[5], [6]]]);
51 
52 	static assert(is(typeof(asFlatStaticArray!2(mdimSArr)) == int[1][6]));
53 	assert(asFlatStaticArray!2(mdimSArr) == [[1], [2], [3], [4], [5], [6]]);
54 }
55 
56 @safe pure nothrow unittest
57 {
58 	static void test(T, U, El, V, W)(U valInit, El[] sarrFrom, V arrAssign, W valNew) @safe
59 	{
60 		T t = valInit;
61 		auto p = &asFlatStaticArray(t);
62 		(() @trusted => assert(cast(void*) p == &t)) ();
63 		static assert((*p).sizeof == T.sizeof);
64 		assert(*p == sarrFrom);
65 		static if(isArray!V)
66 			(*p)[] = arrAssign[];
67 		else
68 			(*p)[] = arrAssign;
69 		assert(t == valNew);
70 	}
71 
72 	test!int(3, [3],  4 , 4);
73 	test!int(3, [3], [4], 4);
74 
75 	test!(int[0])(null, [], null, []);
76 	test!(int[0])( 3, [], null, []);
77 	test!(int[0])( 3, [],  4, []);
78 
79 	test!(int[2])([3, 4], [3, 4], [5, 6], [5, 6]);
80 	test!(int[2])(3, [3, 3], 1, [1, 1]);
81 }
82 
83 
84 /**
85 Binary copies $(D src) to $(D dest). $(D src) and $(D dest) can overlap.
86 
87 This function is a preffered way over $(I C)'s $(D memcpy) and $(D memmove) as
88 it's CTFE-able and can work faster than $(I C)'s ones as it knows data type.
89 */
90 void rawCopy(T)(const ref T src, ref T dest) @system pure nothrow @nogc
91 {
92 	if(__ctfe)
93 	{
94 		rawCopyCTImpl(src, dest);
95 	}
96 	else static if(T.sizeof == 16 || T.sizeof == 12 || (T.sizeof < 11 && T.sizeof != 7))
97 	{
98 		// Optimization for structs <= 16 bytes except for sizes 7, 11, 13-15.
99 
100 		alias Types = GenericTuple!(byte, short, int, 0, long);
101 
102 		enum bytes1 = {
103 			foreach(bytes1; [8, 4, 2, 1])
104 				foreach(bytes2; [0, 1, 2, 4, 8])
105 					if(bytes1 + bytes2 == T.sizeof)
106 						return bytes1;
107 			assert(0);
108 		}();
109 		enum bytes2 = T.sizeof - bytes1;
110 
111 		alias U = Types[bytes1 / 2];
112 
113 		static if(bytes2)
114 		{
115 			alias V = Types[bytes2 / 2];
116 			immutable tmp = *cast(V*) (cast(U*) &src + 1);
117 		}
118 
119 		*cast(U*) &dest = *cast(U*) &src;
120 
121 		static if(bytes2)
122 			*cast(V*) (cast(U*) &dest + 1) = tmp;
123 	}
124 	else
125 	{
126 		memmove(&dest, &src, T.sizeof);
127 	}
128 }
129 
130 /// ditto
131 void rawCopy(T)(in T* src, T* dest, size_t count) @system pure nothrow @nogc
132 in { assert(count * T.sizeof / T.sizeof == count); }
133 body
134 {
135 	if(__ctfe)
136 		rawCopyCTImpl(src, dest, count);
137 	else
138 		memmove(dest, src, T.sizeof * count);
139 }
140 
141 private void rawCopyCTImpl(T)(const ref T src, ref T dest) @system pure nothrow @nogc
142 {
143 	static if(isAssignable!T && !hasElaborateAssign!T)
144 	{
145 		dest = cast(T) src;
146 	}
147 	else static if(isStaticArray!T)
148 	{
149 		// We assume static arrays can not overlap in CTFE
150 		foreach(i, ref el; src)
151 			rawCopyCTImpl(el, dest[i]);
152 	}
153 	else static if(is(T == struct))
154 	{
155 		// A struct can be unassignable because of elaborate
156 		// copy constructor or const fields.
157 		foreach(i, ref field; src.tupleof)
158 		{
159 			alias F = typeof(field);
160 			static if (is(F U == shared const U))
161 				alias Unqualed = shared(U);
162 			else 
163 				alias Unqualed = Unqual!F;
164 			rawCopyCTImpl(*cast(Unqualed*) &field, *cast(Unqualed*) &dest.tupleof[i]);
165 		}
166 	}
167 	else
168 	{
169 		static assert(0, T.stringof ~ " isn't assignable");
170 	}
171 }
172 
173 private void rawCopyCTImpl(T)(in T* src, T* dest, in size_t count) @system pure nothrow @nogc
174 {
175 	static if(is(T == void))
176 	{
177 		rawCopyCTImpl(cast(ubyte*) src, cast(ubyte*) dest, count);
178 	}
179 	else
180 	{
181 		if(count == 1) // As we can't slice non-arrays in CTFE
182 			rawCopyCTImpl(*src, *dest);
183 		else if(count != 0)
184 			foreach(i, ref el; src[0 .. count])
185 				rawCopyCTImpl(el, dest[i]);
186 	}
187 }
188 
189 pure nothrow @nogc unittest
190 {
191 	void test(alias f)() @nogc
192 	{
193 		{
194 			int src = 1, dest;
195 			f(src, dest);
196 			assert(dest == 1);
197 		}
198 		{
199 			static struct S1
200 			{
201 				int n;
202 				int* p;
203 				const int cn;
204 				shared int sn;
205 			}
206 			int i;
207 			const S1 src = { 1, &i, 2, 3 };
208 			S1 dest;
209 			f(src, dest);
210 			assert(dest == S1(1, &i, 2, 3));
211 
212 			const S1[2] srcArr = src;
213 			S1[2] destArr;
214 			f(srcArr, destArr);
215 			assert(destArr[0] == S1(1, &i, 2, 3));
216 			assert(destArr[1] == destArr[0]);
217 		}
218 		{
219 			static struct S3
220 			{
221 				void opAssign(typeof(this)) { assert(0); }
222 				this(this) { assert(0); }
223 				// ~this() { } Can not test destructor because of compiler @@@BUG@@@
224 				int n;
225 			}
226 			S3 src = { 1 }, dest;
227 			f(src, dest);
228 			assert(dest.n == 1);
229 
230 			dest.n = 0;
231 			f(&src, &dest, 0);
232 			assert(dest.n == 0);
233 
234 			f(&src, &dest, 1);
235 			assert(dest.n == 1);
236 
237 			S3[2] srcArr, destArr;
238 			srcArr[0].n = srcArr[1].n = 1; // To not call postblit
239 			f(srcArr.ptr, destArr.ptr, 2);
240 			assert(destArr[0].n == 1);
241 			assert(destArr[1].n == 1);
242 
243 			static struct S4 { S3 s3; }
244 			S4 src4, dest4;
245 			src4.s3.n = 1; // To not call postblit
246 			f(src4, dest4);
247 			assert(dest4.s3.n == 1);
248 		}
249 		{
250 			ubyte[2] srcArr = [1, 2], destArr;
251 			f!void(srcArr.ptr, destArr.ptr, 2);
252 			assert(destArr == cast(ubyte[2]) [1, 2]);
253 		}
254 	}
255 	test!rawCopyCTImpl(); // Test CT variant at RT
256 	test!rawCopy();
257 	static assert((test!rawCopy(), true));
258 }
259 
260 pure nothrow @nogc unittest // Optimization for small structs correctness check
261 {
262 	static struct S(size_t n)
263 	{ byte[n] arr; }
264 
265 	foreach(n; iotaTuple!17)
266 	{
267 		S!n src;
268 		byte[n + 2] destArr;
269 		auto dest = cast(S!n*) (destArr.ptr + 1);
270 		foreach(byte i; 0 .. n)
271 			src.arr[i] = cast(byte) (i + 1);
272 		rawCopy(src, *dest);
273 		assert(*dest == src);
274 		assert(!destArr[0] && !destArr[$ - 1]);
275 	}
276 }