128 lines
2.7 KiB
Lua
Executable File

-- write formats
-- return true if they have consumed a value from the input stream
-- return false/nil otherwise (ie, the next value will be preserved
-- for subsequent calls, eg skip/pad)
-- Copyright © 2008 Ben "ToxicFrog" Kelly; see COPYING
local require,error,setmetatable,string,print,math,unpack,io
= require,error,setmetatable,string,print,math,unpack,io
module((...))
local struct = require (_PACKAGE:sub(1,-2))
local common = require (_PACKAGE.."common")
local fp = require (_PACKAGE.."fp")
local pack = setmetatable({}, { __index = common })
-- boolean
function pack.b(fd, d, w)
return pack.u(fd, (d and 1) or 0, w)
end
-- counted string
-- a string immediately prefaced with its length as a uint
function pack.c(fd, d, w)
pack.u(fd, #d, w)
return pack.s(fd, d)
end
-- floating point
function pack.f(fd, d, w)
if not fp.w[w] then
error("struct.pack: illegal floating point width")
end
-- local f = fp.w[w](d)
-- print(f, type(f))
return pack.s(fd, fp.w[w](d), w)
end
-- signed int
function pack.i(fd, d, w)
if d < 0 then
d = 2^(w*8) + d
end
return pack.u(fd, d, w)
end
-- bitmask
-- we use a string here because using an unsigned will lose data on bitmasks
-- wider than lua's native number format
function pack.m(fd, d, w)
local buf = ""
for i=1,w*8,8 do
local bits = { unpack(d, i, i+7) }
local byte = string.char(struct.implode(bits, 8))
if pack.is_bigendian then
buf = byte..buf
else
buf = buf..byte
end
end
return pack.s(fd, buf, w)
end
-- fixed point bit aligned
function pack.P(fd, d, dp, fp)
if (dp+fp) % 8 ~= 0 then
error "total width of fixed point value must be byte multiple"
end
return pack.i(fd, d * 2^fp, (dp+fp)/8)
end
-- fixed point byte aligned
function pack.p(fd, d, dp, fp)
return pack.P(fd, d, dp*8, fp*8)
end
-- fixed length string
-- length 0 is write string as is
-- length >0 is write exactly w bytes, truncating or padding as needed
function pack.s(fd, d, w)
w = w or #d
if w == 0 then return end
if #d < w then
d = d..string.char(0):rep(w-#d)
end
return fd:write(d:sub(1,w))
end
-- unsigned int
function pack.u(fd, d, w)
local s = ""
for i=1,w do
if pack.is_bigendian then
s = string.char(d % 2^8) .. s
else
s = s .. string.char(d % 2^8)
end
d = math.trunc(d/2^8)
end
return pack.s(fd, s, w)
end
-- skip/pad
-- this is technically a control format, so it has a different signature
-- specifically, there is no "data" argument
function pack.x(fd, w)
return pack.s(fd, "", w)
end
-- null terminated string
-- w==nil is write string as is + termination
-- w>0 is write exactly w bytes, truncating/padding and terminating
function pack.z(fd, d, w)
w = w or #d+1
if #d >= w then
d = d:sub(1, w-1)
end
return pack.s(fd, d.."\0", w)
end
return pack