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 }