comparison mod_anti_spam/trie.lib.lua @ 5859:259ffdbf8906

mod_anti_spam: New module for spam filtering (pre-alpha)
author Matthew Wild <mwild1@gmail.com>
date Tue, 05 Mar 2024 18:26:29 +0000
parents
children
comparison
equal deleted inserted replaced
5858:761142ee0ff2 5859:259ffdbf8906
1 local bit = require "prosody.util.bitcompat";
2
3 local trie_methods = {};
4 local trie_mt = { __index = trie_methods };
5
6 local function new_node()
7 return {};
8 end
9
10 function trie_methods:set(item, value)
11 local node = self.root;
12 for i = 1, #item do
13 local c = item:byte(i);
14 if not node[c] then
15 node[c] = new_node();
16 end
17 node = node[c];
18 end
19 node.terminal = true;
20 node.value = value;
21 end
22
23 local function _remove(node, item, i)
24 if i > #item then
25 if node.terminal then
26 node.terminal = nil;
27 node.value = nil;
28 end
29 if next(node) ~= nil then
30 return node;
31 end
32 return nil;
33 end
34 local c = item:byte(i);
35 local child = node[c];
36 local ret;
37 if child then
38 ret = _remove(child, item, i+1);
39 node[c] = ret;
40 end
41 if ret == nil and next(node) == nil then
42 return nil;
43 end
44 return node;
45 end
46
47 function trie_methods:remove(item)
48 return _remove(self.root, item, 1);
49 end
50
51 function trie_methods:get(item, partial)
52 local value;
53 local node = self.root;
54 local len = #item;
55 for i = 1, len do
56 if partial and node.terminal then
57 value = node.value;
58 end
59 local c = item:byte(i);
60 node = node[c];
61 if not node then
62 return value, i - 1;
63 end
64 end
65 return node.value, len;
66 end
67
68 function trie_methods:add(item)
69 return self:set(item, true);
70 end
71
72 function trie_methods:contains(item, partial)
73 return self:get(item, partial) ~= nil;
74 end
75
76 function trie_methods:longest_prefix(item)
77 return select(2, self:get(item));
78 end
79
80 function trie_methods:add_subnet(item, bits)
81 item = item.packed:sub(1, math.ceil(bits/8));
82 local existing = self:get(item);
83 if not existing then
84 existing = { bits };
85 return self:set(item, existing);
86 end
87
88 -- Simple insertion sort
89 for i = 1, #existing do
90 local v = existing[i];
91 if v == bits then
92 return; -- Already in there
93 elseif v > bits then
94 table.insert(existing, v, i);
95 return;
96 end
97 end
98 end
99
100 function trie_methods:remove_subnet(item, bits)
101 item = item.packed:sub(1, math.ceil(bits/8));
102 local existing = self:get(item);
103 if not existing then
104 return;
105 end
106
107 -- Simple insertion sort
108 for i = 1, #existing do
109 local v = existing[i];
110 if v == bits then
111 table.remove(existing, i);
112 break;
113 elseif v > bits then
114 return; -- Stop search
115 end
116 end
117
118 if #existing == 0 then
119 self:remove(item);
120 end
121 end
122
123 function trie_methods:has_ip(item)
124 item = item.packed;
125 local node = self.root;
126 local len = #item;
127 for i = 1, len do
128 if node.terminal then
129 return true;
130 end
131
132 local c = item:byte(i);
133 local child = node[c];
134 if not child then
135 for child_byte, child_node in pairs(node) do
136 if type(child_byte) == "number" and child_node.terminal then
137 local bits = child_node.value;
138 for j = #bits, 1, -1 do
139 local b = bits[j]-((i-1)*8);
140 if b ~= 8 then
141 local mask = bit.bnot(2^b-1);
142 if bit.band(bit.bxor(c, child_byte), mask) == 0 then
143 return true;
144 end
145 end
146 end
147 end
148 end
149 return false;
150 end
151 node = child;
152 end
153 end
154
155 local function new()
156 return setmetatable({
157 root = new_node();
158 }, trie_mt);
159 end
160
161 local function is_trie(o)
162 return getmetatable(o) == trie_mt;
163 end
164
165 return {
166 new = new;
167 is_trie = is_trie;
168 };