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 }