modlib/binary.lua

150 lines
3.7 KiB
Lua
Raw Permalink Normal View History

-- Localize globals
2021-07-07 12:52:37 +02:00
local assert, math_huge, math_frexp, math_floor
= assert, math.huge, math.frexp, math.floor
2021-08-09 13:58:10 +02:00
local positive_nan, negative_nan = modlib.math.positive_nan, modlib.math.negative_nan
2021-06-17 19:45:08 +02:00
-- Set environment
local _ENV = {}
setfenv(1, _ENV)
-- All little endian
--+ Reads doubles (f64) or floats (f32)
--: double reads an f64 if true, f32 otherwise
function read_float(read_byte, double)
2021-03-27 20:10:49 +01:00
-- First read the mantissa
local mantissa = 0
for _ = 1, double and 6 or 2 do
mantissa = (mantissa + read_byte()) / 0x100
end
-- Second and first byte in big endian: last bit of exponent + 7 bits of mantissa, sign bit + 7 bits of exponent
local byte_2, byte_1 = read_byte(), read_byte()
local sign = 1
if byte_1 >= 0x80 then
sign = -1
byte_1 = byte_1 - 0x80
end
local exponent = byte_1 * 2
if byte_2 >= 0x80 then
exponent = exponent + 1
byte_2 = byte_2 - 0x80
end
mantissa = (mantissa + byte_2) / 0x80
if exponent == 0xFF then
if mantissa == 0 then
2021-07-07 12:52:37 +02:00
return sign * math_huge
2021-03-27 20:10:49 +01:00
end
-- Differentiating quiet and signalling nan is not possible in Lua, hence we don't have to do it
2021-08-09 13:58:10 +02:00
return sign == 1 and positive_nan or negative_nan
2021-03-27 20:10:49 +01:00
end
assert(mantissa < 1)
if exponent == 0 then
-- subnormal value
return sign * 2^-126 * mantissa
end
return sign * 2 ^ (exponent - 127) * (1 + mantissa)
end
--+ Reads a single floating point number (f32)
function read_single(read_byte)
2021-03-27 20:10:49 +01:00
return read_float(read_byte)
end
--+ Reads a double (f64)
function read_double(read_byte)
2021-03-27 20:10:49 +01:00
return read_float(read_byte, true)
end
function read_uint(read_byte, bytes)
2021-03-27 20:10:49 +01:00
local factor = 1
local uint = 0
for _ = 1, bytes do
uint = uint + read_byte() * factor
factor = factor * 0x100
end
return uint
end
2021-10-10 18:14:35 +02:00
function read_int(read_byte, bytes)
local uint = read_uint(read_byte, bytes)
local max = 0x100 ^ bytes
if uint >= max / 2 then
return uint - max
end
return uint
end
function write_uint(write_byte, uint, bytes)
2021-03-27 20:10:49 +01:00
for _ = 1, bytes do
write_byte(uint % 0x100)
2021-07-07 12:52:37 +02:00
uint = math_floor(uint / 0x100)
2021-03-27 20:10:49 +01:00
end
assert(uint == 0)
end
2021-10-10 18:14:35 +02:00
function write_int(write_byte, int, bytes)
2022-06-08 09:48:10 +02:00
local max = 0x100 ^ bytes
2021-10-10 18:14:35 +02:00
if int < 0 then
2022-06-08 09:48:10 +02:00
assert(-int <= max / 2)
int = max + int
2021-10-10 18:14:35 +02:00
else
2022-06-08 09:48:10 +02:00
assert(int < max / 2)
2021-10-10 18:14:35 +02:00
end
return write_uint(write_byte, int, bytes)
end
--: on_write function(double)
--: double set to true to force f64, false for f32, nil for auto
function write_float(write_byte, number, on_write, double)
2021-03-27 20:10:49 +01:00
local sign = 0
if number < 0 then
number = -number
sign = 0x80
end
2021-07-07 12:52:37 +02:00
local mantissa, exponent = math_frexp(number)
2021-03-27 20:10:49 +01:00
exponent = exponent + 127
2022-06-08 10:16:47 +02:00
if number == 0 then exponent = 0 end -- zero must be written as a subnormal number
2021-03-27 20:10:49 +01:00
if exponent > 1 then
-- TODO ensure this deals properly with subnormal numbers
mantissa = mantissa * 2 - 1
exponent = exponent - 1
2022-06-08 10:16:47 +02:00
elseif exponent < 0 then -- number is currently sub-subnormal, subnormalize
mantissa = mantissa * 2^(exponent-1)
exponent = 0
2021-03-27 20:10:49 +01:00
end
2021-07-07 12:52:37 +02:00
local sign_byte = sign + math_floor(exponent / 2)
2021-03-27 20:10:49 +01:00
if double == nil then
double = mantissa % 2^-23 ~= 0
2021-03-27 20:10:49 +01:00
end
if on_write then
on_write(double)
end
mantissa = mantissa * 0x80
local exponent_byte = (exponent % 2) * 0x80 + math_floor(mantissa)
mantissa = mantissa % 1
local mantissa_bytes = {}
2021-03-27 20:10:49 +01:00
local len = double and 6 or 2
for index = 1, len do
2021-03-27 20:10:49 +01:00
mantissa = mantissa * 0x100
2021-07-07 12:52:37 +02:00
mantissa_bytes[index] = math_floor(mantissa)
2021-03-27 20:10:49 +01:00
mantissa = mantissa % 1
end
assert(mantissa == 0) -- no truncation allowed; round your numbers properly or use auto
for index = len, 1, -1 do
2021-03-27 20:10:49 +01:00
write_byte(mantissa_bytes[index])
end
write_byte(exponent_byte)
write_byte(sign_byte)
end
function write_single(write_byte, number)
2021-03-27 20:10:49 +01:00
return write_float(write_byte, number, nil, false)
end
function write_double(write_byte, number)
2021-03-27 20:10:49 +01:00
return write_float(write_byte, number, nil, true)
2021-06-17 19:45:08 +02:00
end
-- Export environment
return _ENV