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 }