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