1use deku::prelude::*;
2
3use super::operand::{FileOffset, Length};
4
5#[derive(DekuRead, DekuWrite, Debug, Clone, PartialEq)]
6#[deku(bits = 3, type = "u8")]
7pub enum ArithmeticComparisonType {
8 #[deku(id = "0")]
9 Inequal,
10 #[deku(id = "1")]
11 Equal,
12 #[deku(id = "2")]
13 LessThan,
14 #[deku(id = "3")]
15 LessThanOrEqual,
16 #[deku(id = "4")]
17 GreaterThan,
18 #[deku(id = "5")]
19 GreaterThanOrEqual,
20}
21
22#[derive(DekuRead, DekuWrite, Debug, Clone, PartialEq)]
23pub struct ArithmeticQueryParams {
24 #[deku(bits = 1)]
25 pub signed: bool,
26 pub comparison_type: ArithmeticComparisonType,
27}
28
29#[derive(DekuRead, DekuWrite, Debug, Clone, PartialEq)]
30#[deku(bits = 3, type = "u8")]
31pub enum RangeComparisonType {
32 #[deku(id = "0")]
33 NotInRange,
34 #[deku(id = "1")]
35 InRange,
36}
37
38#[derive(DekuRead, DekuWrite, Debug, Clone, PartialEq)]
39pub struct RangeQueryParams {
40 #[deku(bits = 1)]
41 pub signed: bool,
42 pub comparison_type: RangeComparisonType,
43}
44
45#[derive(DekuRead, DekuWrite, Debug, Clone, PartialEq)]
46#[deku(bits = 3, type = "u8")]
47pub enum Query {
48 #[deku(id = "0x00")]
49 NonVoid(NonVoid),
50 #[deku(id = "0x01")]
51 ComparisonWithZero(ComparisonWithZero),
52 #[deku(id = "0x02")]
53 ComparisonWithValue(ComparisonWithValue),
54 #[deku(id = "0x03")]
55 ComparisonWithOtherFile(ComparisonWithOtherFile),
56 #[deku(id = "0x04")]
57 BitmapRangeComparison(BitmapRangeComparison),
58 #[deku(id = "0x07")]
59 StringTokenSearch(StringTokenSearch),
60}
61
62#[derive(DekuRead, DekuWrite, Debug, Clone, PartialEq)]
65pub struct NonVoid {
66 #[deku(pad_bits_before = "5")]
67 pub length: Length,
68 pub file: FileOffset,
69}
70
71#[derive(DekuRead, DekuWrite, Debug, Clone, PartialEq)]
72pub struct ComparisonWithZero {
73 #[deku(bits = 1, update = "self.mask.len() > 0")]
74 mask_present: bool,
75
76 pub params: ArithmeticQueryParams,
77
78 #[deku(update = "self.mask.len()")]
79 length: Length,
80
81 #[deku(cond = "*mask_present", count = "length", endian = "big")]
82 pub mask: Vec<u8>,
83 pub file: FileOffset,
84}
85
86impl ComparisonWithZero {
87 pub fn new(params: ArithmeticQueryParams, mask: Vec<u8>, file: FileOffset) -> Self {
88 Self {
89 mask_present: mask.len() > 0,
90 params,
91 length: mask.len().into(),
92 mask,
93 file,
94 }
95 }
96}
97
98#[derive(DekuRead, DekuWrite, Debug, Clone, PartialEq)]
100pub struct ComparisonWithValue {
101 #[deku(bits = 1, update = "self.mask.len() > 0")]
102 mask_present: bool,
103
104 pub params: ArithmeticQueryParams,
105
106 #[deku(update = "self.value.len()")]
107 length: Length,
108
109 #[deku(cond = "*mask_present", count = "length", endian = "big")]
110 pub mask: Vec<u8>,
111
112 #[deku(count = "length", endian = "big")]
113 pub value: Vec<u8>,
114
115 pub file: FileOffset,
116}
117
118impl ComparisonWithValue {
119 pub fn new(
120 params: ArithmeticQueryParams,
121 mask: Vec<u8>,
122 value: Vec<u8>,
123 file: FileOffset,
124 ) -> Self {
125 Self {
126 mask_present: mask.len() > 0,
127 params,
128 length: value.len().into(),
129 mask,
130 value,
131 file,
132 }
133 }
134}
135
136#[derive(DekuRead, DekuWrite, Debug, Clone, PartialEq)]
138pub struct ComparisonWithOtherFile {
139 #[deku(bits = 1, update = "self.mask.len() > 0")]
140 mask_present: bool,
141 pub params: ArithmeticQueryParams,
142
143 pub length: Length,
144 #[deku(cond = "*mask_present", count = "length", endian = "big")]
145 pub mask: Vec<u8>,
146
147 pub file1: FileOffset,
148 pub file2: FileOffset,
149}
150
151impl ComparisonWithOtherFile {
152 pub fn new(
153 params: ArithmeticQueryParams,
154 mask: Vec<u8>,
155 file1: FileOffset,
156 file2: FileOffset,
157 ) -> Self {
158 Self {
159 mask_present: mask.len() > 0,
160 params,
161 length: mask.len().into(),
162 mask,
163 file1,
164 file2,
165 }
166 }
167}
168
169#[derive(DekuRead, DekuWrite, Debug, Clone, PartialEq)]
171pub struct BitmapRangeComparison {
172 #[deku(bits = 1, update = "self.mask.len() > 0")]
173 mask_present: bool,
174 pub params: RangeQueryParams,
175 pub length: Length,
176 pub start: Length,
182 pub stop: Length,
183
184 #[deku(count = "length", endian = "big")]
185 pub mask: Vec<u8>,
186 pub file: FileOffset,
187}
188
189impl BitmapRangeComparison {
190 pub fn new(
191 params: RangeQueryParams,
192 start: u32,
193 stop: u32,
194 mask: Vec<u8>,
195 file: FileOffset,
196 ) -> Self {
197 Self {
198 mask_present: mask.len() > 0,
199 params,
200 length: mask.len().into(),
201 start: start.into(),
202 stop: stop.into(),
203 mask,
204 file,
205 }
206 }
207}
208
209#[derive(DekuRead, DekuWrite, Debug, Clone, PartialEq)]
212pub struct StringTokenSearch {
213 #[deku(bits = 1, update = "self.mask.len() > 0", pad_bits_after = "1")]
214 mask_present: bool,
215
216 #[deku(bits = 3)]
218 pub max_errors: u8,
219
220 #[deku(update = "self.value.len()")]
221 pub length: Length,
222
223 #[deku(count = "length", endian = "big")]
224 pub mask: Vec<u8>,
225
226 #[deku(count = "length", endian = "big")]
227 pub value: Vec<u8>,
228
229 pub file: FileOffset,
230}
231
232impl StringTokenSearch {
233 pub fn new(max_errors: u8, mask: Vec<u8>, value: Vec<u8>, file: FileOffset) -> Self {
234 Self {
235 mask_present: mask.len() > 0,
236 max_errors,
237 length: value.len().into(),
238 mask,
239 value,
240 file,
241 }
242 }
243}
244
245#[cfg(test)]
246mod test {
247 use hex_literal::hex;
248
249 use crate::test_tools::test_item;
250
251 use super::*;
252
253 #[test]
254 fn test_query_non_void() {
255 test_item(
256 Query::NonVoid(NonVoid {
257 length: 4u32.into(),
258 file: FileOffset {
259 file_id: 5,
260 offset: 6u32.into(),
261 },
262 }),
263 &hex!("00 04 05 06"),
264 )
265 }
266
267 #[test]
268 fn test_query_comparison_with_zero() {
269 test_item(
270 Query::ComparisonWithZero(ComparisonWithZero::new(
271 ArithmeticQueryParams {
272 signed: true,
273 comparison_type: ArithmeticComparisonType::Inequal,
274 },
275 vec![0, 1, 2],
276 FileOffset {
277 file_id: 4,
278 offset: 5u32.into(),
279 },
280 )),
281 &hex!("38 03 000102 04 05"),
282 )
283 }
284
285 #[test]
286 fn test_query_comparison_with_value() {
287 test_item(
288 Query::ComparisonWithValue(ComparisonWithValue::new(
289 ArithmeticQueryParams {
290 signed: false,
291 comparison_type: ArithmeticComparisonType::Equal,
292 },
293 vec![],
294 vec![9, 9, 9],
295 FileOffset {
296 file_id: 4,
297 offset: 5u32.into(),
298 },
299 )),
300 &hex!("41 03 090909 04 05"),
301 )
302 }
303
304 #[test]
305 fn test_query_comparison_with_other_file() {
306 test_item(
307 Query::ComparisonWithOtherFile(ComparisonWithOtherFile::new(
308 ArithmeticQueryParams {
309 signed: false,
310 comparison_type: ArithmeticComparisonType::GreaterThan,
311 },
312 vec![0xFF, 0xFF],
313 FileOffset {
314 file_id: 4,
315 offset: 5u32.into(),
316 },
317 FileOffset {
318 file_id: 8,
319 offset: 9u32.into(),
320 },
321 )),
322 &hex!("74 02 FFFF 04 05 08 09"),
323 )
324 }
325
326 #[test]
327 fn test_query_bitmap_range_comparison() {
328 test_item(
329 Query::BitmapRangeComparison(BitmapRangeComparison::new(
330 RangeQueryParams {
331 signed: false,
332 comparison_type: RangeComparisonType::InRange,
333 },
334 3,
335 32,
336 hex!("01020304").to_vec(),
337 FileOffset {
338 file_id: 0,
339 offset: 4u32.into(),
340 },
341 )),
342 &hex!("91 04 03 20 01020304 00 04"),
343 )
344 }
345
346 #[test]
347 fn test_query_string_token_search() {
348 test_item(
349 Query::StringTokenSearch(StringTokenSearch::new(
350 2,
351 hex!("FF00FF00").to_vec(),
352 hex!("01020304").to_vec(),
353 FileOffset {
354 file_id: 0,
355 offset: 4u32.into(),
356 },
357 )),
358 &hex!("F2 04 FF00FF00 01020304 00 04"),
359 )
360 }
361}