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
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)
|