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
This commit is contained in:
Konrad Kuśnierz 2015-05-19 13:13:14 +02:00
parent 7ea6c46b2c
commit 48fefb03cb
1 changed files with 85 additions and 15 deletions

View File

@ -1,7 +1,7 @@
Struct = {} Struct = {}
function Struct.pack(format, ...) function Struct.pack(format, ...)
local stream = '' local stream = {}
local vars = {...} local vars = {...}
local endianness = true local endianness = true
@ -11,35 +11,78 @@ function Struct.pack(format, ...)
if opt == '<' or opt == '>' then if opt == '<' or opt == '>' then
endianness = opt == '<' and true or false 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 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 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 if val < 0 then
val = val + 2 ^ (n * 8 - 1) val = val + 2 ^ (n * 8 - 1)
end end
local binary = '' local bytes = {}
for j = 1, n do 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)) val = math.floor(val / (2 ^ 8))
end end
if not endianness then 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 end
stream = stream .. binary 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
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 elseif opt == 's' then
stream = stream .. tostring(table.remove(vars, 1)) table.insert(stream, tostring(table.remove(vars, 1)))
stream = stream .. string.char(0) table.insert(stream, string.char(0))
end end
end end
return stream return table.concat(stream)
end end
function Struct.unpack(format, stream) function Struct.unpack(format, stream)
local vars = {} local vars = {}
local iterator = 1
local endianness = true local endianness = true
for i = 1, string.len(format) do for i = 1, string.len(format) do
@ -53,13 +96,13 @@ function Struct.unpack(format, stream)
local val = 0 local val = 0
for j = 1, n do 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 if endianness then
val = val + byte * (2 ^ ((j - 1) * 8)) val = val + byte * (2 ^ ((j - 1) * 8))
else else
val = val + byte * (2 ^ ((n - j) * 8)) val = val + byte * (2 ^ ((n - j) * 8))
end end
stream = string.sub(stream, 2) iterator = iterator + 1
end end
if signed then if signed then
@ -67,17 +110,44 @@ function Struct.unpack(format, stream)
end end
table.insert(vars, val) 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 elseif opt == 's' then
local str = '' local bytes = {}
for j = 1, string.len(stream) do for j = 1 + iterator, string.len(stream) do
if string.sub(stream, j, j) == string.char(0) then if string.sub(stream, j, j) == string.char(0) then
break break
end end
str = str .. string.sub(stream, j, j) table.insert(bytes, string.sub(stream, j, j))
end end
stream = string.sub(stream, string.len(str) + 2) local str = table.concat(bytes)
iterator = iterator + string.len(str) + 1
table.insert(vars, str) table.insert(vars, str)
end end
end end