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 = (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 
210     assert(popLE!uint(arr) == 0x03020100);
211     assert(popBE!int(arr) == 0x00010203);
212 
213     import std.array;
214     ubyte[] arr2;
215     auto app = appender(arr2);
216     writeBE!float(app, 1.0f);
217     writeLE!double(app, 2.0);
218 }
219 
220 
221 unittest
222 {
223     ubyte[] arr = [0, 0, 0, 0, 0, 0, 0xe0, 0x3f];
224     assert(popLE!double(arr) == 0.5);
225     arr = [0, 0, 0, 0, 0, 0, 0xe0, 0xbf];
226     assert(popLE!double(arr) == -0.5);
227 }