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