You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

109 lines
3.0 KiB
Lua

local base = string.match(..., "(.-)[^%.]+%.[^%.]+$")
local bit = require("bit")
local eh = require(base.."helper.enum")
local ffi = require("ffi")
local mth = require(base.."helper.mt")
local ceil, error = math.ceil, error
local table_concat = table.concat
local enum_to_int = eh.to_int
local band, bnot, bor, lshift, rshift = bit.band, bit.bnot, bit.bor, bit.lshift, bit.rshift
local ffi_cast, ffi_fill, ffi_sizeof = ffi.cast, ffi.fill, ffi.sizeof
local ulong_ary = ffi.typeof("unsigned long[?]")
local ulongp = ffi.typeof("unsigned long*")
local elem_bytes = ffi.sizeof("long")
local elem_bits = elem_bytes * 8
local mt, index = {}, {}
local function array_size(len)
return ceil(len / elem_bits)
end
index.array_size = array_size
function index:clear()
ffi_fill(self._buf, self.bytes)
return self
end
function index:fill()
ffi_fill(self._buf, self.bytes, 0xff)
return self
end
function index:get(i)
i = enum_to_int(self._enum, i)
if i < 0 or i >= self.length then return false end
return band(rshift(self._buf[i / elem_bits], i % elem_bits), 1) ~= 0
end
mt.base_getter = index.get
function index:set(i, val)
i = enum_to_int(self._enum, i)
if i < 0 or i >= self.length then error("Index out of range") end
local idx = i / elem_bits
local bit = lshift(1ULL, i % elem_bits)
local buf = self._buf
if val then
buf[idx] = bor(buf[idx], bit)
else
buf[idx] = band(buf[idx], bnot(bit))
end
return self
end
mt.base_setter = index.set
-- poor man's macro processor
local function iter_func(shit)
local str = [[
local enum_to_int, bit, elem_bits = ...
local band, rshift = bit.band, bit.rshift
local function func(self, i)
i = enum_to_int(self._enum, i)
while true do
i = i+1
if i >= self.length then return end
local name = self._enum[i]
local val = band(rshift(self._buf[i / elem_bits], i % elem_bits), 1) ~= 0
<shit>
end
end
return function(self) return func, self, -1 end
]]
return assert(loadstring(str:gsub("<shit>", shit)))(enum_to_int, bit, elem_bits)
end
index.int_pairs = iter_func("if name or val then return i, val end")
index.str_pairs = iter_func("if name or val then return name or i, val end")
index.i_members = iter_func("if val then return i end")
index.str_members = iter_func("if val then return name or i end")
function index:tostring()
local res = {}
for m in self:str_members() do res[#res+1] = m end
local raw = {}
for i=1,ceil(self.length / elem_bits) do
raw[i] = string.format("%#0x", self._buf[i-1])
end
return "["..table_concat(res, ", ").."] ("..table.concat(raw, ", ")..")"
end
mt.__tostring = index.tostring
local function from_buf(enum, buf, len)
buf = ffi_cast(ulongp, buf)
local bs = elem_bytes * ceil(len / elem_bits)
return setmetatable({_enum=enum, _buf=buf, length=len, bytes=bs}, mt)
end
index.from_buf = from_buf
function index.new_for_ioctl(enum, len)
local buf = ulong_ary(array_size(len))
local x = from_buf(enum, buf, len)
return x, buf, ffi_sizeof(buf)
end
mth.add_index_methods(mt, index)
return setmetatable({}, mt)