1 module utils; 2 /*************************** 3 * Utils used by SQLite-D * 4 * By Stefan Koch 2016 * 5 ***************************/ 6 7 // Copyright Stefan Koch 2015 - 2018. 8 // Distributed under the Boost Software License, Version 1.0. 9 // (See accompanying file LICENSE.md or copy at 10 // http://www.boost.org/LICENSE_1_0.txt) 11 12 13 /** this struct tries to keep it's own value as BigEndian */ 14 struct BigEndian(T) { 15 T asNative; 16 alias asBigEndian this; 17 18 pure nothrow @safe : 19 @property auto asArray() const { 20 alias staticArrayType = ubyte[T.sizeof]; 21 staticArrayType result = staticArrayType.init; 22 23 foreach(i;0 .. T.sizeof) { 24 result[i] = ((asNative >> i*8) & 0xff); 25 } 26 27 return result.dup; 28 } 29 30 @nogc : 31 @property T asBigEndian() const { 32 return swapIfNeeded(asNative); 33 } 34 35 @property asBigEndian(U)(U val) if(is(U == T)) { 36 return asNative = swapIfNeeded(val); 37 } 38 39 alias isBigEndian = void; 40 41 this (T val) { 42 static if (is(T.isBigEndian)) { 43 this.asNative = val.asNative; 44 } else { 45 this.asNative = swapIfNeeded(val); 46 } 47 } 48 49 this(const ubyte[] _array) @trusted { 50 assert(T.sizeof == _array.length); 51 foreach(i;0 .. T.sizeof) { 52 asNative |= (_array[i] << i*8UL); 53 } 54 } 55 56 BigEndian!T opAssign(BigEndian!T val) { 57 this.asNative = val.asNative; 58 return this; 59 } 60 61 import std.traits : isIntegral; 62 BigEndian!T opAssign(U)(U val) if(!is(U.isBigEndian) && isIntegral!U) { 63 assert(val <= T.max && val >= T.min); 64 this.asNative = swapIfNeeded(cast(T)val); 65 return this; 66 } 67 68 BigEndian!T opAssign(U)(U val) if(is(U : const ubyte[])) { 69 this = BigEndian!T(val); 70 return this; 71 } 72 73 static U swapIfNeeded (U)(U val) { 74 75 version(BigEndian) { 76 return val; 77 } else { 78 static if (is(U.isBigEndian)) { 79 return val; 80 } else { 81 enum _2066_cannot_handle_swapEndian = true; 82 static if (_2066_cannot_handle_swapEndian) { 83 static if (U.sizeof == 8) { 84 return (((val & 0x00000000000000ffUL) << 56UL) | 85 ((val & 0x000000000000ff00UL) << 40UL) | 86 ((val & 0x0000000000ff0000UL) << 24UL) | 87 ((val & 0x00000000ff000000UL) << 8UL) | 88 ((val & 0x000000ff00000000UL) >> 8UL) | 89 ((val & 0x0000ff0000000000UL) >> 24UL) | 90 ((val & 0x00ff000000000000UL) >> 40UL) | 91 ((val & 0xff00000000000000UL) >> 56UL) 92 ); 93 } else static if (U.sizeof == 4) { 94 return ((val & 0x000000ff) << 24) | 95 ((val & 0x0000ff00) << 8) | 96 ((val & 0x00ff0000) >> 8) | 97 ((val & 0xff000000) >> 24); 98 } else static if (U.sizeof == 2) { 99 return cast(ushort)(((val & 0xff00) >> 8) | 100 ((val & 0x00ff) << 8)); 101 } else static if (U.sizeof == 1) { 102 assert(0, "you should not use BigEndian for byte-sized vaules"); 103 } else { 104 assert(0, "cannot swap this byteSize"); 105 } 106 } else { 107 import std.bitmanip:swapEndian; 108 return swapEndian(val); 109 } 110 } 111 } 112 } 113 114 version(BigEndian) {} else { 115 static assert(swapIfNeeded(0xABCD_EF01_2345_6789) == 0x8967_4523_01EF_CDAB); 116 static assert(swapIfNeeded(0x0123_4567_89AB_CDEF) == 0xEFCD_AB89_6745_2301); 117 static assert(BigEndian!int(cast(ubyte[])[0x01, 0x02, 0x03, 0x04]).asArray == (cast(ubyte[])[0x01, 0x02, 0x03, 0x04])); 118 } 119 } 120 121 auto bigEndian(T)(T val) pure { 122 static if (is(T.isBigEndian)) { 123 return BigEndian!(typeof(val.asNative))(val.asNative); 124 } else { 125 return BigEndian!T(val); 126 } 127 } 128 129 T[] toArray(T)(const ubyte[] _array, const size_t size) { 130 if (__ctfe) { 131 T[] result; 132 alias sliceType = typeof(_array[0 .. T.sizeof]); 133 134 result.length = size; 135 136 foreach(i; 0 .. size) { 137 const pos = i * T.sizeof; 138 static if (is(typeof(T(sliceType.init)))) { 139 result[i] = T(_array[pos .. pos + T.sizeof]); 140 } else { 141 static assert(0, T.stringof ~ " has to have a constructor taking " ~ sliceType.stringof); 142 } 143 } 144 145 return result; 146 } else { 147 return cast(T[])(_array); 148 } 149 } 150 151 T fromArray(T)(const ubyte[] _array) { 152 if (__ctfe) { 153 uint offset; 154 T result; 155 static assert(T.alignof == 1, "Be sure to use this only on align(1) structures!"); 156 assert(_array.length >= T.sizeof, "your input array needs to be at least as long as your type.sizeof"); 157 158 ///XXX this cucially depends on your type being byte aligned! 159 foreach (member; __traits(derivedMembers, struct_type)) { 160 alias type = typeof(__traits(getMember, instance, member)); 161 162 static if (!(is(type == function) || is(type == const))) { 163 alias sliceType = typeof(_array[0 .. type.sizeof]); 164 static if (is(typeof(type(sliceType.init)))) { 165 __traits(getMember, result, member) = type(_array[offset .. offset + type.sizeof]); 166 } else static if (type.sizeof == sliceType.init[0].sizeof && is(typeof(cast(type)(sliceType.init[0])))) { 167 __traits(getMember, result, member) = type(_array[offset .. offset + type.sizeof][0]); 168 } else { 169 static assert(0, T.stringof ~ " has to have a constructor taking or needs to be castable to ubyte" ~ sliceType.stringof); 170 } 171 offset += type.sizeof; 172 assert(__traits(getMember, result, member).alignof == offset); 173 } 174 } 175 176 return result; 177 } else { 178 return *(cast(T*) _array.ptr); 179 } 180 } 181 182 uint sizeInBytes(ulong val) pure @nogc nothrow { 183 foreach(n;0 .. cast(uint)ulong.sizeof) { 184 if (!(val >>= 8)) { 185 return n+1; 186 } else { 187 continue; 188 } 189 } 190 assert(0); 191 } 192 import std.range : isRandomAccessRange, ElementType; 193 194 struct SkipArray(T) if (isRandomAccessRange!(ElementType!T)) { 195 const(ElementType!T)[] arrays; 196 size_t _length; 197 198 @property const(size_t) length() const pure { 199 return cast(const) _length; 200 } 201 202 auto opOpAssign (string op)(const T rhs) { 203 static if (op == "~") { 204 arrays ~= rhs; 205 _length += rhs.length; 206 } else { 207 assert(0, "Operator " ~ op ~ " not supported"); 208 } 209 } 210 211 const auto opIndex(const size_t idx) { 212 assert(idx < length); 213 size_t currentPos; 214 foreach(ref a;arrays) { 215 if (idx >= a.length + currentPos) { 216 currentPos += a.length; 217 } else { 218 return a[idx - currentPos]; 219 } 220 } 221 assert(0, "invalid idx"); 222 } 223 224 this(T arrays) { 225 this.arrays = arrays; 226 foreach(a;arrays) { 227 _length += a.length; 228 } 229 } 230 } 231 232 auto skipArray(T)(T t) { 233 return SkipArray!T(t); 234 } 235 236 static immutable intArrArr = skipArray([[1,2],[3],[4,5,6],[7,8,9]]); 237 static assert(intArrArr.length == 9); 238 static assert(intArrArr[3] == 4); 239 static assert(intArrArr[8] == 9); 240 static assert(intArrArr[0] == 1); 241 242 unittest { 243 auto arr = skipArray([["Hello"]]); 244 arr ~= [["beautiful"], ["world"]]; 245 assert(arr.length == 3); 246 247 } 248 249 //TODO implement this! 250 double float64(const ubyte[] _bytes) { 251 assert(_bytes.length > double.sizeof); 252 enum bias = 1023; 253 enum mantissa_length = 53; 254 enum exponent_length = 11; 255 assert(mantissa_length + exponent_length == 64); 256 double result; 257 258 foreach(i; 0 .. (mantissa_length / 8) + 1) { 259 260 } 261 262 return result; 263 }