1 /**
2 Parsing helpers.
3 
4 Copyright: Guillaume Piolat 2018-2024.
5 License:   $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
6 */
7 module printed.font.binrange;
8 
9 import std.range.primitives;
10 import std.traits;
11 
12 
13 public
14 {
15     void skipBytes(R)(ref R input, int numBytes) if (isInputRange!R)
16     {
17         for (int i = 0; i < numBytes; ++i)
18             popUbyte(input);
19     }
20 
21     // Reads a big endian integer from input.
22     T popBE(T, R)(ref R input) if (isInputRange!R)
23     {
24         return popFunction!(T, R, false)(input);
25     }
26 
27     // Reads a little endian integer from input.
28     T popLE(T, R)(ref R input) if (isInputRange!R)
29     {
30         return popFunction!(T, R, true)(input);
31     }
32 }
33 
34 private
35 {
36     // read/write 64-bits float
37     union float_uint
38     {
39         float f;
40         uint i;
41     }
42 
43     // read/write 64-bits float
44     union double_ulong
45     {
46         double f;
47         ulong i;
48     }
49 
50     uint float2uint(float x) pure nothrow
51     {
52         float_uint fi;
53         fi.f = x;
54         return fi.i;
55     }
56 
57     float uint2float(int x) pure nothrow
58     {
59         float_uint fi;
60         fi.i = x;
61         return fi.f;
62     }
63 
64     ulong double2ulong(double x) pure nothrow
65     {
66         double_ulong fi;
67         fi.f = x;
68         return fi.i;
69     }
70 
71     double ulong2double(ulong x) pure nothrow
72     {
73         double_ulong fi;
74         fi.i = x;
75         return fi.f;
76     }
77 
78     private template IntegerLargerThan(int numBytes) if (numBytes >= 1 && numBytes <= 8)
79     {
80         static if (numBytes == 1)
81             alias IntegerLargerThan = ubyte;
82         else static if (numBytes == 2)
83             alias IntegerLargerThan = ushort;
84         else static if (numBytes <= 4)
85             alias IntegerLargerThan = uint;
86         else
87             alias IntegerLargerThan = ulong;
88     }
89 
90     ubyte popUbyte(R)(ref R input) if (isInputRange!R)
91     {
92         if (input.empty)
93             throw new Exception("Expected a byte, but found end of input");
94 
95         ubyte b = input.front;
96         input.popFront();
97         return b;
98     }
99 
100     // Generic integer parsing
101     auto popInteger(R, int NumBytes, bool WantSigned, bool LittleEndian)(ref R input) if (isInputRange!R)
102     {
103         alias T = IntegerLargerThan!NumBytes;
104 
105         T result = 0;
106 
107         static if (LittleEndian)
108         {
109             for (int i = 0; i < NumBytes; ++i)
110                 result |= ( cast(T)(popUbyte(input)) << (8 * i) );
111         }
112         else
113         {
114             for (int i = 0; i < NumBytes; ++i)
115                 result = cast(T)(result << 8) | popUbyte(input);
116         }
117 
118         static if (WantSigned)
119             return cast(Signed!T)result;
120         else
121             return result;
122     }
123 
124     T popFunction(T, R, bool endian)(ref R input) if (isInputRange!R)
125     {
126         static if(isIntegral!T)
127             return popInteger!(R, T.sizeof, isSigned!T, endian)(input);
128         else static if (is(T == float))
129             return uint2float(popInteger!(R, 4, false, endian)(input));
130         else static if (is(T == double))
131             return ulong2double(popInteger!(R, 8, false, endian)(input));
132         else
133             static assert(false, "Unsupported type " ~ T.stringof);
134     }
135 }
136 
137 unittest
138 {
139     ubyte[] arr = [ 0x00, 0x01, 0x02, 0x03 ,
140                     0x00, 0x01, 0x02, 0x03,
141                     0x04, 0x05 ];
142 
143     assert(popLE!uint(arr) == 0x03020100);
144     assert(popBE!int(arr) == 0x00010203);
145     assert(popBE!ushort(arr) == 0x0405);
146 }
147 
148 
149 unittest
150 {
151     ubyte[] arr = [0, 0, 0, 0, 0, 0, 0xe0, 0x3f];
152     assert(popLE!double(arr) == 0.5);
153     arr = [0, 0, 0, 0, 0, 0, 0xe0, 0xbf];
154     assert(popLE!double(arr) == -0.5);
155 }