use super::Pattern;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum BuilderError {
ParseError(std::num::ParseIntError),
SizeMismatch,
InvalidSignature(String),
InvalidThreadCount,
}
impl std::fmt::Display for BuilderError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::ParseError(err) => write!(f, "{}", err),
Self::SizeMismatch => write!(f, "the size of signature and mask do not match"),
Self::InvalidSignature(message) => write!(f, "{}", message),
Self::InvalidThreadCount => write!(f, "the thread count must be greater than zero and less than or equal to the number of logical cores"),
}
}
}
impl std::error::Error for BuilderError {}
impl From<std::num::ParseIntError> for BuilderError {
fn from(err: std::num::ParseIntError) -> Self {
Self::ParseError(err)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PatternBuilder {
signature: Vec<u8>,
mask: Vec<bool>,
threads: usize,
}
impl PatternBuilder {
pub fn from_code_style(signature: &[u8], mask: &str) -> Result<Self, BuilderError> {
let signature_bytes: Vec<u8> = signature.to_vec();
let mask_bytes: Vec<bool> = mask.chars().map(|c| c != '?').collect();
if signature_bytes.len() != mask_bytes.len() {
Err(BuilderError::SizeMismatch)
} else {
Ok(Self {
signature: signature_bytes,
mask: mask_bytes,
threads: 1,
})
}
}
pub fn from_ida_style(pattern: &str) -> Result<Self, BuilderError> {
if pattern.is_empty() {
Err(BuilderError::InvalidSignature(
"the pattern cannot be empty".to_string()
))?
}
let mut signature_bytes: Vec<u8> = vec![];
let mut mask_bytes: Vec<bool> = vec![];
for pair in pattern.split_whitespace() {
if pair == "?" || pair == "??" {
mask_bytes.push(false);
signature_bytes.push(0);
} else {
mask_bytes.push(true);
signature_bytes.push(
u8::from_str_radix(pair, 16)?
);
}
}
Ok(Self {
signature: signature_bytes,
mask: mask_bytes,
threads: 1,
})
}
pub fn from_hex_string(pattern: &str) -> Result<Self, BuilderError> {
if pattern.is_empty() {
Err(BuilderError::InvalidSignature(
"the pattern cannot be empty".to_string()
))?
}
if pattern.len() % 2 != 0 {
Err(BuilderError::InvalidSignature(
"the pattern must have an even number of characters".to_string()
))?
}
let mut signature_bytes: Vec<u8> = vec![];
let mut mask_bytes: Vec<bool> = vec![];
for pair in pattern.as_bytes().chunks(2) {
if pair == b"??" {
mask_bytes.push(false);
signature_bytes.push(0);
} else if pair.contains(&b'?') {
Err(BuilderError::InvalidSignature(
"the pattern does not accept single '?' wildcards".to_string()
))?
} else {
mask_bytes.push(true);
match std::str::from_utf8(pair) {
Ok(pair) => {
signature_bytes.push(
u8::from_str_radix(pair, 16)?
);
}
Err(_) => Err(BuilderError::InvalidSignature(
"the pattern contains an invalid character".to_string()
))?
}
}
}
Ok(Self {
signature: signature_bytes,
mask: mask_bytes,
threads: 1,
})
}
pub fn with_threads(mut self, threads: usize) -> Result<Self, BuilderError> {
if threads == 0 || threads > num_cpus::get() {
Err(BuilderError::InvalidThreadCount)
} else {
self.threads = threads;
Ok(self)
}
}
pub fn with_all_threads(mut self) -> Self {
self.threads = num_cpus::get();
self
}
pub fn build(self) -> Pattern {
Pattern::new(self.signature, self.mask, self.threads)
}
}