1 /** 2 * Binary tooling 3 * 4 * Authors: Tristan Brice Velloza Kildaire (deavmi) 5 */ 6 module niknaks.bits; 7 8 version(unittest) 9 { 10 import std.stdio : writeln; 11 } 12 13 /** 14 * Flips the given integral value 15 * 16 * Params: 17 * bytesIn = the integral value 18 * Returns: the flipped integral 19 */ 20 public T flip(T)(T bytesIn) if(__traits(isIntegral, T)) 21 { 22 T copy = bytesIn; 23 24 ubyte* base = (cast(ubyte*)&bytesIn)+T.sizeof-1; 25 ubyte* baseCopy = cast(ubyte*)© 26 27 for(ulong idx = 0; idx < T.sizeof; idx++) 28 { 29 *(baseCopy+idx) = *(base-idx); 30 } 31 32 return copy; 33 } 34 35 /** 36 * Tests the `flip!(T)(T)` function 37 */ 38 unittest 39 { 40 version(BigEndian) 41 { 42 ushort i = 1; 43 ushort flipped = flip(i); 44 assert(flipped == 256); 45 } 46 else version(LittleEndian) 47 { 48 ushort i = 1; 49 ushort flipped = flip(i); 50 assert(flipped == 256); 51 } 52 } 53 54 /** 55 * Ordering 56 */ 57 public enum Order 58 { 59 /** 60 * Little endian 61 */ 62 LE, 63 64 /** 65 * Big endian 66 */ 67 BE 68 } 69 70 /** 71 * Swaps the bytes to the given ordering but does a no-op 72 * if the ordering requested is the same as that of the 73 * system's 74 * 75 * Params: 76 * bytesIn = the integral value to swap 77 * order = the byte ordering to request 78 * Returns: the integral value 79 */ 80 public T order(T)(T bytesIn, Order order) if(__traits(isIntegral, T)) 81 { 82 version(LittleEndian) 83 { 84 if(order == Order.LE) 85 { 86 return bytesIn; 87 } 88 else 89 { 90 return flip(bytesIn); 91 } 92 } 93 else version(BigEndian) 94 { 95 if(order == Order.BE) 96 { 97 return bytesIn; 98 } 99 else 100 { 101 return flip(bytesIn); 102 } 103 } 104 } 105 106 /** 107 * Tests the `order!(T)(T, Order)` 108 * 109 * To Big Endian testing 110 */ 111 unittest 112 { 113 version(LittleEndian) 114 { 115 ushort i = 1; 116 writeln("Pre-order: ", i); 117 ushort ordered = order(i, Order.BE); 118 writeln("Post-order: ", ordered); 119 assert(ordered == 256); 120 } 121 else version(BigEndian) 122 { 123 ushort i = 1; 124 writeln("Pre-order: ", i); 125 ushort ordered = order(i, Order.BE); 126 writeln("Post-order: ", ordered); 127 assert(ordered == i); 128 } 129 } 130 131 /** 132 * Tests the `order!(T)(T, Order)` 133 * 134 * To Little Endian testing 135 */ 136 unittest 137 { 138 version(LittleEndian) 139 { 140 ushort i = 1; 141 writeln("Pre-order: ", i); 142 ushort ordered = order(i, Order.LE); 143 writeln("Post-order: ", ordered); 144 assert(ordered == i); 145 } 146 else version(BigEndian) 147 { 148 ushort i = 1; 149 writeln("Pre-order: ", i); 150 ushort ordered = order(i, Order.LE); 151 writeln("Post-order: ", ordered); 152 assert(ordered == 256); 153 } 154 } 155 156 /** 157 * Converts the given integral value to 158 * its byte encoding 159 * 160 * Params: 161 * integral = the integral value 162 * Returns: a `ubyte[]` of the value 163 */ 164 public ubyte[] toBytes(T)(T integral) if(__traits(isIntegral, T)) 165 { 166 ubyte[] bytes; 167 168 static if(integral.sizeof == 1) 169 { 170 bytes = [cast(ubyte)integral]; 171 } 172 else static if(integral.sizeof == 2) 173 { 174 ubyte* ptrBase = cast(ubyte*)&integral; 175 bytes = [*ptrBase, *(ptrBase+1)]; 176 } 177 else static if(integral.sizeof == 4) 178 { 179 ubyte* ptrBase = cast(ubyte*)&integral; 180 bytes = [*ptrBase, *(ptrBase+1), *(ptrBase+2), *(ptrBase+3)]; 181 } 182 else static if(integral.sizeof == 8) 183 { 184 ubyte* ptrBase = cast(ubyte*)&integral; 185 bytes = [*ptrBase, *(ptrBase+1), *(ptrBase+2), *(ptrBase+3), *(ptrBase+4), *(ptrBase+5), *(ptrBase+6), *(ptrBase+7)]; 186 } 187 else 188 { 189 pragma(msg, "toBytes cannot support integral types greater than 8 bytes"); 190 static assert(false); 191 } 192 193 return bytes; 194 } 195 196 /** 197 * Tests the `toBytes!(T)(T)` function 198 */ 199 unittest 200 { 201 version(LittleEndian) 202 { 203 ulong value = 1; 204 ubyte[] bytes = toBytes(value); 205 206 assert(bytes == [1, 0, 0, 0, 0, 0, 0, 0]); 207 } 208 else version(BigEndian) 209 { 210 ulong value = 1; 211 ubyte[] bytes = toBytes(value); 212 213 assert(bytes == [0, 0, 0, 0, 0, 0, 0, 1]); 214 } 215 } 216 217 /** 218 * Takes an array of bytes and dereferences 219 * then to an integral of your choosing 220 * 221 * Params: 222 * T = the integral type to go to 223 * bytes = the bytes to copy 224 * Returns: the integral but `T.init` if 225 * the provided size would cause an overrun 226 * read 227 */ 228 public T bytesToIntegral(T)(ubyte[] bytes) if(__traits(isIntegral, T)) 229 { 230 if(bytes.length >= T.sizeof) 231 { 232 return *cast(T*)bytes.ptr; 233 } 234 else 235 { 236 return T.init; 237 } 238 } 239 240 /** 241 * Tests taking a byte array and then 242 * decoding it into the requested type 243 * by using `bytesToIntegral!(T)(ubyte[])` 244 * 245 * In this case provided bytes match the 246 * given integral to-type 247 */ 248 unittest 249 { 250 version(LittleEndian) 251 { 252 ubyte[] bytes = [1, 0]; 253 ushort to = bytesToIntegral!(ushort)(bytes); 254 assert(to == 1); 255 } 256 else version(BigEndian) 257 { 258 ubyte[] bytes = [1, 0]; 259 ushort to = bytesToIntegral!(ushort)(bytes); 260 assert(to == 256); 261 } 262 } 263 264 /** 265 * Tests taking a byte array and then 266 * decoding it into the requested type 267 * by using `bytesToIntegral!(T)(ubyte[])` 268 * 269 * In this case provided bytes DO NOT 270 * match the given integral to-type 271 * and therefore to-type.init is returned 272 */ 273 unittest 274 { 275 version(LittleEndian) 276 { 277 ubyte[] bytes = [1]; 278 ushort to = bytesToIntegral!(ushort)(bytes); 279 assert(to == ushort.init); 280 } 281 else version(BigEndian) 282 { 283 ubyte[] bytes = [1]; 284 ushort to = bytesToIntegral!(ushort)(bytes); 285 assert(to == ushort.init); 286 } 287 }