binaryninja/binary_view/
search.rs

1use serde_derive::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, Default, Serialize, Deserialize)]
4pub struct SearchQuery {
5    /// ex. "42 2e 64 65 ?? 75 67 24"
6    pattern: String,
7    #[serde(skip_serializing_if = "Option::is_none")]
8    start: Option<u64>,
9    #[serde(skip_serializing_if = "Option::is_none")]
10    end: Option<u64>,
11    #[serde(rename = "ignoreCase")]
12    ignore_case: bool,
13    raw: bool,
14    overlap: bool,
15    #[serde(skip_serializing_if = "Option::is_none")]
16    align: Option<u64>,
17}
18
19impl SearchQuery {
20    pub fn new(pattern: impl Into<String>) -> Self {
21        Self {
22            pattern: pattern.into(),
23            ..Default::default()
24        }
25    }
26
27    /// Set the starting address for the search
28    pub fn start(mut self, addr: u64) -> Self {
29        self.start = Some(addr);
30        self
31    }
32
33    /// Set the ending address for the search (inclusive)
34    pub fn end(mut self, addr: u64) -> Self {
35        self.end = Some(addr);
36        self
37    }
38
39    /// Set whether to interpret the pattern as a raw string
40    pub fn raw(mut self, raw: bool) -> Self {
41        self.raw = raw;
42        self
43    }
44
45    /// Set whether to perform case-insensitive matching
46    pub fn ignore_case(mut self, ignore_case: bool) -> Self {
47        self.ignore_case = ignore_case;
48        self
49    }
50
51    /// Set whether to allow matches to overlap
52    pub fn overlap(mut self, overlap: bool) -> Self {
53        self.overlap = overlap;
54        self
55    }
56
57    /// Set the alignment of matches (must be a power of 2)
58    pub fn align(mut self, align: u64) -> Self {
59        // Validate that align is a power of 2
60        if align != 0 && (align & (align - 1)) == 0 {
61            self.align = Some(align);
62        }
63        self
64    }
65}
66
67impl SearchQuery {
68    /// Serialize the query to a JSON string
69    pub fn to_json(&self) -> String {
70        serde_json::to_string(self).expect("failed to serialize search query")
71    }
72}
73
74#[cfg(test)]
75mod tests {
76    use super::*;
77
78    #[test]
79    fn test_search_query_builder() {
80        let query = SearchQuery::new("test pattern")
81            .start(0x1000)
82            .end(0x2000)
83            .raw(true)
84            .ignore_case(true)
85            .overlap(false)
86            .align(16);
87
88        assert_eq!(query.pattern, "test pattern");
89        assert_eq!(query.start, Some(0x1000));
90        assert_eq!(query.end, Some(0x2000));
91        assert!(query.raw);
92        assert!(query.ignore_case);
93        assert!(!query.overlap);
94        assert_eq!(query.align, Some(16));
95    }
96
97    #[test]
98    fn test_search_query_json() {
99        let query = SearchQuery::new("test").start(0x1000).align(8);
100
101        let json = query.to_json();
102        let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
103
104        assert_eq!(parsed["pattern"], "test");
105        assert_eq!(parsed["start"], 4096);
106        assert_eq!(parsed["align"], 8);
107        assert!(!parsed.as_object().unwrap().contains_key("end"));
108    }
109}