1 module nxt.array_help; 2 3 import core.internal.traits : Unqual; 4 5 @safe: 6 7 /** Returns: statically (stack) allocated array with elements of type `T` of 8 * length `n`. 9 * 10 * For more convenient usage alias it as `s' together with UFCS for the 11 * following convenient notation: 12 * 13 * const x = [1, 2, 3].s; 14 * 15 * TODO: Replace with Phobos `staticArray` when dmd automatically does move for uncopyable `T`. 16 * 17 * TODO: Fix problems discussed here: http://forum.dlang.org/post/otrsanpgmokzpzqmfyvx@forum.dlang.org 18 * TODO: File a bug report: http://forum.dlang.org/post/otrsanpgmokzpzqmfyvx@forum.dlang.org 19 * 20 * TODO: fix compiler so that move kicks in here automatically and remove 21 * special case on `isCopyable` 22 * 23 * See_Also: http://dpaste.dzfl.pl/d0059e6e6c09 24 * See_Also: http://forum.dlang.org/post/oq0cd1$2ji3$1@digitalmars.com 25 */ 26 Unqual!T[n] staticArray(T, size_t n)(T[n] x...) @trusted 27 { 28 static if (__traits(isCopyable, T)) /+ TODO: remove when compiler does move for us +/ 29 return x[]; 30 else /+ TODO: remove `move` when compiler does it for us +/ 31 { 32 /+ TODO: remove `move` when compiler does it for us: +/ 33 T[n] y = void; // initialized below 34 import core.internal.traits : hasElaborateDestructor; 35 static if (hasElaborateDestructor!T) { 36 import core.lifetime : move; 37 /* NOTE: moveEmplaceAll doesn't support uncopyable elements 38 * import std.algorithm.mutation : moveEmplaceAll; 39 * moveEmplaceAll(x[], y[]); 40 */ 41 foreach (const ix, ref value; x) 42 move(value, y[ix]); 43 } 44 else 45 { 46 import core.stdc.string : memcpy; 47 memcpy(y.ptr, x.ptr, n*T.sizeof); // fast 48 } 49 return y; 50 } 51 } 52 alias s = staticArray; 53 54 pure @safe unittest { 55 // assert([].staticArray.ptr == null); 56 assert([].s.length == 0); 57 } 58 59 version (none) 60 pure @safe unittest { 61 import std.array : staticArray; 62 assert([].staticArray.ptr !is null); 63 assert([].staticArray.length == 0); 64 } 65 66 /** Make a static array. */ 67 version (none) 68 auto staticArrayAlternative() @safe 69 { 70 static struct _staticArray 71 { 72 T[n] s(T, size_t n)(auto ref T[n] values) @safe @property { return values; } 73 74 T[0][n] opIndex(size_t n = T.length, T...)(T items) { 75 typeof(return) arr; 76 foreach (index,item; items) 77 arr[index] = item; 78 79 return (values) { return values; } (arr);//s!(T[0], n)(arr); 80 } 81 } 82 return _staticArray(); 83 } 84 85 version (unittest) { 86 static struct US 87 { 88 this(this) @disable; 89 int x; 90 void f() { x = 42; } 91 } 92 } 93 94 /// 95 pure nothrow @safe @nogc unittest { 96 auto a = [1, 2, 3].staticArray; 97 static assert(is(typeof(a) == int[a.length])); 98 static assert(is(typeof([1, 2, 3].staticArray) == int[a.length])); 99 100 auto b = "hello".s; 101 static assert(is(typeof(b) == char[5])); 102 103 auto x = s!ubyte(1, 2, 3); 104 static assert(is(typeof(x) == ubyte[3])); 105 } 106 107 /// non-copyable element type in static array 108 pure nothrow @safe @nogc unittest { 109 auto b = [US(42)].s; 110 static assert(b.length == 1); 111 } 112 113 /// 114 pure nothrow @safe @nogc unittest { 115 auto x = [1, 2, 3].staticArray; 116 117 static assert(is(typeof(x) == int[x.length])); 118 static assert(is(typeof([1, 2, 3].staticArray) == int[x.length])); 119 120 static assert(!__traits(compiles, { 121 static int[] doNotDoThat() { 122 return [1, 2, 3].s; 123 } 124 })); 125 } 126 127 /** Returns: `x` as a static array of unsigned bytes. */ 128 @property ubyte[T.sizeof] toUbytes(T)(in T x) @trusted pure nothrow @nogc /+ TODO: endian-dependent +/ 129 => (cast(ubyte*)(&x))[0 .. x.sizeof]; 130 131 /** Returns: `x` as a static array with elements of type `E`. */ 132 @property ref inout(E)[T.sizeof] asN(E, T)(inout ref T x) @trusted pure nothrow @nogc /+ TODO: endian-dependent +/ 133 if (T.sizeof % E.sizeof == 0) 134 => (cast(E*)(&x))[0 .. x.sizeof]; 135 136 /// 137 pure nothrow @safe @nogc unittest { 138 immutable ushort x = 17; 139 auto y = x.asN!ubyte; 140 version(LittleEndian) 141 assert(y == [17, 0].s); 142 } 143 144 /// Number of bytes in a word. 145 private enum wordBytes = size_t.sizeof; 146 147 /// Number of bits in a word. 148 private enum wordBits = 8*wordBytes; 149 150 /** Returns: number of words (`size_t`) needed to represent 151 * `bitCount` bits. 152 */ 153 static size_t wordCountOfBitCount(size_t bitCount) pure nothrow @safe @nogc 154 => ((bitCount / wordBits) + 155 (bitCount % wordBits != 0 ? 1 : 0)); 156 157 static size_t binBlockBytes(size_t bitCount) pure nothrow @safe @nogc 158 => wordBytes*wordCountOfBitCount(bitCount); 159 160 /** Returns: an uninitialized bit-array containing `bitCount` number of bits. */ 161 size_t* makeUninitializedBitArray(alias Allocator)(size_t bitCount) @trusted pure nothrow @nogc 162 => cast(typeof(return))Allocator.instance.allocate(binBlockBytes(bitCount)); 163 164 T[] makeArrayZeroed(T, alias Allocator)(size_t numBytes) @trusted pure nothrow @nogc 165 { 166 import std.experimental.allocator : makeArray; 167 /+ TODO: activate +/ 168 // static if (__traits(isZeroInit, T) && 169 // __traits(hasMember, Allocator, "allocateZeroed")) 170 // { 171 // return cast(typeof(return))makeArray!(T)(Allocator.instance, numBytes); 172 // } 173 // else 174 { 175 import core.stdc.string : memset; 176 auto result = makeArray!(T)(Allocator.instance, numBytes); 177 memset(result.ptr, 0, numBytes); 178 return result; 179 } 180 } 181 182 /** Returns: an zero-initialized bit-array containing `bitCount` number of bits. */ 183 size_t[] makeBitArrayZeroed(alias Allocator)(size_t bitCount) @trusted pure nothrow @nogc 184 { 185 version (D_Coverage) {} else pragma(inline, true); 186 return cast(typeof(return))makeArrayZeroed!(size_t, Allocator)(binBlockBytes(bitCount)); /+ TODO: check aligned allocate +/ 187 } 188 189 /** Returns: `input` reallocated to contain `newBitCount` number of bits. New bits 190 * are default-initialized to zero. 191 */ 192 size_t* makeReallocatedBitArrayZeroPadded(alias Allocator)(size_t* input, 193 const size_t currentBitCount, 194 const size_t newBitCount) @system 195 if (__traits(hasMember, Allocator, "reallocate")) { 196 assert(currentBitCount < newBitCount, "no use reallocate to same size"); 197 198 immutable currentWordCount = wordCountOfBitCount(currentBitCount); 199 immutable newWordCount = wordCountOfBitCount(newBitCount); 200 201 auto rawArray = cast(void[])(input[0 .. currentWordCount]); 202 203 const ok = Allocator.instance.reallocate(rawArray, newWordCount*wordBytes); 204 assert(ok, "couldn't reallocate input"); 205 input = cast(size_t*)rawArray.ptr; 206 207 // See: https://forum.dlang.org/post/puolgthmxgacveqasqkk@forum.dlang.org 208 input[currentWordCount .. newWordCount] = 0; 209 210 return input; 211 } 212 213 /// 214 @trusted pure nothrow @nogc unittest { 215 enum bitCount = 8*size_t.sizeof + 1; 216 217 size_t* x = makeUninitializedBitArray!(Allocator)(bitCount); 218 Allocator.instance.deallocate(cast(void[])(x[0 .. wordCountOfBitCount(bitCount)])); 219 } 220 221 /// 222 @trusted pure nothrow @nogc unittest { 223 size_t bitCount = 1; 224 size_t* y = makeBitArrayZeroed!(Allocator)(bitCount).ptr; // start empty 225 while (bitCount < 100_000) { 226 const newBitCount = bitCount * 2; 227 y = makeReallocatedBitArrayZeroPadded!(Allocator)(y, bitCount, newBitCount); 228 bitCount = newBitCount; 229 230 // check contents 231 foreach (immutable bitIndex; 0 .. bitCount) 232 assert(bt(y, bitIndex) == 0); 233 } 234 Allocator.instance.deallocate(cast(void[]) (y[0 .. wordCountOfBitCount(bitCount)])); 235 } 236 237 version (unittest) { 238 import core.bitop : bt; 239 import std.experimental.allocator.mallocator : Allocator = Mallocator; 240 }