1 module binrange; 2 3 import std.range, 4 std.traits; 5 6 7 public 8 { 9 void skipBytes(R)(ref R input, int numBytes) if (isInputRange!R) 10 { 11 for (int i = 0; i < numBytes; ++i) 12 popUbyte(input); 13 } 14 15 // Reads a big endian integer from input. 16 T popBE(T, R)(ref R input) if (isInputRange!R) 17 { 18 return popFunction!(T, R, false)(input); 19 } 20 21 // Reads a little endian integer from input. 22 T popLE(T, R)(ref R input) if (isInputRange!R) 23 { 24 return popFunction!(T, R, true)(input); 25 } 26 27 /// Writes a big endian integer/float to output. 28 void writeBE(T, R)(ref R output, T n) if (isOutputRange!(R, ubyte)) 29 { 30 writeFunction!(T, R, false)(output, n); 31 } 32 33 /// Writes a little endian integer/float to output. 34 void writeLE(T, R)(ref R output, T n) if (isOutputRange!(R, ubyte)) 35 { 36 writeFunction!(T, R, true)(output, n); 37 } 38 39 /// Returns: A RIFF chunk header parsed from an input range. 40 void readRIFFChunkHeader(R)(ref R input, out uint chunkId, out uint chunkSize) if (isInputRange!R) 41 { 42 chunkId = popBE!uint(input); 43 chunkSize = popLE!uint(input); 44 } 45 46 /// Writes a RIFF chunk header to an output range. 47 void writeRIFFChunkHeader(R)(ref R output, uint chunkId, uint chunkSize) if (isOutputRange!(R, ubyte)) 48 { 49 writeBE!uint(output, chunkId); 50 writeLE!uint(output, chunkSize); 51 } 52 53 /// Returns: A RIFF chunk id. 54 template RIFFChunkId(string id) 55 { 56 static assert(id.length == 4); 57 uint RIFFChunkId = (cast(ubyte)(id[0]) << 24) 58 | (cast(ubyte)(id[1]) << 16) 59 | (cast(ubyte)(id[2]) << 8) 60 | (cast(ubyte)(id[3])); 61 } 62 63 } 64 65 private 66 { 67 // read/write 64-bits float 68 union float_uint 69 { 70 float f; 71 uint i; 72 } 73 74 // read/write 64-bits float 75 union double_ulong 76 { 77 double f; 78 ulong i; 79 } 80 81 uint float2uint(float x) pure nothrow 82 { 83 float_uint fi; 84 fi.f = x; 85 return fi.i; 86 } 87 88 float uint2float(int x) pure nothrow 89 { 90 float_uint fi; 91 fi.i = x; 92 return fi.f; 93 } 94 95 ulong double2ulong(double x) pure nothrow 96 { 97 double_ulong fi; 98 fi.f = x; 99 return fi.i; 100 } 101 102 double ulong2double(ulong x) pure nothrow 103 { 104 double_ulong fi; 105 fi.i = x; 106 return fi.f; 107 } 108 109 private template IntegerLargerThan(int numBytes) if (numBytes >= 1 && numBytes <= 8) 110 { 111 static if (numBytes == 1) 112 alias IntegerLargerThan = ubyte; 113 else static if (numBytes == 2) 114 alias IntegerLargerThan = ushort; 115 else static if (numBytes <= 4) 116 alias IntegerLargerThan = uint; 117 else 118 alias IntegerLargerThan = ulong; 119 } 120 121 ubyte popUbyte(R)(ref R input) if (isInputRange!R) 122 { 123 if (input.empty) 124 throw new Exception("Expected a byte, but found end of input"); 125 126 ubyte b = input.front; 127 input.popFront(); 128 return b; 129 } 130 131 // Generic integer parsing 132 auto popInteger(R, int NumBytes, bool WantSigned, bool LittleEndian)(ref R input) if (isInputRange!R) 133 { 134 alias T = IntegerLargerThan!NumBytes; 135 136 T result = 0; 137 138 static if (LittleEndian) 139 { 140 for (int i = 0; i < NumBytes; ++i) 141 result |= ( cast(T)(popUbyte(input)) << (8 * i) ); 142 } 143 else 144 { 145 for (int i = 0; i < NumBytes; ++i) 146 result = cast(T)(result << 8) | popUbyte(input); 147 } 148 149 static if (WantSigned) 150 return cast(Signed!T)result; 151 else 152 return result; 153 } 154 155 // Generic integer writing 156 void writeInteger(R, int NumBytes, bool LittleEndian)(ref R output, IntegerLargerThan!NumBytes n) if (isOutputRange!(R, ubyte)) 157 { 158 alias T = IntegerLargerThan!NumBytes; 159 160 auto u = cast(Unsigned!T)n; 161 162 static if (LittleEndian) 163 { 164 for (int i = 0; i < NumBytes; ++i) 165 { 166 ubyte b = (u >> (i * 8)) & 255; 167 output.put(b); 168 } 169 } 170 else 171 { 172 for (int i = 0; i < NumBytes; ++i) 173 { 174 ubyte b = (u >> ( (NumBytes - 1 - i) * 8) ) & 255; 175 output.put(b); 176 } 177 } 178 } 179 180 void writeFunction(T, R, bool endian)(ref R output, T n) if (isOutputRange!(R, ubyte)) 181 { 182 static if (isIntegral!T) 183 writeInteger!(R, T.sizeof, endian)(output, n); 184 else static if (is(T : float)) 185 writeInteger!(R, 4, endian)(output, float2uint(n)); 186 else static if (is(T : double)) 187 writeInteger!(R, 8, endian)(output, double2ulong(n)); 188 else 189 static assert(false, "Unsupported type " ~ T.stringof); 190 } 191 192 T popFunction(T, R, bool endian)(ref R input) if (isInputRange!R) 193 { 194 static if(isIntegral!T) 195 return popInteger!(R, T.sizeof, isSigned!T, endian)(input); 196 else static if (is(T == float)) 197 return uint2float(popInteger!(R, 4, false, endian)(input)); 198 else static if (is(T == double)) 199 return ulong2double(popInteger!(R, 8, false, endian)(input)); 200 else 201 static assert(false, "Unsupported type " ~ T.stringof); 202 } 203 } 204 205 unittest 206 { 207 ubyte[] arr = [ 0x00, 0x01, 0x02, 0x03 , 208 0x00, 0x01, 0x02, 0x03, 209 0x04, 0x05 ]; 210 211 assert(popLE!uint(arr) == 0x03020100); 212 assert(popBE!int(arr) == 0x00010203); 213 assert(popBE!ushort(arr) == 0x0405); 214 215 import std.array; 216 ubyte[] arr2; 217 auto app = appender(arr2); 218 writeBE!float(app, 1.0f); 219 writeLE!double(app, 2.0); 220 writeLE!short(app, 2); 221 } 222 223 224 unittest 225 { 226 ubyte[] arr = [0, 0, 0, 0, 0, 0, 0xe0, 0x3f]; 227 assert(popLE!double(arr) == 0.5); 228 arr = [0, 0, 0, 0, 0, 0, 0xe0, 0xbf]; 229 assert(popLE!double(arr) == -0.5); 230 }