summaryrefslogtreecommitdiffstats
path: root/modules/policy/lua-aho-corasick/load_ac.lua
blob: eb704465d2eb4b5cb197d7bfa89e93e5f47ff1f2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
-- Helper wrappring script for loading shared object libac.so (FFI interface)
-- from package.cpath instead of LD_LIBRARTY_PATH.
--

local ffi = require 'ffi'
ffi.cdef[[
  void* ac_create(const char** str_v, unsigned int* strlen_v,
                  unsigned int v_len);
  int ac_match2(void*, const char *str, int len);
  void ac_free(void*);
]]

local _M = {}

local string_gmatch = string.gmatch
local string_match = string.match

local ac_lib = nil
local ac_create = nil
local ac_match = nil
local ac_free = nil

--[[ Find shared object file package.cpath, obviating the need of setting
   LD_LIBRARY_PATH
]]
local function find_shared_obj(cpath, so_name)
    for k, v in string_gmatch(cpath, "[^;]+") do
        local so_path = string_match(k, "(.*/)")
        if so_path then
            -- "so_path" could be nil. e.g, the dir path component is "."
            so_path = so_path .. so_name

            -- Don't get me wrong, the only way to know if a file exist is
            -- trying to open it.
            local f = io.open(so_path)
            if f ~= nil then
                io.close(f)
                return so_path
            end
        end
    end
end

function _M.load_ac_lib()
    if ac_lib ~= nil then
        return ac_lib
    else
        local so_path = find_shared_obj(package.cpath, "libac.so")
        if so_path ~= nil then
            ac_lib = ffi.load(so_path)
            ac_create = ac_lib.ac_create
            ac_match = ac_lib.ac_match2
            ac_free = ac_lib.ac_free
            return ac_lib
        end
    end
end

-- Create an Aho-Corasick instance, and return the instance if it was
-- successful.
function _M.create_ac(dict)
    local strnum = #dict
    if ac_lib == nil then
        _M.load_ac_lib()
    end

    local str_v = ffi.new("const char *[?]", strnum)
    local strlen_v = ffi.new("unsigned int [?]", strnum)

    for i = 1, strnum do
        local s = dict[i]
        str_v[i - 1] = s
        strlen_v[i - 1] = #s
    end

    local ac = ac_create(str_v, strlen_v, strnum);
    if ac ~= nil then
        return ffi.gc(ac, ac_free)
    end
end

-- Return nil if str doesn't match the dictionary, else return non-nil.
function _M.match(ac, str)
    local r = ac_match(ac, str, #str);
    if r >= 0 then
        return r
    end
end

return _M