1
-- elua core utilities used in other modules
3
local ffi = require("ffi")
14
local getmetatable, setmetatable = getmetatable, setmetatable
15
local dgetmt = debug.getmetatable
16
local newproxy = newproxy
19
-- tables can have __gc from 5.2
20
newproxy = function(b)
22
return setmetatable({}, {})
28
-- multiple inheritance index with depth-first search
29
local proto_lookup = function(protos, name)
30
if not protos then return nil end
32
local proto = protos[i]
41
__index = function(self, name)
42
local v = proto_lookup(self.__mixins, name)
44
return proto_lookup(self.__protos, name)
48
__tostring = function(self)
49
local f = self["__tostring"]
51
return ("Object: %s"):format(self.name or "unnamed")
56
__call = function(self, ...)
57
return self["__call"](self, ...)
61
local obj_gc = function(px)
62
local obj = dgetmt(px).__obj
63
local dtor = obj and obj.__dtor or nil
64
if dtor then dtor(obj) end
68
__enable_dtor = false,
70
__call = function(self, ...)
71
local r = self:clone()
72
if self.__enable_dtor then
73
local px = newproxy(true)
74
local pxmt = dgetmt(px)
79
if self.__ctor then return r, self.__ctor(r, ...) end
83
clone = function(self, o)
85
o.__protos, o.__mixins = { self }, {}
86
setmetatable(o, Object_MT)
90
is_a = function(self, base)
91
if self == base then return true end
92
local protos = self.__protos
94
if protos[i]:is_a(base) then
101
add_parent = function(self, parent)
102
local protos = self.__protos
103
protos[#protos + 1] = parent
106
add_mixin = function(self, mixin)
107
local mixins = self.__mixins
108
mixins[#mixins + 1] = mixin
112
local robj_gc = function(px)
113
local dtor = px.__dtor
114
if dtor then dtor(px) end
117
M.Readonly_Object = M.Object:clone {}
118
M.Readonly_Object.__call = function(self, ...)
119
local r = newproxy(true)
120
local rmt = dgetmt(r)
122
rmt.__tostring = Object_MT.__tostring
123
rmt.__metatable = false
124
if self.__enable_dtor then
127
if self.__ctor then return r, self.__ctor(r, rmt, ...) end
131
local loaded_libs = {}
132
local loaded_libc = {}
134
local load_lib_win = function(libname, ev)
136
if not ev or ev == "" then
137
succ, v = pcall(ffi.load, libname)
139
succ, v = pcall(ffi.load, "lib" .. libname)
142
succ, v = pcall(ffi.load, ev .. "\\" .. libname .. ".dll")
144
succ, v = pcall(ffi.load, ev .. "\\lib" .. libname .. ".dll")
150
local load_lib = function(libname, ev)
152
if ffi.os == "Windows" then
153
succ, v = load_lib_win(libname, ev)
154
elseif not ev or ev == "" then
155
succ, v = pcall(ffi.load, libname)
157
local ext = (ffi.os == "OSX") and ".dylib" or ".so"
158
succ, v = pcall(ffi.load, ev .. "/lib" .. libname .. ext)
163
-- makes sure we only keep one handle for each lib
165
M.lib_load = function(libname)
166
local lib = loaded_libs[libname]
168
local ev = os.getenv("ELUA_" .. libname:upper() .. "_LIBRARY_PATH")
169
local succ, v = load_lib(libname, ev)
174
loaded_libs[libname] = lib
175
loaded_libc[libname] = 0
177
loaded_libc[libname] = loaded_libc[libname] + 1
181
M.lib_unload = function(libname)
182
local cnt = loaded_libc[libname]
183
if not cnt then return end
185
loaded_libs[libname], loaded_libc[libname] = nil, nil
187
loaded_libc[libname] = cnt - 1
194
typedef struct _Str_Buf {
200
void *malloc(size_t);
202
size_t strlen(const char *str);
208
local fmt = string.format
212
local tostr = tostring
214
local bytes = { ("cdeEfgGiopuxXsq"):byte(1, #("cdeEfgGiopuxXsq")) }
215
for i, v in ipairs(bytes) do bytes[v] = true end
217
local Str_Buf = ffi.metatype("Str_Buf", {
218
__new = function(self)
219
local r = new("Str_Buf")
226
__tostring = function(self)
227
return str(self.buf, self.len)
230
free = function(self)
236
clear = function(self)
239
grow = function(self, newcap)
240
local oldcap = self.cap
241
if oldcap >= newcap then return end
242
local buf = C.malloc(newcap)
243
if self.len ~= 0 then copy(buf, self.buf, self.len) end
244
if self.buf ~= nil then C.free(self.buf) end
248
append_char = function(self, c)
254
append_str = function(self, str, strlen)
255
if type(str) == "string" then strlen = strlen or #str end
256
local strp = cast("const char*", str)
257
strlen = strlen or C.strlen(strp)
259
self:grow(len + strlen)
260
for i = 0, strlen - 1 do
261
self.buf[len + i] = strp[i]
263
self.len = len + strlen
268
local fmterr = function(idx, msg, off)
269
local argerr = (type(idx) == "number")
271
or ("'" .. idx .. "'")
272
error("bad argument " .. argerr .. " to '%' (" .. msg .. ")",
276
-- simulates lua's coercion
277
local checktype = function(c, idx, val)
278
if c == 115 or c == 112 then -- s, p
282
if c == 113 then -- q
283
if tv ~= "string" or tv ~= "number" then
284
fmterr(idx, "string expected, got " .. tv, 1)
288
if tv == "number" then return val end
289
if tv == "string" then
290
local v = tonumber(val)
291
if v then return v end
293
fmterr(idx, "number expected, got " .. tv, 1)
296
getmetatable("").__mod = function(fmts, params)
297
if not fmts then return nil end
298
if type(params) ~= "table" then
302
local buf, nbuf = Str_Buf(), Str_Buf()
303
local s = cast("const char*", fmts)
304
local c, s = s[0], s + 1
311
while c ~= 0 and C.isalnum(c) ~= 0 do
317
local n = tostr(nbuf)
319
while C.isdigit(c) ~= 0 or c == 45 or c == 46 do -- -, .
325
buf:append_char(36) -- $
329
local idx = tonumber(n) or n
330
if type(idx) == "number" and idx > #params then
331
fmterr(idx, "no value")
333
local v = params[idx]
334
v = checktype(c, idx, v)
335
buf:append_str(("%" .. tostr(nbuf)):format(v))
339
local fmtmark = (nbuf.len > 0) and nbuf.buf[0] or nil
341
while c ~= 0 and (C.isdigit(c) ~= 0 or c == 45
346
if bytes[c] then fmtmark = c end
349
if argn > #params then
350
fmterr(argn, "no value")
352
local v = params[argn]
353
v = checktype(fmtmark, argn, v)
354
buf:append_str(("%" .. tostr(nbuf)):format(v))
358
buf:append_str(nbuf.buf, nbuf.len)
361
if c ~= 0 then buf:append_char(c) end
372
local ret = tostr(buf)
379
M.find_file = function(fname, paths)
380
for i, path in ipairs(paths) do
382
if path:match(".*/") then
383
actual_path = path .. fname
385
actual_path = path .. "/" .. fname
387
local f = io.open(actual_path)
397
table.uniq = function(tbl)
400
for i, v in ipairs(tbl) do
402
ret[#ret + 1], used[v] = v, true
408
M.get_namespace = function(M, nspaces)
410
for i, v in ipairs(nspaces) do