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