1 /*
2 * Copyright (c) 2018-2020, Intel Corporation
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 * * Redistributions of source code must retain the above copyright notice,
8 * this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of Intel Corporation nor the names of its contributors
13 * may be used to endorse or promote products derived from this software
14 * without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 /** \file
30 * \brief Parse and build ParsedLogical::logicalTree and combInfoMap.
31 */
32 #include "logical_combination.h"
33 #include "parser/parse_error.h"
34 #include "util/container.h"
35 #include "hs_compile.h"
36 #include "allocator.h"
37
38 #include <vector>
39
40 using namespace std;
41
42 namespace ue2 {
43
getLogicalKey(u32 a)44 u32 ParsedLogical::getLogicalKey(u32 a) {
45 auto it = toLogicalKeyMap.find(a);
46 if (it == toLogicalKeyMap.end()) {
47 // get size before assigning to avoid wacky LHS shenanigans
48 u32 size = toLogicalKeyMap.size();
49 bool inserted;
50 tie(it, inserted) = toLogicalKeyMap.emplace(a, size);
51 assert(inserted);
52 }
53 DEBUG_PRINTF("%u -> lkey %u\n", it->first, it->second);
54 return it->second;
55 }
56
getCombKey(u32 a)57 u32 ParsedLogical::getCombKey(u32 a) {
58 auto it = toCombKeyMap.find(a);
59 if (it == toCombKeyMap.end()) {
60 u32 size = toCombKeyMap.size();
61 bool inserted;
62 tie(it, inserted) = toCombKeyMap.emplace(a, size);
63 assert(inserted);
64 }
65 DEBUG_PRINTF("%u -> ckey %u\n", it->first, it->second);
66 return it->second;
67 }
68
addRelateCKey(u32 lkey,u32 ckey)69 void ParsedLogical::addRelateCKey(u32 lkey, u32 ckey) {
70 auto it = lkey2ckeys.find(lkey);
71 if (it == lkey2ckeys.end()) {
72 bool inserted;
73 tie(it, inserted) = lkey2ckeys.emplace(lkey, set<u32>());
74 assert(inserted);
75 }
76 it->second.insert(ckey);
77 DEBUG_PRINTF("lkey %u belongs to combination key %u\n",
78 it->first, ckey);
79 }
80
81 #define TRY_RENUM_OP(ckey) \
82 do { \
83 if (ckey & LOGICAL_OP_BIT) { \
84 ckey = (ckey & ~LOGICAL_OP_BIT) + toLogicalKeyMap.size(); \
85 } \
86 } while(0)
87
logicalTreeAdd(u32 op,u32 left,u32 right)88 u32 ParsedLogical::logicalTreeAdd(u32 op, u32 left, u32 right) {
89 LogicalOp lop;
90 assert((LOGICAL_OP_BIT & (u32)logicalTree.size()) == 0);
91 lop.id = LOGICAL_OP_BIT | (u32)logicalTree.size();
92 lop.op = op;
93 lop.lo = left;
94 lop.ro = right;
95 logicalTree.push_back(lop);
96 return lop.id;
97 }
98
combinationInfoAdd(UNUSED u32 ckey,u32 id,u32 ekey,u32 lkey_start,u32 lkey_result,u64a min_offset,u64a max_offset)99 void ParsedLogical::combinationInfoAdd(UNUSED u32 ckey, u32 id, u32 ekey,
100 u32 lkey_start, u32 lkey_result,
101 u64a min_offset, u64a max_offset) {
102 assert(ckey == combInfoMap.size());
103 CombInfo ci;
104 ci.id = id;
105 ci.ekey = ekey;
106 ci.start = lkey_start;
107 ci.result = lkey_result;
108 ci.min_offset = min_offset;
109 ci.max_offset = max_offset;
110 combInfoMap.push_back(ci);
111
112 DEBUG_PRINTF("ckey %u (id %u) -> lkey %u..%u, ekey=0x%x\n", ckey, ci.id,
113 ci.start, ci.result, ci.ekey);
114 }
115
validateSubIDs(const unsigned * ids,const char * const * expressions,const unsigned * flags,unsigned elements)116 void ParsedLogical::validateSubIDs(const unsigned *ids,
117 const char *const *expressions,
118 const unsigned *flags,
119 unsigned elements) {
120 for (const auto &it : toLogicalKeyMap) {
121 bool unknown = true;
122 u32 i = 0;
123 for (i = 0; i < elements; i++) {
124 if ((ids ? ids[i] : 0) == it.first) {
125 unknown = false;
126 break;
127 }
128 }
129 if (unknown) {
130 throw CompileError("Unknown sub-expression id.");
131 }
132 if (contains(toCombKeyMap, it.first)) {
133 throw CompileError("Have combination of combination.");
134 }
135 if (flags && (flags[i] & HS_FLAG_SOM_LEFTMOST)) {
136 throw CompileError("Have SOM flag in sub-expression.");
137 }
138 if (flags && (flags[i] & HS_FLAG_PREFILTER)) {
139 throw CompileError("Have PREFILTER flag in sub-expression.");
140 }
141 hs_compile_error_t *compile_err = NULL;
142 hs_expr_info_t *info = NULL;
143 hs_error_t err = hs_expression_info(expressions[i], flags[i], &info,
144 &compile_err);
145 if (err != HS_SUCCESS) {
146 hs_free_compile_error(compile_err);
147 throw CompileError("Run hs_expression_info() failed.");
148 }
149 if (!info) {
150 throw CompileError("Get hs_expr_info_t failed.");
151 } else {
152 if (info->unordered_matches) {
153 throw CompileError("Have unordered match in sub-expressions.");
154 }
155 hs_misc_free(info);
156 }
157 }
158 }
159
logicalKeyRenumber()160 void ParsedLogical::logicalKeyRenumber() {
161 // renumber operation lkey in op vector
162 for (auto &op : logicalTree) {
163 TRY_RENUM_OP(op.id);
164 TRY_RENUM_OP(op.lo);
165 TRY_RENUM_OP(op.ro);
166 }
167 // renumber operation lkey in info map
168 for (auto &ci : combInfoMap) {
169 TRY_RENUM_OP(ci.start);
170 TRY_RENUM_OP(ci.result);
171 }
172 }
173
174 struct LogicalOperator {
LogicalOperatorue2::LogicalOperator175 LogicalOperator(u32 op_in, u32 paren_in)
176 : op(op_in), paren(paren_in) {}
177 u32 op;
178 u32 paren;
179 };
180
181 static
toOperator(char c)182 u32 toOperator(char c) {
183 u32 op = UNKNOWN_OP;
184 switch (c) {
185 case '!' :
186 op = LOGICAL_OP_NOT;
187 break;
188 case '&' :
189 op = LOGICAL_OP_AND;
190 break;
191 case '|' :
192 op = LOGICAL_OP_OR;
193 break;
194 default:
195 break;
196 };
197 return op;
198 }
199
200 static
cmpOperator(const LogicalOperator & op1,const LogicalOperator & op2)201 bool cmpOperator(const LogicalOperator &op1, const LogicalOperator &op2) {
202 if (op1.paren < op2.paren) {
203 return false;
204 }
205 if (op1.paren > op2.paren) {
206 return true;
207 }
208 assert(op1.paren == op2.paren);
209 if (op1.op > op2.op) {
210 return false;
211 }
212 if (op1.op < op2.op) {
213 return true;
214 }
215 return true;
216 }
217
218 static
fetchSubID(const char * logical,u32 & digit,u32 end)219 u32 fetchSubID(const char *logical, u32 &digit, u32 end) {
220 if (digit == (u32)-1) { // no digit parsing in progress
221 return (u32)-1;
222 }
223 assert(end > digit);
224 if (end - digit > 9) {
225 throw LocatedParseError("Expression id too large");
226 }
227 u32 mult = 1;
228 u32 sum = 0;
229 for (u32 j = end - 1; (j >= digit) && (j != (u32)-1) ; j--) {
230 assert(isdigit(logical[j]));
231 sum += (logical[j] - '0') * mult;
232 mult *= 10;
233 }
234 digit = (u32)-1;
235 return sum;
236 }
237
238 static
popOperator(vector<LogicalOperator> & op_stack,vector<u32> & subid_stack,ParsedLogical & pl)239 void popOperator(vector<LogicalOperator> &op_stack, vector<u32> &subid_stack,
240 ParsedLogical &pl) {
241 if (subid_stack.empty()) {
242 throw LocatedParseError("Not enough operand");
243 }
244 u32 right = subid_stack.back();
245 subid_stack.pop_back();
246 u32 left = 0;
247 if (op_stack.back().op != LOGICAL_OP_NOT) {
248 if (subid_stack.empty()) {
249 throw LocatedParseError("Not enough operand");
250 }
251 left = subid_stack.back();
252 subid_stack.pop_back();
253 }
254 subid_stack.push_back(pl.logicalTreeAdd(op_stack.back().op, left, right));
255 op_stack.pop_back();
256 }
257
parseLogicalCombination(unsigned id,const char * logical,u32 ekey,u64a min_offset,u64a max_offset)258 void ParsedLogical::parseLogicalCombination(unsigned id, const char *logical,
259 u32 ekey, u64a min_offset,
260 u64a max_offset) {
261 u32 ckey = getCombKey(id);
262 vector<LogicalOperator> op_stack;
263 vector<u32> subid_stack;
264 u32 lkey_start = INVALID_LKEY; // logical operation's lkey
265 u32 paren = 0; // parentheses
266 u32 digit = (u32)-1; // digit start offset, invalid offset is -1
267 u32 subid = (u32)-1;
268 u32 i;
269 try {
270 for (i = 0; logical[i]; i++) {
271 if (isdigit(logical[i])) {
272 if (digit == (u32)-1) { // new digit start
273 digit = i;
274 }
275 } else {
276 if ((subid = fetchSubID(logical, digit, i)) != (u32)-1) {
277 subid_stack.push_back(getLogicalKey(subid));
278 addRelateCKey(subid_stack.back(), ckey);
279 }
280 if (logical[i] == ' ') { // skip whitespace
281 continue;
282 }
283 if (logical[i] == '(') {
284 paren += 1;
285 } else if (logical[i] == ')') {
286 if (paren <= 0) {
287 throw LocatedParseError("Not enough left parentheses");
288 }
289 paren -= 1;
290 } else {
291 u32 prio = toOperator(logical[i]);
292 if (prio != UNKNOWN_OP) {
293 LogicalOperator op(prio, paren);
294 while (!op_stack.empty()
295 && cmpOperator(op_stack.back(), op)) {
296 popOperator(op_stack, subid_stack, *this);
297 if (lkey_start == INVALID_LKEY) {
298 lkey_start = subid_stack.back();
299 }
300 }
301 op_stack.push_back(op);
302 } else {
303 throw LocatedParseError("Unknown character");
304 }
305 }
306 }
307 }
308 if (paren != 0) {
309 throw LocatedParseError("Not enough right parentheses");
310 }
311 if ((subid = fetchSubID(logical, digit, i)) != (u32)-1) {
312 subid_stack.push_back(getLogicalKey(subid));
313 addRelateCKey(subid_stack.back(), ckey);
314 }
315 while (!op_stack.empty()) {
316 popOperator(op_stack, subid_stack, *this);
317 if (lkey_start == INVALID_LKEY) {
318 lkey_start = subid_stack.back();
319 }
320 }
321 if (subid_stack.size() != 1) {
322 throw LocatedParseError("Not enough operator");
323 }
324 } catch (LocatedParseError &error) {
325 error.locate(i);
326 throw;
327 }
328 u32 lkey_result = subid_stack.back(); // logical operation's lkey
329 if (lkey_start == INVALID_LKEY) {
330 throw CompileError("No logical operation.");
331 }
332 combinationInfoAdd(ckey, id, ekey, lkey_start, lkey_result,
333 min_offset, max_offset);
334 }
335
336 } // namespace ue2
337