From 48fefb03cb985243e1369c1209557a77e4a754cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konrad=20Ku=C5=9Bnierz?= Date: Tue, 19 May 2015 13:13:14 +0200 Subject: [PATCH] Add float and double support for struct @edubart suggested it would be still better to have it done within C as extra module (just like lbitlib). "f" a float (4 bytes). "d" a double (8 bytes). http://en.wikipedia.org/wiki/IEEE_floating_point --- modules/corelib/struct.lua | 100 +++++++++++++++++++++++++++++++------ 1 file changed, 85 insertions(+), 15 deletions(-) diff --git a/modules/corelib/struct.lua b/modules/corelib/struct.lua index 6bc511b4..44fcf56b 100644 --- a/modules/corelib/struct.lua +++ b/modules/corelib/struct.lua @@ -1,7 +1,7 @@ Struct = {} function Struct.pack(format, ...) - local stream = '' + local stream = {} local vars = {...} local endianness = true @@ -11,35 +11,78 @@ function Struct.pack(format, ...) if opt == '<' or opt == '>' then endianness = opt == '<' and true or false elseif opt == 'b' or opt == 'B' or opt == 'h' or opt == 'H' or opt == 'i' or opt == 'I' or opt == 'l' or opt == 'L' then - local val = tonumber(table.remove(vars, 1)) local n = ((opt == 'h' or opt == 'H') and 2) or ((opt == 'i' or opt == 'I') and 4) or ((opt == 'l' or opt == 'L') and 8) or 1 + local val = tonumber(table.remove(vars, 1)) if val < 0 then val = val + 2 ^ (n * 8 - 1) end - local binary = '' + local bytes = {} for j = 1, n do - binary = binary .. string.char(val % (2 ^ 8)) + table.insert(bytes, string.char(val % (2 ^ 8))) val = math.floor(val / (2 ^ 8)) end if not endianness then - binary = string.reverse(binary) + table.insert(stream, string.reverse(table.concat(bytes))) + else + table.insert(stream, table.concat(bytes)) + end + elseif opt == 'f' or opt == 'd' then + local val = tonumber(table.remove(vars, 1)) + local sign = 0 + + if val < 0 then + sign = 1 + val = -val + end + + local mantissa, exponent = math.frexp(val) + if val == 0 then + mantissa = 0 + exponent = 0 + else + mantissa = (mantissa * 2 - 1) * math.ldexp(0.5, (opt == 'd') and 53 or 24) + exponent = exponent + ((opt == 'd') and 1022 or 126) + end + + local bytes = {} + if opt == 'd' then + val = mantissa + for i = 1, 6 do + table.insert(bytes, string.char(math.floor(val) % (2 ^ 8))) + val = math.floor(val / (2 ^ 8)) + end + else + table.insert(bytes, string.char(math.floor(mantissa) % (2 ^ 8))) + val = math.floor(mantissa / (2 ^ 8)) + table.insert(bytes, string.char(math.floor(val) % (2 ^ 8))) + val = math.floor(val / (2 ^ 8)) end - stream = stream .. binary + table.insert(bytes, string.char(math.floor(exponent * ((opt == 'd') and 16 or 128) + val) % (2 ^ 8))) + val = math.floor((exponent * ((opt == 'd') and 16 or 128) + val) / (2 ^ 8)) + table.insert(bytes, string.char(math.floor(sign * 128 + val) % (2 ^ 8))) + val = math.floor((sign * 128 + val) / (2 ^ 8)) + + if not endianness then + table.insert(stream, string.reverse(table.concat(bytes))) + else + table.insert(stream, table.concat(bytes)) + end elseif opt == 's' then - stream = stream .. tostring(table.remove(vars, 1)) - stream = stream .. string.char(0) + table.insert(stream, tostring(table.remove(vars, 1))) + table.insert(stream, string.char(0)) end end - return stream + return table.concat(stream) end function Struct.unpack(format, stream) local vars = {} + local iterator = 1 local endianness = true for i = 1, string.len(format) do @@ -53,13 +96,13 @@ function Struct.unpack(format, stream) local val = 0 for j = 1, n do - local byte = string.byte(string.sub(stream, 1, 1)) + local byte = string.byte(string.sub(stream, iterator, iterator)) if endianness then val = val + byte * (2 ^ ((j - 1) * 8)) else val = val + byte * (2 ^ ((n - j) * 8)) end - stream = string.sub(stream, 2) + iterator = iterator + 1 end if signed then @@ -67,17 +110,44 @@ function Struct.unpack(format, stream) end table.insert(vars, val) + elseif opt == 'f' or opt == 'd' then + local n = (opt == 'd') and 8 or 4 + local x = string.sub(stream, iterator, iterator + n - 1) + iterator = iterator + n + + if not endianness then + x = string.reverse(x) + end + + local sign = 1 + local mantissa = string.byte(x, (opt == 'd') and 7 or 3) % ((opt == 'd') and 16 or 128) + for i = n - 2, 1, -1 do + mantissa = mantissa * (2 ^ 8) + string.byte(x, i) + end + + if string.byte(x, n) > 127 then + sign = -1 + end + + local exponent = (string.byte(x, n) % 128) * ((opt == 'd') and 16 or 2) + math.floor(string.byte(x, n - 1) / ((opt == 'd') and 16 or 128)) + if exponent == 0 then + table.insert(vars, 0.0) + else + mantissa = (math.ldexp(mantissa, (opt == 'd') and -52 or -23) + 1) * sign + table.insert(vars, math.ldexp(mantissa, exponent - ((opt == 'd') and 1023 or 127))) + end elseif opt == 's' then - local str = '' - for j = 1, string.len(stream) do + local bytes = {} + for j = 1 + iterator, string.len(stream) do if string.sub(stream, j, j) == string.char(0) then break end - str = str .. string.sub(stream, j, j) + table.insert(bytes, string.sub(stream, j, j)) end - stream = string.sub(stream, string.len(str) + 2) + local str = table.concat(bytes) + iterator = iterator + string.len(str) + 1 table.insert(vars, str) end end