local core = require "alien.core" local io = require "io" local pairs, ipairs = pairs, ipairs local setmetatable = setmetatable local error = error local pcall = pcall local type = type local rawset = rawset local unpack = unpack local math = math local print = print local _M = alien _M.loaded = {} local load_library, find_library = {}, {} local function find_library_helper(libname, opt) local expr = '/[^() ]*lib' .. libname .. '\.so[^() ]*' local cmd = '/sbin/ldconfig ' .. opt .. ' 2>/dev/null | egrep -o "' .. expr .. '"' local pipe = io.popen(cmd) if pipe then local res = pipe:read() pipe:close() return res and res:match("([^%s]*)") end return nil end function find_library.linux(libname) return find_library_helper(libname, "-p") end function find_library.bsd(libname) return find_library_helper(libname, "-r") end function find_library.darwin(libname) local ok, lib = pcall(core.load, libname .. ".dylib") if ok then return lib end ok, lib = pcall(core.load, libname .. ".framework/" .. libname) if ok then return lib end return nil end local function load_library_helper(libname, libext) if libname:match("/") or libname:match("%" .. libext) then return core.load(libname) else local ok, lib = pcall(core.load, "lib" .. libname .. libext) if not ok then ok, lib = pcall(core.load, "./lib" .. libname .. libext) if not ok then local name = find_library[core.platform](libname) if name then lib = core.load(name) else error("library " .. libname .. " not found") end end end return lib end end function load_library.linux(libname) return load_library_helper(libname, ".so") end load_library.bsd = load_library.linux function load_library.darwin(libname) return load_library_helper(libname, ".dylib") end setmetatable(load_library, { __index = function (t, plat) return core.load end } ) function load_library.windows(libname) return core.load(libname) end setmetatable(_M.loaded, { __index = function (t, libname) local lib = load_library[core.platform](libname) t[libname] = lib return lib end, __mode = "kv" }) setmetatable(_M, { __index = _M.loaded }) for name, f in pairs(core) do _M[name] = f end function _M.load(libname) return _M.loaded[libname] end function _M.callback(f, ...) local cb = core.callback(f) cb.types(cb, ...) return cb end local array_methods = {} local function array_next(arr, i) if i < arr.length then return i + 1, arr[i + 1] else return nil end end function array_methods:ipairs() return array_next, self, 0 end local function array_get(arr, key) if type(key) == "number" then if key < 1 or key > arr.length then error("array access out of bounds") end local offset = (key - 1) * arr.size + 1 return arr.buffer:get(offset, arr.type) else return array_methods[key] end end local function array_set(arr, key, val) if type(key) == "number" then if key < 1 or key > arr.length then error("array access out of bounds") end local offset = (key - 1) * arr.size + 1 arr.buffer:set(offset, val, arr.type) if arr.type == "pointer" then arr.pinned[key] = val end else rawset(arr, key, val) end end function _M.array(t, length, init) local ok, size = pcall(core.sizeof, t) if not ok then error("type " .. t .. " does not exist") end if type(length) == "table" then init = length length = #length end local arr = { type = t, length = length, size = size, pinned = {} } setmetatable(arr, { __index = array_get, __newindex = array_set }) if type(init) == "userdata" then arr.buffer = init else arr.buffer = core.buffer(size * length) if type(init) == "table" then for i = 1, length do if type(init[i]) == "string" then local offset = (i - 1) * size + 1 arr.pinned[i] = alien.buffer(#init[i] + 1) arr.buffer:set(offset, arr.pinned[i], "pointer") end arr[i] = init[i] end end end return arr end local function struct_new(s_proto, ptr) local buf = core.buffer(ptr or s_proto.size) local function struct_get(_, key) if s_proto.offsets[key] then return buf:get(s_proto.offsets[key] + 1, s_proto.types[key]) else error("field " .. key .. " does not exist") end end local function struct_set(_, key, val) if s_proto.offsets[key] then buf:set(s_proto.offsets[key] + 1, val, s_proto.types[key]) else error("field " .. key .. " does not exist") end end return setmetatable({}, { __index = struct_get, __newindex = struct_set, __call = function () return buf end }) end local function struct_byval(s_proto) local types = {} local size = s_proto.size for i = 0, size - 1, 4 do if size - i == 1 then types[#types + 1] = "char" elseif size - i == 2 then types[#types + 1] = "short" else types[#types + 1] = "int" end end return unpack(types) end function _M.defstruct(t) local off = 0 local names, offsets, types = {}, {}, {} for _, field in ipairs(t) do local name, type = field[1], field[2] names[#names + 1] = name off = math.ceil(off / core.align(type)) * core.align(type) offsets[name] = off types[name] = type off = off + core.sizeof(type) end return { names = names, offsets = offsets, types = types, size = off, new = struct_new, byval = struct_byval } end function _M.byval(buf) if buf.size then local size = buf.size local types = { "char", "short"} local vals = {} for i = 1, size, 4 do if size - i == 0 then vals[#vals + 1] = buf:get(i, "char") elseif size - i == 1 then vals[#vals + 1] = buf:get(i, "short") else vals[#vals + 1] = buf:get(i, "int") end end return unpack(vals) else error("this type of buffer can't be passed by value") end end