1 /*
2  * bitleveld - datatypes.d
3  * by Laszlo Szeremi
4  *
5  * Copyright under Boost Software License.
6  */
7 
8 module bitleveld.datatypes;
9 
10 import std.bitmanip;
11 
12 /**
13  * Used for template initialization.
14  * Reverses the order of data elements whithin a byte if false.
15  */
16 public enum ByteEndianness : bool {
17 	normal	= 	true,
18 	reverse	=	false,
19 }
20 /**
21  * Implements a 4bit nibble array.
22  */
23 public struct NibbleArrayTemplt (bool Endianness = ByteEndianness.normal) {
24 	private ubyte[]	src;
25 	private size_t	_length;
26 
27 	public const size_t length() @nogc nothrow @safe pure @property {
28 		return _length;
29 	}
30 	public size_t length(size_t val) nothrow @safe pure @property {
31 		src.length = val>>>1 + (val & 1);
32 		return _length = val;
33 	}
34 	public size_t reserve(size_t val) nothrow @safe pure {
35 		return src.reserve(val>>>1)<<1;
36 	}
37 	ubyte opIndex (size_t i) @nogc nothrow @safe pure {
38 		static if (Endianness) {
39 			if (i & 1) {
40 				return src[i>>>1]>>>4;
41 			} else {
42 				return src[i>>>1] & 0x0F;
43 			}
44 		} else {
45 			if (i & 1) {
46 				return src[i>>>1] & 0x0F;
47 			} else {
48 				return src[i>>>1]>>>4;
49 			}
50 		}
51 	}
52 	ubyte opIndexAssign (ubyte val, size_t i) @nogc nothrow @safe pure {
53 		val &= 0x0F;
54 		static if (Endianness) {
55 			if (i & 1) {
56 				src[i>>>1] |= val<<4;
57 			} else {
58 				src[i>>>1] |= val & 0x0F;
59 			}
60 		} else {
61 			if (i & 1) {
62 				src[i>>>1] |= val & 0x0F;
63 			} else {
64 				src[i>>>1] |= val<<4;
65 			}
66 		}
67 		return opIndex(i);
68 	}
69 }
70 alias NibbleArray = NibbleArrayTemplt!(ByteEndianness.normal);
71 alias NibbleArrayR = NibbleArrayTemplt!(ByteEndianness.reverse);
72 /**
73  * Implements a 2bit quad array.
74  */
75 public struct QuadArrayTemplt (bool Endianness = ByteEndianness.normal) {
76 	private ubyte[] src;
77 	private size_t	_length;
78 	public const size_t length() @nogc nothrow @safe pure @property {
79 		return _length;
80 	}
81 	public size_t length(size_t val) nothrow @safe pure @property {
82 		src.length = val>>>2 + ((val & 1) | (val>>>1 & 1));
83 		return _length = val;
84 	}
85 	public size_t reserve(size_t val) nothrow @safe pure {
86 		return src.reserve(val>>>1)<<1;
87 	}
88 	ubyte opIndex (size_t i) @nogc nothrow @safe pure {
89 		static if (Endianness) {
90 			switch (i & 3) {
91 				case 1:
92 					return (src[i>>>2] & 0b0000_1100)>>>2;
93 				case 2:
94 					return (src[i>>>2] & 0b0011_0000)>>>4;
95 				case 3:
96 					return src[i>>>2]>>>6;
97 				default:
98 					return src[i>>>2] & 0b0000_0011;
99 			}
100 		} else {
101 			switch (i & 3) {
102 				case 1:
103 					return (src[i>>>2] & 0b0011_0000)>>>4;
104 				case 2:
105 					return (src[i>>>2] & 0b0000_1100)>>>2;
106 				case 3:
107 					return src[i>>>2] & 0b0000_0011;
108 				default:
109 					return src[i>>>2]>>>6;
110 			}
111 		}
112 	}
113 	ubyte opIndexAssign (ubyte val, size_t i) @nogc nothrow @safe pure {
114 		val &= 0b0000_0011;
115 		static if (Endianness) {
116 			switch (i & 3) {
117 				case 1:
118 					src[i>>>2] |= val<<2;
119 					break;
120 				case 2:
121 					src[i>>>2] |= val<<4;
122 					break;
123 				case 3:
124 					src[i>>>2] |= val<<6;
125 					break;
126 				default:
127 					src[i>>>2] |= val;
128 					break;
129 			}
130 		} else {
131 			switch (i & 3) {
132 				case 1:
133 					src[i>>>2] |= val<<4;
134 					break;
135 				case 2:
136 					src[i>>>2] |= val<<2;
137 					break;
138 				case 3:
139 					src[i>>>2] |= val;
140 					break;
141 				default:
142 					src[i>>>2] |= val<<6;
143 					break;
144 			}
145 		}
146 		return opIndex(i);
147 	}
148 }
149 alias QuadArray = QuadArrayTemplt!(ByteEndianness.normal);
150 alias QuadArrayR = QuadArrayTemplt!(ByteEndianness.reverse);
151 
152 unittest {
153 	import std.conv : to;
154 	ubyte[] test = [0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef];
155 	ubyte[] output;
156 	NibbleArray na = NibbleArray(test, 16);
157 	for (int i ; i < na.length ; i++) {
158 		output ~= na[i];
159 	}
160 	assert(output == [1,0,3,2,5,4,7,6,9,8,11,10,13,12,15,14], "Error in result : " ~ to!string(output));
161 	output.length = 0;
162 	NibbleArrayR nar = NibbleArrayR(test, 16);
163 	for (int i ; i < nar.length ; i++) {
164 		output ~= nar[i];
165 	}
166 	assert(output == [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15], "Error in result : " ~ to!string(output));
167 	output.length = 0;
168 	QuadArray qa = QuadArray(test, 32);
169 	for (int i ; i < qa.length ; i++) {
170 		output ~= qa[i];
171 	}
172 	assert(output == [1,0, 0,0, 3,0, 2,0, 1,1, 0,1, 3,1, 2,1, 1,2, 0,2, 3,2, 2,2, 1,3, 0,3, 3,3, 2,3], "Error in result : "
173 			~ to!string(output));
174 	output.length = 0;
175 	QuadArrayR qar = QuadArrayR(test, 32);
176 	for (int i ; i < qar.length ; i++) {
177 		output ~= qar[i];
178 	}
179 	assert(output == [0,0, 0,1, 0,2, 0,3, 1,0, 1,1, 1,2, 1,3, 2,0, 2,1, 2,2, 2,3, 3,0, 3,1, 3,2, 3,3], "Error in result : "
180 			~ to!string(output));
181 }
182 
183 /**
184  * Implements a bitplane reader/writer, that reads/writes multiple bitplanes at once.
185  * Between 1-8 planes, ubyte is used as the return/input type, between 9-16 it's ushort.
186  */
187 public struct Bitplane (int NOfBitplanes) 
188 		if (NOfBitplanes <= 16 && NOfBitplanes > 0) {
189 	private BitArray[NOfBitplanes]		src;
190 	private	size_t						_length;
191 	public const size_t length () @property @nogc nothrow @safe pure {
192 		return length;
193 	}
194 	public size_t length (size_t val) @property nothrow @trusted pure {
195 		for (int i ; i < NOfBitplanes ; i++) {
196 			src[i].length = val;
197 		}
198 		return length = val;
199 	}
200 	static if (NOfBitplanes <= 8) {
201 		ubyte opIndex(size_t i) @nogc pure nothrow {
202 			ubyte result;
203 			for (int b ; b < NOfBitplanes ; b++){
204 				result>>>=1;
205 				result |= src[b][i] ? 1 : 0;
206 			}
207 			return result;
208 		}
209 		ubyte opIndexAssign(ubyte val, size_t i) @nogc pure nothrow {
210 			for (int b ; b < NOfBitplanes ; b++){
211 				src[b][i] = (val&1) == 1;
212 				val>>>=1;
213 			}
214 			return opIndex(i);
215 		}
216 	} else {
217 		ushort opIndex(size_t i) @nogc pure nothrow {
218 			ushort result;
219 			for (int b ; b < NOfBitplanes ; b++){
220 				result>>>=1;
221 				result |= src[b][i] ? 1 : 0;
222 			}
223 			return result;
224 		}
225 		ushort opIndexAssign(ushort val, size_t i) @nogc pure nothrow {
226 			for (int b ; b < NOfBitplanes ; b++){
227 				src[b][i] = (val&1) == 1;
228 				val>>>=1;
229 			}
230 			return opIndex(i);
231 		}
232 	}
233 }
234 unittest{
235 	ubyte[] plane0 = [0x51,0x31,0x05,0x08,0x60,0x00,0x00,0x00];
236 	ubyte[] plane1 = [0x00,0x05,0x00,0x22,0x60,0x00,0x00,0x00];
237 	ubyte[] plane2 = [0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x00];
238 	Bitplane!3 plane = Bitplane!3([BitArray(plane0, 8*8), BitArray(plane1, 8*8), BitArray(plane2, 8*8)], 8*8);
239 }