efl

Форк
0
/
util.lua 
421 строка · 10.9 Кб
1
-- elua core utilities used in other modules
2

3
local ffi  = require("ffi")
4
local cast = ffi.cast
5
local new  = ffi.new
6
local copy = ffi.copy
7
local str  = ffi.string
8
local gc   = ffi.gc
9

10
local C = ffi.C
11

12
local M = {}
13

14
local getmetatable, setmetatable = getmetatable, setmetatable
15
local dgetmt = debug.getmetatable
16
local newproxy = newproxy
17

18
if not newproxy then
19
    -- tables can have __gc from 5.2
20
    newproxy = function(b)
21
        if b then
22
            return setmetatable({}, {})
23
        end
24
        return {}
25
    end
26
end
27

28
-- multiple inheritance index with depth-first search
29
local proto_lookup = function(protos, name)
30
    if not protos then return nil end
31
    for i = 1, #protos do
32
        local proto = protos[i]
33
        local v = proto[name]
34
        if v ~= nil then
35
            return v
36
        end
37
    end
38
end
39

40
local Object_MT = {
41
    __index = function(self, name)
42
        local v = proto_lookup(self.__mixins, name)
43
        if v == nil then
44
            return proto_lookup(self.__protos, name)
45
        end
46
    end,
47

48
    __tostring = function(self)
49
        local  f = self["__tostring"]
50
        if not f then
51
            return ("Object: %s"):format(self.name or "unnamed")
52
        end
53
        return f(self)
54
    end,
55

56
    __call = function(self, ...)
57
        return self["__call"](self, ...)
58
    end
59
}
60

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
65
end
66

67
M.Object = {
68
    __enable_dtor = false,
69

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)
75
            r.__gcproxy = px
76
            pxmt.__gc = obj_gc
77
            pxmt.__obj = r
78
        end
79
        if self.__ctor then return r, self.__ctor(r, ...) end
80
        return r
81
    end,
82

83
    clone = function(self, o)
84
        o = o or {}
85
        o.__protos, o.__mixins = { self }, {}
86
        setmetatable(o, Object_MT)
87
        return o
88
    end,
89

90
    is_a = function(self, base)
91
        if self == base then return true end
92
        local protos = self.__protos
93
        for i = 1, #protos do
94
            if protos[i]:is_a(base) then
95
                return true
96
            end
97
        end
98
        return false
99
    end,
100

101
    add_parent = function(self, parent)
102
        local protos = self.__protos
103
        protos[#protos + 1] = parent
104
    end,
105

106
    add_mixin = function(self, mixin)
107
        local mixins = self.__mixins
108
        mixins[#mixins + 1] = mixin
109
    end
110
}
111

112
local robj_gc = function(px)
113
    local dtor = px.__dtor
114
    if dtor then dtor(px) end
115
end
116

117
M.Readonly_Object = M.Object:clone {}
118
M.Readonly_Object.__call = function(self, ...)
119
    local r = newproxy(true)
120
    local rmt = dgetmt(r)
121
    rmt.__index = self
122
    rmt.__tostring = Object_MT.__tostring
123
    rmt.__metatable = false
124
    if self.__enable_dtor then
125
        rmt.__gc = robj_gc
126
    end
127
    if self.__ctor then return r, self.__ctor(r, rmt, ...) end
128
    return r
129
end
130

131
local loaded_libs = {}
132
local loaded_libc = {}
133

134
local load_lib_win = function(libname, ev)
135
    local succ, v
136
    if not ev or ev == "" then
137
        succ, v = pcall(ffi.load, libname)
138
        if not succ then
139
            succ, v = pcall(ffi.load, "lib" .. libname)
140
        end
141
    else
142
        succ, v = pcall(ffi.load, ev .. "\\" .. libname .. ".dll")
143
        if not succ then
144
            succ, v = pcall(ffi.load, ev .. "\\lib" .. libname .. ".dll")
145
        end
146
    end
147
    return succ, v
148
end
149

150
local load_lib = function(libname, ev)
151
    local succ, v
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)
156
    else
157
        local ext = (ffi.os == "OSX") and ".dylib" or ".so"
158
        succ, v = pcall(ffi.load, ev .. "/lib" .. libname .. ext)
159
    end
160
    return succ, v
161
end
162

163
-- makes sure we only keep one handle for each lib
164
-- reference counted
165
M.lib_load = function(libname)
166
    local  lib = loaded_libs[libname]
167
    if not lib then
168
        local ev = os.getenv("ELUA_" .. libname:upper() .. "_LIBRARY_PATH")
169
        local succ, v = load_lib(libname, ev)
170
        if not succ then
171
            error(v, 2)
172
        end
173
        lib = v
174
        loaded_libs[libname] = lib
175
        loaded_libc[libname] = 0
176
    end
177
    loaded_libc[libname] = loaded_libc[libname] + 1
178
    return lib
179
end
180

181
M.lib_unload = function(libname)
182
    local  cnt = loaded_libc[libname]
183
    if not cnt then return end
184
    if cnt == 1 then
185
        loaded_libs[libname], loaded_libc[libname] = nil, nil
186
    else
187
        loaded_libc[libname] = cnt - 1
188
    end
189
end
190

191
-- string fmt
192

193
ffi.cdef [[
194
    typedef struct _Str_Buf {
195
        char  *buf;
196
        size_t len;
197
        size_t cap;
198
    } Str_Buf;
199

200
    void *malloc(size_t);
201
    void    free(void *);
202
    size_t  strlen(const char *str);
203

204
    int isalnum(int c);
205
    int isdigit(int c);
206
]]
207

208
local fmt   = string.format
209
local pcall = pcall
210
local error = error
211
local type  = type
212
local tostr = tostring
213

214
local bytes = { ("cdeEfgGiopuxXsq"):byte(1, #("cdeEfgGiopuxXsq")) }
215
for i, v in ipairs(bytes) do bytes[v] = true end
216

217
local Str_Buf = ffi.metatype("Str_Buf", {
218
    __new = function(self)
219
        local r = new("Str_Buf")
220
        r.buf = C.malloc(8)
221
        r.len = 0
222
        r.cap = 8
223
        gc(r, self.free)
224
        return r
225
    end,
226
    __tostring = function(self)
227
        return str(self.buf, self.len)
228
    end,
229
    __index = {
230
        free = function(self)
231
            C.free(self.buf)
232
            self.buf = nil
233
            self.len = 0
234
            self.cap = 0
235
        end,
236
        clear = function(self)
237
            self.len = 0
238
        end,
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
245
            self.buf = buf
246
            self.cap = newcap
247
        end,
248
        append_char = function(self, c)
249
            local len = self.len
250
            self:grow (len + 1)
251
            self.buf  [len] = c
252
            self.len = len + 1
253
        end,
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)
258
            local len = self.len
259
            self:grow(len + strlen)
260
            for i = 0, strlen - 1 do
261
                self.buf[len + i] = strp[i]
262
            end
263
            self.len = len + strlen
264
        end
265
    }
266
})
267

268
local fmterr = function(idx, msg, off)
269
    local argerr = (type(idx) == "number")
270
        and ("#" .. idx)
271
         or ("'" .. idx .. "'")
272
    error("bad argument " .. argerr .. " to '%' (" .. msg .. ")",
273
        3 + (off or 0))
274
end
275

276
-- simulates lua's coercion
277
local checktype = function(c, idx, val)
278
    if c == 115 or c == 112 then -- s, p
279
        return val
280
    end
281
    local tv = type(val)
282
    if c == 113 then -- q
283
        if tv ~= "string" or tv ~= "number" then
284
            fmterr(idx, "string expected, got " .. tv, 1)
285
        end
286
        return val
287
    end
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
292
    end
293
    fmterr(idx, "number expected, got " .. tv, 1)
294
end
295

296
getmetatable("").__mod = function(fmts, params)
297
    if not fmts then return nil end
298
    if type(params) ~= "table" then
299
        params = { params }
300
    end
301

302
    local buf, nbuf = Str_Buf(), Str_Buf()
303
    local s         = cast("const char*", fmts)
304
    local c, s      = s[0], s + 1
305
    local argn      = 1
306

307
    while c ~= 0 do
308
        if c == 37 then -- %
309
            c, s = s[0], s + 1
310
            if c ~= 37 then
311
                while c ~= 0 and C.isalnum(c) ~= 0 do
312
                    nbuf:append_char(c)
313
                    c, s = s[0], s + 1
314
                end
315
                if c == 36 then -- $
316
                    c, s = s[0], s + 1
317
                    local n = tostr(nbuf)
318
                    nbuf:clear()
319
                    while C.isdigit(c) ~= 0 or c == 45 or c == 46 do -- -, .
320
                        nbuf:append_char(c)
321
                        c, s = s[0], s + 1
322
                    end
323
                    if not bytes[c] then
324
                        buf:append_str(n)
325
                        buf:append_char(36) -- $
326
                        buf:append_char(c)
327
                    else
328
                        nbuf:append_char(c)
329
                        local idx = tonumber(n) or n
330
                        if type(idx) == "number" and idx > #params then
331
                            fmterr(idx, "no value")
332
                        end
333
                        local v = params[idx]
334
                        v = checktype(c, idx, v)
335
                        buf:append_str(("%" .. tostr(nbuf)):format(v))
336
                        nbuf:clear()
337
                    end
338
                else
339
                    local fmtmark = (nbuf.len > 0) and nbuf.buf[0] or nil
340
                    if not fmtmark then
341
                        while c ~= 0 and (C.isdigit(c) ~= 0 or c == 45
342
                        or c == 46) do
343
                            nbuf:append_char(c)
344
                            c, s = s[0], s + 1
345
                        end
346
                        if bytes[c] then fmtmark = c end
347
                    end
348
                    if fmtmark then
349
                        if argn > #params then
350
                            fmterr(argn, "no value")
351
                        end
352
                        local v = params[argn]
353
                        v = checktype(fmtmark, argn, v)
354
                        buf:append_str(("%" .. tostr(nbuf)):format(v))
355
                        nbuf:clear()
356
                        argn = argn + 1
357
                    else
358
                        buf:append_str(nbuf.buf, nbuf.len)
359
                        nbuf:clear()
360
                    end
361
                    if c ~= 0 then buf:append_char(c) end
362
                end
363
            else
364
                buf:append_char(c)
365
            end
366
        else
367
            buf:append_char(c)
368
        end
369
        c, s = s[0], s + 1
370
    end
371
    nbuf:free()
372
    local ret = tostr(buf)
373
    buf:free()
374
    return ret
375
end
376

377
-- file utils
378

379
M.find_file = function(fname, paths)
380
    for i, path in ipairs(paths) do
381
        local actual_path
382
        if path:match(".*/") then
383
            actual_path = path .. fname
384
        else
385
            actual_path = path .. "/" .. fname
386
        end
387
        local f = io.open(actual_path)
388
        if f then
389
            f:close()
390
            return actual_path
391
        end
392
    end
393
end
394

395
-- table utils
396

397
table.uniq = function(tbl)
398
    local ret  = {}
399
    local used = {}
400
    for i, v in ipairs(tbl) do
401
        if not used[v] then
402
            ret[#ret + 1], used[v] = v, true
403
        end
404
    end
405
    return ret
406
end
407

408
M.get_namespace = function(M, nspaces)
409
    local last_m = M
410
    for i, v in ipairs(nspaces) do
411
        local nsp = M[v]
412
        if not nsp then
413
            nsp = {}
414
            last_m[v] = nsp
415
        end
416
        last_m = nsp
417
    end
418
    return last_m
419
end
420

421
return M
422

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.