Bunch of updates

Have component stub generator also generate documentation
Add documentation to the components
Support components defining their own type
add return nil, "no such component" to more component functions
fix component.doc
Add argument checks to computer api, maybe implement computer.realTime?
Implement unicode isWide, charWidth, and wlen
clean up boot.lua a little
Implement readonly mode in eeprom, and implement crc32 checksum
Add more readonly checks to filesystem, implement filesystem.rename
makeDirectory returns false if the path exists
suffix keyboard and screen with _sdl, and check for SDL before loading
move sdl_to_lwjgl to the support folder
This commit is contained in:
gamax92 2015-04-25 18:41:03 -06:00
parent 0004498294
commit d25b780d1f
14 changed files with 198 additions and 69 deletions

View File

@ -5,6 +5,7 @@ end
local component = require("component") local component = require("component")
local address = component.get(args[1]) local address = component.get(args[1])
local proxy = component.proxy(address) local proxy = component.proxy(address)
print(proxy.type)
local keys = {} local keys = {}
for k,v in pairs(proxy) do for k,v in pairs(proxy) do
if type(v) == "table" then if type(v) == "table" then
@ -25,5 +26,13 @@ for i = 1,#keys do
end end
file:write("function obj." .. k .. "(" .. doc .. ") " .. comment .."\n\t--STUB\n\tcprint(\"" .. proxy.type .. "." .. k .. "\"" .. (doc ~= "" and "," or "") .. doc .. ")\nend\n") file:write("function obj." .. k .. "(" .. doc .. ") " .. comment .."\n\t--STUB\n\tcprint(\"" .. proxy.type .. "." .. k .. "\"" .. (doc ~= "" and "," or "") .. doc .. ")\nend\n")
end end
file:write("\nlocal cec = {}\n\nreturn obj,cec") file:write("\nlocal cec = {}\n\nlocal doc = {\n")
for i = 1,#keys do
local k = keys[i]
if component.doc(address,k) ~= nil then
local doc = component.doc(address,k)
file:write(string.format("\t[%q]=%q,\n",k,doc))
end
end
file:write("}\n\nreturn obj,cec,doc")
file:close() file:close()

View File

@ -32,7 +32,7 @@ for k,v in pairs(components) do
end end
local proxy, cec, doc = fn(table.unpack(v,2)) local proxy, cec, doc = fn(table.unpack(v,2))
proxy.address = address proxy.address = address
proxy.type = v[1] proxy.type = proxy.type or v[1]
proxylist[address] = proxy proxylist[address] = proxy
emuicc[address] = cec emuicc[address] = cec
doclist[address] = doc doclist[address] = doc
@ -115,6 +115,7 @@ function env.component.methods(address)
end end
return methods return methods
end end
return nil, "no such component"
end end
function env.component.invoke(address, method, ...) function env.component.invoke(address, method, ...)
@ -126,6 +127,7 @@ function env.component.invoke(address, method, ...)
end end
return true, proxylist[address][method](...) return true, proxylist[address][method](...)
end end
return nil, "no such component"
end end
function env.component.doc(address, method) function env.component.doc(address, method)
@ -133,11 +135,12 @@ function env.component.doc(address, method)
checkArg(2,method,"string") checkArg(2,method,"string")
if proxylist[address] ~= nil then if proxylist[address] ~= nil then
if proxylist[address][method] == nil then if proxylist[address][method] == nil then
error("no such method",2) return nil
end end
if doclist[address] ~= nil then if doclist[address] ~= nil then
return doclist[address][method] return doclist[address][method]
end end
return nil return nil
end end
return nil, "no such component"
end end

View File

@ -1,5 +1,13 @@
local env = ... local env = ...
local sok,socket = pcall(require,"socket")
local gettime
if sok then
gettime = socket.gettime
else
gettime = os.time
end
local tmpaddr = "tmp-address" local tmpaddr = "tmp-address"
computer = {} computer = {}
@ -11,12 +19,12 @@ end
env.computer = {} env.computer = {}
function env.computer.realTime() function env.computer.realTime()
--STUB --TODO
--cprint("computer.realTime") -- Spammy --cprint("computer.realTime") -- Spammy
return 0 return gettime()
end end
function env.computer.uptime() function env.computer.uptime()
--STUB --TODO
cprint("computer.uptime") cprint("computer.uptime")
return elsa.timer.getTime() - machine.starttime return elsa.timer.getTime() - machine.starttime
end end
@ -35,8 +43,8 @@ function env.computer.totalMemory()
return 10000 return 10000
end end
function env.computer.pushSignal(name, ...) function env.computer.pushSignal(name, ...)
--TODO
cprint("computer.pushSignal", name, ...) cprint("computer.pushSignal", name, ...)
compCheckArg(1,name,"string")
table.insert(machine.signals, {name, ... }) table.insert(machine.signals, {name, ... })
end end
function env.computer.tmpAddress() function env.computer.tmpAddress()
@ -51,11 +59,13 @@ end
function env.computer.addUser(username) function env.computer.addUser(username)
--STUB --STUB
cprint("computer.addUser", username) cprint("computer.addUser", username)
compCheckArg(1,username,"string")
return nil, "player must be online" return nil, "player must be online"
end end
function env.computer.removeUser(username) function env.computer.removeUser(username)
--STUB --STUB
cprint("computer.removeUser", username) cprint("computer.removeUser", username)
compCheckArg(1,username,"string")
return false return false
end end
function env.computer.energy() function env.computer.energy()
@ -64,7 +74,7 @@ function env.computer.energy()
return math.huge return math.huge
end end
function env.computer.maxEnergy() function env.computer.maxEnergy()
--STUB --STUB, move to a config
cprint("computer.maxEnergy") cprint("computer.maxEnergy")
return math.huge return 500
end end

View File

@ -2,59 +2,51 @@ local env = ...
local utf8 = require("utf8") local utf8 = require("utf8")
env.unicode = {} env.unicode = {
lower = utf8.lower,
upper = utf8.upper,
char = utf8.char,
len = utf8.len,
reverse = utf8.reverse,
sub = utf8.sub,
}
function env.unicode.lower(str)
-- STUB
cprint("unicode.lower", str)
checkArg(1,str,"string")
return utf8.lower(str)
end
function env.unicode.upper(str)
-- STUB
cprint("unicode.upper", str)
checkArg(1,str,"string")
return utf8.upper(str)
end
function env.unicode.char(...)
-- TODO
cprint("unicode.char", ...)
return utf8.char(...)
end
function env.unicode.len(str)
-- TODO
cprint("unicode.len", str)
checkArg(1,str,"string")
return utf8.len(str)
end
function env.unicode.reverse(str)
-- TODO
cprint("unicode.reverse", str)
checkArg(1,str,"string")
return utf8.reverse(str)
end
function env.unicode.sub(str, i, j)
-- TODO
cprint("unicode.sub", str, i, j)
return utf8.sub(str, i, j)
end
function env.unicode.isWide(str) function env.unicode.isWide(str)
-- STUB
cprint("unicode.isWide", str) cprint("unicode.isWide", str)
checkArg(1,str,"string") checkArg(1,str,"string")
if #str == 0 then
error("String index out of range: 0",3)
end
local char = utf8.byte(str)
if unifont[char] ~= nil then
return #unifont[char] > 32
end
return false return false
end end
function env.unicode.charWidth(str) function env.unicode.charWidth(str)
-- STUB
cprint("unicode.charWidth", str) cprint("unicode.charWidth", str)
checkArg(1,str,"string") checkArg(1,str,"string")
if #str == 0 then
error("String index out of range: 0",3)
end
local char = utf8.byte(str)
if unifont[char] ~= nil then
return #unifont[char] / 32
end
return 1 return 1
end end
function env.unicode.wlen(str) function env.unicode.wlen(str)
-- STUB
cprint("unicode.wlen", str) cprint("unicode.wlen", str)
checkArg(1,str,"string") checkArg(1,str,"string")
return utf8.len(str) local length = 0
for _,c in utf8.next, str do
if unifont[c] ~= nil then
length = length + #unifont[c] / 32
else
length = length + 1
end
end
return length
end end
function env.unicode.wtrunc(str, count) function env.unicode.wtrunc(str, count)
-- STUB -- STUB

View File

@ -28,4 +28,11 @@ end
local cec = {} local cec = {}
return obj,cec local doc = {
["isRunning"]="function():boolean -- Returns whether the computer is running.",
["beep"]="function([frequency:number[, duration:number]]) -- Plays a tone, useful to alert users via audible feedback.",
["stop"]="function():boolean -- Stops the computer. Returns true if the state changed.",
["start"]="function():boolean -- Starts the computer. Returns true if the state changed.",
}
return obj,cec,doc

View File

@ -1,8 +1,11 @@
local address, slot, filename = ... local address, slot, filename = ...
local crc32 = require("support.crc32")
local code = elsa.filesystem.read(filename) local code = elsa.filesystem.read(filename)
local data = "" local data = ""
local label = "EEPROM" local label = "EEPROM"
local readonly = false
-- eeprom component -- eeprom component
local obj = {} local obj = {}
@ -34,15 +37,17 @@ function obj.getLabel() -- Get the label of the EEPROM.
end end
function obj.setLabel(newlabel) -- Set the label of the EEPROM. function obj.setLabel(newlabel) -- Set the label of the EEPROM.
cprint("eeprom.setLabel", newlabel) cprint("eeprom.setLabel", newlabel)
if readonly then
return nil, "storage is readonly"
end
compCheckArg(1,newlabel,"string","nil") compCheckArg(1,newlabel,"string","nil")
if newlabel == nil then newlabel = "EEPROM" end if newlabel == nil then newlabel = "EEPROM" end
label = newlabel:sub(1,16) label = newlabel:sub(1,16)
return label return label
end end
function obj.getChecksum() -- Get the checksum of the data on this EEPROM. function obj.getChecksum() -- Get the checksum of the data on this EEPROM.
-- STUB
cprint("eeprom.getChecksum") cprint("eeprom.getChecksum")
return "1badbabe" return string.format("%08x", tonumber(crc32(code)))
end end
function obj.get() -- Get the currently stored byte array. function obj.get() -- Get the currently stored byte array.
cprint("eeprom.get") cprint("eeprom.get")
@ -50,6 +55,9 @@ function obj.get() -- Get the currently stored byte array.
end end
function obj.set(newcode) -- Overwrite the currently stored byte array. function obj.set(newcode) -- Overwrite the currently stored byte array.
cprint("eeprom.set", newcode) cprint("eeprom.set", newcode)
if readonly then
return nil, "storage is readonly"
end
compCheckArg(1,newcode,"string","nil") compCheckArg(1,newcode,"string","nil")
if newcode == nil then newcode = "" end if newcode == nil then newcode = "" end
if #newcode > 4096 then if #newcode > 4096 then
@ -58,15 +66,28 @@ function obj.set(newcode) -- Overwrite the currently stored byte array.
code = newcode code = newcode
end end
function obj.makeReadonly(checksum) -- Make this EEPROM readonly if it isn't already. This process cannot be reversed! function obj.makeReadonly(checksum) -- Make this EEPROM readonly if it isn't already. This process cannot be reversed!
--STUB
print("eeprom.makeReadonly", checksum) print("eeprom.makeReadonly", checksum)
compCheckArg(1,checksum,"string") compCheckArg(1,checksum,"string")
if checksum ~= obj.getChecksum() then if checksum ~= obj.getChecksum() then
return nil, "incorrect checksum" return nil, "incorrect checksum"
end end
return false readonly = true
return true
end end
local cec = {} local cec = {}
return obj,cec local doc = {
["getData"]="function():string -- Get the currently stored byte array.",
["setData"]="function(data:string) -- Overwrite the currently stored byte array.",
["getDataSize"]="function():string -- Get the storage capacity of this EEPROM.",
["getSize"]="function():string -- Get the storage capacity of this EEPROM.",
["getLabel"]="function():string -- Get the label of the EEPROM.",
["setLabel"]="function(data:string):string -- Set the label of the EEPROM.",
["getChecksum"]="function():string -- Get the checksum of the data on this EEPROM.",
["get"]="function():string -- Get the currently stored byte array.",
["set"]="function(data:string) -- Overwrite the currently stored byte array.",
["makeReadonly"]="function(checksum:string):boolean -- Make this EEPROM readonly if it isn't already. This process cannot be reversed!",
}
return obj,cec,doc

View File

@ -68,7 +68,6 @@ function obj.spaceUsed() -- The currently used capacity of the file system, in b
return 0 return 0
end end
function obj.rename(from, to) -- Renames/moves an object from the first specified absolute path in the file system to the second. function obj.rename(from, to) -- Renames/moves an object from the first specified absolute path in the file system to the second.
--STUB
cprint("filesystem.rename", from, to) cprint("filesystem.rename", from, to)
compCheckArg(1,from,"string") compCheckArg(1,from,"string")
from = cleanPath(from) from = cleanPath(from)
@ -80,8 +79,11 @@ function obj.rename(from, to) -- Renames/moves an object from the first specifie
if to == ".." or to:sub(1,3) == "../" then if to == ".." or to:sub(1,3) == "../" then
return nil,"file not found" return nil,"file not found"
end end
if readonly then
return false return false
end end
return os.rename(directory .. "/" .. from, directory .. "/" .. to) == true
end
function obj.close(handle) -- Closes an open file descriptor with the specified handle. function obj.close(handle) -- Closes an open file descriptor with the specified handle.
cprint("filesystem.close", handle) cprint("filesystem.close", handle)
compCheckArg(1,handle,"number") compCheckArg(1,handle,"number")
@ -108,6 +110,9 @@ function obj.remove(path) -- Removes the object at the specified absolute path i
if path == ".." or path:sub(1,3) == "../" then if path == ".." or path:sub(1,3) == "../" then
return nil,"file not found" return nil,"file not found"
end end
if readonly then
return false
end
return elsa.filesystem.remove(directory .. "/" .. path) return elsa.filesystem.remove(directory .. "/" .. path)
end end
function obj.size(path) -- Returns the size of the object at the specified absolute path in the file system. function obj.size(path) -- Returns the size of the object at the specified absolute path in the file system.
@ -140,13 +145,15 @@ function obj.getLabel() -- Get the current label of the file system.
return label return label
end end
function obj.setLabel(value) -- Sets the label of the file system. Returns the new value, which may be truncated. function obj.setLabel(value) -- Sets the label of the file system. Returns the new value, which may be truncated.
--TODO --TODO: treat functions as nil
cprint("filesystem.setLabel", value) cprint("filesystem.setLabel", value)
compCheckArg(1,value,"string") compCheckArg(1,value,"string")
if readonly then
error("label is read only",3)
end
label = value:sub(1,16) label = value:sub(1,16)
end end
function obj.open(path, mode) -- Opens a new file descriptor and returns its handle. function obj.open(path, mode) -- Opens a new file descriptor and returns its handle.
--TODO
cprint("filesystem.open", path, mode) cprint("filesystem.open", path, mode)
if mode == nil then mode = "r" end if mode == nil then mode = "r" end
compCheckArg(1,path,"string") compCheckArg(1,path,"string")
@ -158,6 +165,9 @@ function obj.open(path, mode) -- Opens a new file descriptor and returns its han
if mode ~= "r" and mode ~= "rb" and mode ~= "w" and mode ~= "wb" and mode ~= "a" and mode ~= "ab" then if mode ~= "r" and mode ~= "rb" and mode ~= "w" and mode ~= "wb" and mode ~= "a" and mode ~= "ab" then
error("unsupported mode",3) error("unsupported mode",3)
end end
if (not (mode == "r" or mode == "rb") and readonly) or ((mode == "r" or mode == "rb") and not elsa.filesystem.exists(directory .. "/" .. path)) then
return nil,"file not found"
end
local file, err = elsa.filesystem.newFile(directory .. "/" .. path, mode:sub(1,1)) local file, err = elsa.filesystem.newFile(directory .. "/" .. path, mode:sub(1,1))
if not file then return nil, err end if not file then return nil, err end
while true do while true do
@ -204,13 +214,15 @@ function obj.isReadOnly() -- Returns whether the file system is read-only.
return readonly return readonly
end end
function obj.makeDirectory(path) -- Creates a directory at the specified absolute path in the file system. Creates parent directories, if necessary. function obj.makeDirectory(path) -- Creates a directory at the specified absolute path in the file system. Creates parent directories, if necessary.
--TODO
cprint("filesystem.makeDirectory", path) cprint("filesystem.makeDirectory", path)
compCheckArg(1,path,"string") compCheckArg(1,path,"string")
path = cleanPath(path) path = cleanPath(path)
if path == ".." or path:sub(1,3) == "../" then if path == ".." or path:sub(1,3) == "../" then
return nil,"file not found" return nil,"file not found"
end end
if elsa.filesystem.exists(directory .. "/" .. path) or readonly then
return false
end
return elsa.filesystem.createDirectory(directory .. "/" .. path) return elsa.filesystem.createDirectory(directory .. "/" .. path)
end end
function obj.isDirectory(path) -- Returns whether the object at the specified absolute path in the file system is a directory. function obj.isDirectory(path) -- Returns whether the object at the specified absolute path in the file system is a directory.
@ -225,4 +237,25 @@ end
local cec = {} local cec = {}
return obj,cec local doc = {
["read"]="function(handle:number, count:number):string or nil -- Reads up to the specified amount of data from an open file descriptor with the specified handle. Returns nil when EOF is reached.",
["lastModified"]="function(path:string):number -- Returns the (real world) timestamp of when the object at the specified absolute path in the file system was modified.",
["spaceUsed"]="function():number -- The currently used capacity of the file system, in bytes.",
["rename"]="function(from:string, to:string):boolean -- Renames/moves an object from the first specified absolute path in the file system to the second.",
["close"]="function(handle:number) -- Closes an open file descriptor with the specified handle.",
["write"]="function(handle:number, value:string):boolean -- Writes the specified data to an open file descriptor with the specified handle.",
["remove"]="function(path:string):boolean -- Removes the object at the specified absolute path in the file system.",
["size"]="function(path:string):number -- Returns the size of the object at the specified absolute path in the file system.",
["seek"]="function(handle:number, whence:string, offset:number):number -- Seeks in an open file descriptor with the specified handle. Returns the new pointer position.",
["spaceTotal"]="function():number -- The overall capacity of the file system, in bytes.",
["getLabel"]="function():string -- Get the current label of the file system.",
["setLabel"]="function(value:string):string -- Sets the label of the file system. Returns the new value, which may be truncated.",
["open"]="function(path:string[, mode:string='r']):number -- Opens a new file descriptor and returns its handle.",
["exists"]="function(path:string):boolean -- Returns whether an object exists at the specified absolute path in the file system.",
["list"]="function(path:string):table -- Returns a list of names of objects in the directory at the specified absolute path in the file system.",
["isReadOnly"]="function():boolean -- Returns whether the file system is read-only.",
["makeDirectory"]="function(path:string):boolean -- Creates a directory at the specified absolute path in the file system. Creates parent directories, if necessary.",
["isDirectory"]="function(path:string):boolean -- Returns whether the object at the specified absolute path in the file system is a directory.",
}
return obj,cec,doc

View File

@ -199,4 +199,25 @@ end
local cec = {} local cec = {}
return obj,cec local doc = {
["bind"]="function(address:string):boolean -- Binds the GPU to the screen with the specified address.",
["getForeground"]="function():number, boolean -- Get the current foreground color and whether it's from the palette or not.",
["setForeground"]="function(value:number[, palette:boolean]):number, number or nil -- Sets the foreground color to the specified value. Optionally takes an explicit palette index. Returns the old value and if it was from the palette its palette index.",
["getBackground"]="function():number, boolean -- Get the current background color and whether it's from the palette or not.",
["setBackground"]="function(value:number[, palette:boolean]):number, number or nil -- Sets the background color to the specified value. Optionally takes an explicit palette index. Returns the old value and if it was from the palette its palette index.",
["getDepth"]="function():number -- Returns the currently set color depth.",
["setDepth"]="function(depth:number):number -- Set the color depth. Returns the previous value.",
["maxDepth"]="function():number -- Get the maximum supported color depth.",
["fill"]="function(x:number, y:number, width:number, height:number, char:string):boolean -- Fills a portion of the screen at the specified position with the specified size with the specified character.",
["getScreen"]="function():string -- Get the address of the screen the GPU is currently bound to.",
["getResolution"]="function():number, number -- Get the current screen resolution.",
["setResolution"]="function(width:number, height:number):boolean -- Set the screen resolution. Returns true if the resolution changed.",
["maxResolution"]="function():number, number -- Get the maximum screen resolution.",
["getPaletteColor"]="function(index:number):number -- Get the palette color at the specified palette index.",
["setPaletteColor"]="function(index:number, color:number):number -- Set the palette color at the specified palette index. Returns the previous value.",
["get"]="function(x:number, y:number):string, number, number, number or nil, number or nil -- Get the value displayed on the screen at the specified index, as well as the foreground and background color. If the foreground or background is from the palette, returns the palette indices as fourth and fifth results, else nil, respectively.",
["set"]="function(x:number, y:number, value:string[, vertical:boolean]):boolean -- Plots a string value to the screen at the specified position. Optionally writes the string vertically.",
["copy"]="function(x:number, y:number, width:number, height:number, tx:number, ty:number):boolean -- Copies a portion of the screen from the specified location with the specified size by the specified translation.",
}
return obj,cec,doc

View File

@ -4,7 +4,7 @@ local ffi = require("ffi")
local lua_utf8 = require("utf8") local lua_utf8 = require("utf8")
-- Conversion table for SDL2 keys to LWJGL key codes -- Conversion table for SDL2 keys to LWJGL key codes
local keys = elsa.filesystem.load("sdl_to_lwjgl.lua")() local keys = require("support.sdl_to_lwjgl")
local code2char = {} local code2char = {}
@ -33,7 +33,7 @@ end
-- keyboard component -- keyboard component
-- Much complex -- Much complex
local obj = {} local obj = {type="keyboard"}
-- Such methods -- Such methods
local cec = {} local cec = {}

View File

@ -108,7 +108,7 @@ local touchinvert = false
local precise = false local precise = false
-- screen component -- screen component
local obj = {} local obj = {type="screen"}
function obj.isTouchModeInverted() -- Whether touch mode is inverted (sneak-activate opens GUI, instead of normal activate). function obj.isTouchModeInverted() -- Whether touch mode is inverted (sneak-activate opens GUI, instead of normal activate).
cprint("screen.isTouchModeInverted") cprint("screen.isTouchModeInverted")
@ -303,4 +303,16 @@ function cec.copy(x1, y1, w, h, tx, ty) -- Copies a portion of the screen from t
texture,copytexture=copytexture,texture texture,copytexture=copytexture,texture
end end
return obj,cec local doc = {
["isTouchModeInverted"]="function():boolean -- Whether touch mode is inverted (sneak-activate opens GUI, instead of normal activate).",
["setTouchModeInverted"]="function(value:boolean):boolean -- Sets whether to invert touch mode (sneak-activate opens GUI, instead of normal activate).",
["isPrecise"]="function():boolean -- Returns whether the screen is in high precision mode (sub-pixel mouse event positions).",
["setPrecise"]="function(enabled:boolean):boolean -- Set whether to use high precision mode (sub-pixel mouse event positions).",
["turnOff"]="function():boolean -- Turns off the screen. Returns true if it was on.",
["turnOn"]="function():boolean -- Turns the screen on. Returns true if it was off.",
["isOn"]="function():boolean -- Returns whether the screen is currently on.",
["getAspectRatio"]="function():number, number -- The aspect ratio of the screen. For multi-block screens this is the number of blocks, horizontal and vertical.",
["getKeyboards"]="function():table -- The list of keyboards attached to the screen.",
}
return obj,cec,doc

View File

@ -8,15 +8,19 @@ conf = {
-- Read component files for parameter documentation -- Read component files for parameter documentation
components = { components = {
{"gpu",nil,0,160,50,3}, {"gpu",nil,0,160,50,3},
{"screen",nil,nil,80,25,3},
{"eeprom",nil,9,"lua/bios.lua"}, {"eeprom",nil,9,"lua/bios.lua"},
{"filesystem",nil,5,"loot/OpenOS",true}, {"filesystem",nil,5,"loot/OpenOS",true},
{"filesystem",nil,nil,"tmpfs",false}, {"filesystem",nil,nil,"tmpfs",false},
{"filesystem",nil,nil,nil,false}, {"filesystem",nil,nil,nil,false},
{"computer"}, {"computer"},
{"keyboard"},
} }
} }
if elsa.SDL then
table.insert(conf.components, {"screen_sdl2",nil,nil,80,25,3})
table.insert(conf.components, {"keyboard_sdl2"})
else
-- TODO: Alternatives
end
machine = { machine = {
starttime = elsa.timer.getTime(), starttime = elsa.timer.getTime(),

17
src/support/crc32.lua Normal file
View File

@ -0,0 +1,17 @@
local ffi = require("ffi")
local bit32 = require("bit32")
local s_crc32 = ffi.new('const uint32_t[16]', 0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c)
return function(str)
local crc = 0
local len = #str
str = ffi.cast('const uint8_t*', str)
crc = bit32.bnot(crc)
for i = 0, len-1 do
crc = bit32.bxor(bit32.rshift(crc, 4), s_crc32[bit32.bxor(bit32.band(crc, 0xF), bit32.band(str[i], 0xF))])
crc = bit32.bxor(bit32.rshift(crc, 4), s_crc32[bit32.bxor(bit32.band(crc, 0xF), bit32.rshift(str[i], 4))])
end
return bit32.bnot(crc)
end