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