mirror of
https://github.com/DrBeef/Raze.git
synced 2024-12-15 15:11:01 +00:00
0d79372216
git-svn-id: https://svn.eduke32.com/eduke32@3796 1a8010ca-5511-0410-912e-c29ae57300e0
293 lines
8.4 KiB
Lua
293 lines
8.4 KiB
Lua
|
|
-- "Bit array" module based on LuaJIT's BitOp.
|
|
|
|
local bit = require "bit"
|
|
local math = require "math"
|
|
|
|
local ffi = require "ffi"
|
|
|
|
local assert = assert
|
|
local error = error
|
|
local type = type
|
|
|
|
local tostring = tostring
|
|
|
|
|
|
module(...)
|
|
|
|
|
|
local bitar_ct = ffi.typeof[[
|
|
struct {
|
|
const double maxbidx; // last permissible bit index
|
|
const double maxidx; // last permissible int32_t index
|
|
const intptr_t arptr; // address of the int32_t array
|
|
}]]
|
|
local ptr_to_int = ffi.typeof("int32_t *")
|
|
|
|
local anchor = {}
|
|
|
|
-- population count of a nibble
|
|
local nibpop = ffi.new("double [?]", 16,
|
|
{ 0, 1, 1, 2, 1, 2, 2, 3,
|
|
1, 2, 2, 3, 2, 3, 3, 4 })
|
|
-- ...and of a byte
|
|
local bytepop = ffi.new("double [?]", 256)
|
|
for i=0,255 do
|
|
bytepop[i] = nibpop[bit.band(i, 15)] + nibpop[bit.rshift(i, 4)]
|
|
end
|
|
nibpop = nil
|
|
|
|
local function bitar_from_intar(maxbidx, maxidx, ar)
|
|
-- We need to have the int32_t[?] array be reachable so that it will not be
|
|
-- garbage collected
|
|
local ar_intptr = ffi.cast("intptr_t", ar)
|
|
anchor[tostring(ar_intptr)] = ar
|
|
|
|
-- Leaving the (potential) high trailing bits at 0 lets us not worry
|
|
-- about them in the population count calculation (__len metamethod).
|
|
-- Also, this is correct for maxbidx%32 == 0, since BitOp's shifts
|
|
-- mask the 5 lower bits of the counts.
|
|
local numremain = bit.band(maxbidx+1, 31)
|
|
ar[maxidx] = bit.band(ar[maxidx], bit.rshift(-1, 32-numremain))
|
|
|
|
return bitar_ct(maxbidx, maxidx, ar_intptr)
|
|
end
|
|
|
|
local function setop_common_rel(s1, s2)
|
|
if (s1.maxbidx ~= s2.maxbidx) then
|
|
error("bad arguments to bit array set op: must be of same length", 4)
|
|
end
|
|
|
|
local ar1 = ffi.cast(ptr_to_int, s1.arptr)
|
|
local ar2 = ffi.cast(ptr_to_int, s2.arptr)
|
|
|
|
return ar1, ar2
|
|
end
|
|
|
|
local function setop_common(s1, s2)
|
|
if (not ffi.istype(bitar_ct, s1) or not ffi.istype(bitar_ct, s2)) then
|
|
error("bad arguments to bit array set op: both must be 'bitar' types", 3)
|
|
end
|
|
|
|
local ar1, ar2 = setop_common_rel(s1, s2)
|
|
local ar = ffi.new("int32_t [?]", s1.maxidx+1)
|
|
|
|
return ar, ar1, ar2
|
|
end
|
|
|
|
local mt = {
|
|
--- Operational methods
|
|
|
|
__add = function(s1, s2) -- set union
|
|
local ar, ar1, ar2 = setop_common(s1, s2)
|
|
for i=0,s1.maxidx do
|
|
ar[i] = bit.bor(ar1[i], ar2[i])
|
|
end
|
|
return bitar_from_intar(s1.maxbidx, s1.maxidx, ar)
|
|
end,
|
|
|
|
__mul = function(s1, s2) -- set intersection
|
|
local ar, ar1, ar2 = setop_common(s1, s2)
|
|
for i=0,s1.maxidx do
|
|
ar[i] = bit.band(ar1[i], ar2[i])
|
|
end
|
|
return bitar_from_intar(s1.maxbidx, s1.maxidx, ar)
|
|
end,
|
|
|
|
__sub = function(s1, s2) -- set difference
|
|
local ar, ar1, ar2 = setop_common(s1, s2)
|
|
for i=0,s1.maxidx do
|
|
ar[i] = bit.band(ar1[i], bit.bnot(ar2[i]))
|
|
end
|
|
return bitar_from_intar(s1.maxbidx, s1.maxidx, ar)
|
|
end,
|
|
|
|
__unm = function(s) -- bitwise NOT
|
|
local newar = ffi.new("int32_t [?]", s.maxidx+1)
|
|
local oldar = ffi.cast(ptr_to_int, s.arptr)
|
|
for i=0,s.maxidx do
|
|
newar[i] = bit.bnot(oldar[i])
|
|
end
|
|
return bitar_from_intar(s.maxbidx, s.maxidx, newar)
|
|
end,
|
|
|
|
|
|
--- Additional operations
|
|
|
|
__index = {
|
|
-- TODO: Rename to 'testi', 'seti', 'cleari'; add 'flipi'?
|
|
|
|
-- Is bit i set?
|
|
isset = function(s, i)
|
|
if (not (i >= 0 and i<=s.maxbidx)) then
|
|
error("bad bit index for isset: must be in [0.."..s.maxbidx.."]", 2)
|
|
end
|
|
s = ffi.cast(ptr_to_int, s.arptr)
|
|
return (bit.band(s[bit.rshift(i, 5)], bit.lshift(1, i)) ~= 0)
|
|
end,
|
|
|
|
-- Clear bit i.
|
|
set0 = function(s, i)
|
|
if (not (i >= 0 and i<=s.maxbidx)) then
|
|
error("bad bit index for set0: must be in [0.."..s.maxbidx.."]", 2)
|
|
end
|
|
s = ffi.cast(ptr_to_int, s.arptr)
|
|
local jx = bit.rshift(i, 5)
|
|
s[jx] = bit.band(s[jx], bit.rol(0xfffffffe, i))
|
|
end,
|
|
|
|
-- Set bit i.
|
|
set1 = function(s, i)
|
|
if (not (i >= 0 and i<=s.maxbidx)) then
|
|
error("bad bit index for set1: must be in [0.."..s.maxbidx.."]", 2)
|
|
end
|
|
s = ffi.cast(ptr_to_int, s.arptr)
|
|
local jx = bit.rshift(i, 5)
|
|
s[jx] = bit.bor(s[jx], bit.rol(0x00000001, i))
|
|
end
|
|
},
|
|
|
|
|
|
--- Relational methods
|
|
|
|
__eq = function(s1, s2) -- set identity
|
|
local ar1, ar2 = setop_common_rel(s1, s2)
|
|
for i=0,s1.maxidx do
|
|
if (bit.bxor(ar1[i], ar2[i]) ~= 0) then
|
|
return false
|
|
end
|
|
end
|
|
return true
|
|
end,
|
|
|
|
__le = function(s1, s2)
|
|
local ar1, ar2 = setop_common_rel(s1, s2)
|
|
for i=0,s1.maxidx do
|
|
if (bit.band(ar1[i], bit.bnot(ar2[i])) ~= 0) then
|
|
return false
|
|
end
|
|
end
|
|
return true
|
|
end,
|
|
|
|
__lt = function(s1, s2)
|
|
return s1 <= s2 and not (s2 == s1)
|
|
end,
|
|
|
|
-- The length operator gets the population count of the bit array, i.e. the
|
|
-- number of set bits.
|
|
__len = function(s)
|
|
local ar = ffi.cast(ptr_to_int, s.arptr)
|
|
local popcnt = 0
|
|
for i=0,s.maxidx do
|
|
popcnt = popcnt + bytepop[bit.band(ar[i], 255)] +
|
|
bytepop[bit.band(bit.rshift(ar[i], 8), 255)] +
|
|
bytepop[bit.band(bit.rshift(ar[i], 16), 255)] +
|
|
bytepop[bit.rshift(ar[i], 24)]
|
|
end
|
|
return popcnt
|
|
end,
|
|
|
|
-- serialization
|
|
__tostring = function(s)
|
|
local size=s.maxidx+1
|
|
local ar = ffi.cast(ptr_to_int, s.arptr)
|
|
|
|
local hdr = "bitar.new("..s.maxbidx..", '"
|
|
local ofs = #hdr
|
|
local totalstrlen = ofs+8*size+2
|
|
local str = ffi.new("char [?]", totalstrlen)
|
|
|
|
ffi.copy(str, hdr, ofs)
|
|
|
|
for i=0,s.maxidx do
|
|
-- 'a' is ASCII 97
|
|
for nib=0,7 do
|
|
str[ofs + 8*i + nib] = 97 + bit.band(bit.rshift(ar[i], 4*nib), 0x0000000f)
|
|
end
|
|
end
|
|
|
|
ffi.copy(str+totalstrlen-2, "')", 2)
|
|
|
|
return ffi.string(str, totalstrlen)
|
|
end,
|
|
|
|
-- On garbage collection of the bitar, clear the array's anchor so that it
|
|
-- can be collected too.
|
|
__gc = function(s)
|
|
anchor[tostring(s.arptr)] = nil
|
|
end,
|
|
|
|
__metatable = true,
|
|
}
|
|
|
|
ffi.metatype(bitar_ct, mt)
|
|
|
|
|
|
-- Create new bit array.
|
|
function new(maxbidx, initval)
|
|
if (type(maxbidx) ~= "number" or not (maxbidx >= 0 and maxbidx <= (2^31)-2)) then
|
|
-- NOTE: Uh-oh, we can't write '2^31' because that would be interpreted
|
|
-- as color by OSD_Printf.
|
|
error("bad argument #1 to bitar.new (must be a number in [0..(2**31)-2])", 2)
|
|
end
|
|
|
|
if (math.floor(maxbidx) ~= maxbidx) then
|
|
error("bad argument #1 to bitar.new (must be an integral number)")
|
|
end
|
|
|
|
if (ffi.istype(ptr_to_int, initval)) then
|
|
-- Initialization from an array on the C side. INTERNAL.
|
|
-- Cannot be reached by user code since there's no access to the FFI
|
|
-- (and thus no way to create pointers).
|
|
|
|
return bitar_from_intar(maxbidx, (maxbidx+1)/32-1, initval)
|
|
|
|
elseif (type(initval)=="string") then
|
|
-- String containing hex digits (a..p) given, for INTERNAL use only.
|
|
-- XXX: Can be reached by user code.
|
|
|
|
local lstr = initval
|
|
|
|
local numnibs = #lstr
|
|
assert(numnibs%8 == 0)
|
|
|
|
local size = numnibs/8
|
|
local maxidx = size-1
|
|
local ar = ffi.new("int32_t [?]", size)
|
|
|
|
local str = ffi.new("char [?]", numnibs)
|
|
ffi.copy(str, lstr, numnibs)
|
|
|
|
for i=0,maxidx do
|
|
ar[i] = 0
|
|
|
|
for nib=0,7 do
|
|
local hexdig = str[8*i + nib]
|
|
assert(hexdig >= 97 and hexdig < 97+16)
|
|
ar[i] = bit.bor(ar[i], bit.lshift(hexdig-97, 4*nib))
|
|
end
|
|
end
|
|
|
|
-- NOTE: <maxbidx> cannot be extracted from the string, use the passed one.
|
|
return bitar_from_intar(maxbidx, maxidx, ar)
|
|
|
|
else
|
|
-- User-requested bitar creation.
|
|
|
|
if (initval ~= 0 and initval ~= 1) then
|
|
error("bad argument #2 to bitar.new (must be either 0 or 1)", 2)
|
|
end
|
|
|
|
local maxidx = math.floor(maxbidx/32)
|
|
local size = maxidx+1
|
|
|
|
local ar = ffi.new("int32_t [?]", size)
|
|
|
|
if (initval==1) then
|
|
ffi.fill(ar, size*4, -1)
|
|
end
|
|
|
|
return bitar_from_intar(maxbidx, maxidx, ar)
|
|
end
|
|
end
|