use std::ops::Range;
use serde::{Deserialize, Serialize};
use crate::Database;
use crate::address::Address;
use crate::bitness::Bitness;
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[doc(alias("idainfo"))]
pub struct DatabaseInfo {
pub bitness: Option<Bitness>,
#[doc(alias("get_imagebase"))]
pub image_base: Option<Address>,
#[doc(alias("inf_get_procname"))]
pub processor: Option<String>,
#[doc(alias("get_file_type_name"))]
pub file_type: Option<String>,
#[doc(alias("get_input_file_path"))]
pub input_path: Option<String>,
pub root_filename: Option<String>,
}
impl Database {
#[must_use]
#[doc(alias("inf_get_min_ea", "inf_get_max_ea"))]
pub fn address_range(&self) -> Option<Range<Address>> {
let min = Address::try_new(self.min_ea())?;
let max = Address::try_new(self.max_ea())?;
Some(min..max)
}
#[must_use]
pub fn info(&self) -> DatabaseInfo {
DatabaseInfo {
bitness: self.bitness(),
image_base: Address::try_new(self.image_base()),
processor: self.proc_name(),
file_type: self.file_type_name(),
input_path: self.input_path(),
root_filename: self.root_filename(),
}
}
}
#[cfg(test)]
mod tests {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use assert2::assert;
use rstest::rstest;
use super::DatabaseInfo;
use crate::address::Address;
use crate::bitness::Bitness;
const fn assert_send<T: Send>() {}
const _: () = assert_send::<DatabaseInfo>();
fn sample() -> DatabaseInfo {
DatabaseInfo {
bitness: Some(Bitness::Bits64),
image_base: Some(Address::new_const(0x1400_0000)),
processor: Some("metapc".to_owned()),
file_type: None,
input_path: None,
root_filename: Some("sample.exe".to_owned()),
}
}
fn empty() -> DatabaseInfo {
DatabaseInfo {
bitness: None,
image_base: None,
processor: None,
file_type: None,
input_path: None,
root_filename: None,
}
}
fn dense() -> DatabaseInfo {
DatabaseInfo {
bitness: Some(Bitness::Bits32),
image_base: Some(Address::new_const(0)),
processor: Some("arm".to_owned()),
file_type: Some("ELF for ARM".to_owned()),
input_path: Some("/path/to/binary".to_owned()),
root_filename: Some("binary".to_owned()),
}
}
fn hash_of(info: &DatabaseInfo) -> u64 {
let mut hasher = DefaultHasher::new();
info.hash(&mut hasher);
hasher.finish()
}
#[test]
fn equal_snapshots_hash_equally() {
assert!(hash_of(&sample()) == hash_of(&sample()));
}
#[test]
fn differing_snapshots_are_not_equal() {
let mut other = sample();
other.processor = Some("arm".to_owned());
assert!(other != sample());
let mut other = sample();
other.bitness = None;
assert!(other != sample());
}
#[rstest]
#[case::empty(empty())]
#[case::sample(sample())]
#[case::dense(dense())]
fn serde_round_trips(#[case] info: DatabaseInfo) {
let json = serde_json::to_string(&info).unwrap();
let back: DatabaseInfo = serde_json::from_str(&json).unwrap();
assert!(back == info);
}
}