bitfield.lua (3069B)
1 local base = string.match(..., "(.-)[^%.]+%.[^%.]+$") 2 local bit = require("bit") 3 local eh = require(base.."helper.enum") 4 local ffi = require("ffi") 5 local mth = require(base.."helper.mt") 6 7 local ceil, error = math.ceil, error 8 local table_concat = table.concat 9 local enum_to_int = eh.to_int 10 local band, bnot, bor, lshift, rshift = bit.band, bit.bnot, bit.bor, bit.lshift, bit.rshift 11 local ffi_cast, ffi_fill, ffi_sizeof = ffi.cast, ffi.fill, ffi.sizeof 12 13 local ulong_ary = ffi.typeof("unsigned long[?]") 14 local ulongp = ffi.typeof("unsigned long*") 15 local elem_bytes = ffi.sizeof("long") 16 local elem_bits = elem_bytes * 8 17 18 local mt, index = {}, {} 19 20 local function array_size(len) 21 return ceil(len / elem_bits) 22 end 23 index.array_size = array_size 24 25 function index:clear() 26 ffi_fill(self._buf, self.bytes) 27 return self 28 end 29 30 function index:fill() 31 ffi_fill(self._buf, self.bytes, 0xff) 32 return self 33 end 34 35 function index:get(i) 36 i = enum_to_int(self._enum, i) 37 if i < 0 or i >= self.length then return false end 38 return band(rshift(self._buf[i / elem_bits], i % elem_bits), 1) ~= 0 39 end 40 mt.base_getter = index.get 41 42 function index:set(i, val) 43 i = enum_to_int(self._enum, i) 44 if i < 0 or i >= self.length then error("Index out of range") end 45 local idx = i / elem_bits 46 local bit = lshift(1ULL, i % elem_bits) 47 local buf = self._buf 48 if val then 49 buf[idx] = bor(buf[idx], bit) 50 else 51 buf[idx] = band(buf[idx], bnot(bit)) 52 end 53 return self 54 end 55 mt.base_setter = index.set 56 57 -- poor man's macro processor 58 local function iter_func(shit) 59 local str = [[ 60 local enum_to_int, bit, elem_bits = ... 61 local band, rshift = bit.band, bit.rshift 62 local function func(self, i) 63 i = enum_to_int(self._enum, i) 64 while true do 65 i = i+1 66 if i >= self.length then return end 67 68 local name = self._enum[i] 69 local val = band(rshift(self._buf[i / elem_bits], i % elem_bits), 1) ~= 0 70 <shit> 71 end 72 end 73 return function(self) return func, self, -1 end 74 ]] 75 return assert(loadstring(str:gsub("<shit>", shit)))(enum_to_int, bit, elem_bits) 76 end 77 78 index.int_pairs = iter_func("if name or val then return i, val end") 79 index.str_pairs = iter_func("if name or val then return name or i, val end") 80 index.i_members = iter_func("if val then return i end") 81 index.str_members = iter_func("if val then return name or i end") 82 83 function index:tostring() 84 local res = {} 85 for m in self:str_members() do res[#res+1] = m end 86 local raw = {} 87 for i=1,ceil(self.length / elem_bits) do 88 raw[i] = string.format("%#0x", self._buf[i-1]) 89 end 90 return "["..table_concat(res, ", ").."] ("..table.concat(raw, ", ")..")" 91 end 92 mt.__tostring = index.tostring 93 94 local function from_buf(enum, buf, len) 95 buf = ffi_cast(ulongp, buf) 96 local bs = elem_bytes * ceil(len / elem_bits) 97 return setmetatable({_enum=enum, _buf=buf, length=len, bytes=bs}, mt) 98 end 99 index.from_buf = from_buf 100 101 function index.new_for_ioctl(enum, len) 102 local buf = ulong_ary(array_size(len)) 103 local x = from_buf(enum, buf, len) 104 return x, buf, ffi_sizeof(buf) 105 end 106 107 mth.add_index_methods(mt, index) 108 return setmetatable({}, mt)