use super::header::{Handle, Header};
use super::strings::Strings;
use crate::structs::{DefinedStruct, SMBiosEndOfTable, SMBiosStruct};
use std::fmt;
use std::{
convert::TryInto,
fs::File,
io::{prelude::*, Error, ErrorKind, SeekFrom},
slice::Iter,
};
pub struct UndefinedStruct {
pub header: Header,
pub fields: Vec<u8>,
pub strings: Strings,
}
impl<'a> UndefinedStruct {
pub fn new(raw: &Vec<u8>) -> Self {
match raw.get(Header::LENGTH_OFFSET) {
Some(&header_length) => UndefinedStruct {
header: Header::new(raw[..Header::SIZE].try_into().expect("4 bytes")),
fields: raw.get(..(header_length as usize)).unwrap_or(&[]).to_vec(),
strings: {
Strings::new(
raw.get((header_length as usize)..raw.len() - 2)
.unwrap_or(&[])
.to_vec(),
)
},
},
None => UndefinedStruct {
..Default::default()
},
}
}
pub fn get_field_byte(&self, offset: usize) -> Option<u8> {
match self.fields.get(offset..offset + 1) {
Some(val) => Some(val[0]),
None => None,
}
}
pub fn get_field_word(&self, offset: usize) -> Option<u16> {
match self.fields.get(offset..offset + 2) {
Some(val) => Some(u16::from_le_bytes(val.try_into().expect("u16 is 2 bytes"))),
None => None,
}
}
pub fn get_field_handle(&self, offset: usize) -> Option<Handle> {
match self.fields.get(offset..offset + Handle::SIZE) {
Some(val) => Some(Handle(u16::from_le_bytes(
val.try_into().expect("u16 is 2 bytes"),
))),
None => None,
}
}
pub fn get_field_dword(&self, offset: usize) -> Option<u32> {
match self.fields.get(offset..offset + 4) {
Some(val) => Some(u32::from_le_bytes(val.try_into().expect("u32 is 4 bytes"))),
None => None,
}
}
pub fn get_field_qword(&self, offset: usize) -> Option<u64> {
match self.fields.get(offset..offset + 8) {
Some(val) => Some(u64::from_le_bytes(val.try_into().expect("u64 is 8 bytes"))),
None => None,
}
}
pub fn get_field_string(&self, offset: usize) -> Option<String> {
match self.get_field_byte(offset) {
Some(val) => self.strings.get_string(val),
None => None,
}
}
pub fn get_field_data(&self, start_index: usize, end_index: usize) -> Option<&[u8]> {
return self.fields.get(start_index..end_index);
}
pub fn as_type<T: SMBiosStruct<'a>>(&'a self) -> Option<T> {
if T::STRUCT_TYPE == self.header.struct_type() {
Some(T::new(self))
} else {
None
}
}
pub fn defined_struct(&self) -> DefinedStruct<'_> {
self.into()
}
}
impl fmt::Debug for UndefinedStruct {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
let fields = &self.fields[Header::SIZE..];
fmt.debug_struct(std::any::type_name::<UndefinedStruct>())
.field("header", &self.header)
.field("fields", &fields)
.field("strings", &self.strings)
.finish()
}
}
impl Default for UndefinedStruct {
fn default() -> Self {
let v: [u8; 4] = [0; 4];
UndefinedStruct {
header: Header::new(v),
fields: (&[]).to_vec(),
strings: { Strings::new((&[]).to_vec()) },
}
}
}
#[derive(Debug)]
pub struct UndefinedStructTable(Vec<UndefinedStruct>);
impl<'a> UndefinedStructTable {
fn new() -> UndefinedStructTable {
UndefinedStructTable(Vec::new())
}
fn add(&mut self, elem: UndefinedStruct) {
self.0.push(elem);
}
pub fn iter(&self) -> Iter<'_, UndefinedStruct> {
self.0.iter()
}
pub fn defined_struct_iter<T>(&'a self) -> impl Iterator<Item = T> + 'a
where
T: SMBiosStruct<'a>,
{
self.iter()
.take_while(|undefined_struct| {
undefined_struct.header.struct_type() != SMBiosEndOfTable::STRUCT_TYPE
})
.filter_map(|undefined_struct| {
if undefined_struct.header.struct_type() == T::STRUCT_TYPE {
Some(T::new(undefined_struct))
} else {
None
}
})
}
pub fn all<T, F>(&'a self, f: F) -> bool
where
T: SMBiosStruct<'a>,
F: FnMut(T) -> bool,
{
self.defined_struct_iter().all(f)
}
pub fn any<T, F>(&'a self, f: F) -> bool
where
T: SMBiosStruct<'a>,
F: FnMut(T) -> bool,
{
self.defined_struct_iter().any(f)
}
pub fn first<T>(&'a self) -> Option<T>
where
T: SMBiosStruct<'a>,
{
self.defined_struct_iter().next()
}
pub fn find<T, P>(&'a self, predicate: P) -> Option<T>
where
T: SMBiosStruct<'a>,
P: FnMut(&T) -> bool,
{
self.defined_struct_iter().find(predicate)
}
pub fn find_map<A, B, F>(&'a self, f: F) -> Option<B>
where
A: SMBiosStruct<'a>,
F: FnMut(A) -> Option<B>,
{
self.defined_struct_iter().find_map(f)
}
pub fn filter<T: 'a, P: 'a>(&'a self, predicate: P) -> impl Iterator<Item = T> + 'a
where
T: SMBiosStruct<'a>,
P: FnMut(&T) -> bool,
{
self.defined_struct_iter().filter(predicate)
}
pub fn map<A: 'a, B, F: 'a>(&'a self, f: F) -> impl Iterator<Item = B> + 'a
where
A: SMBiosStruct<'a>,
F: FnMut(A) -> B,
{
self.defined_struct_iter().map(f)
}
pub fn filter_map<A: 'a, B, F: 'a>(&'a self, f: F) -> impl Iterator<Item = B> + 'a
where
A: SMBiosStruct<'a>,
F: FnMut(A) -> Option<B>,
{
self.defined_struct_iter().filter_map(f)
}
pub fn find_by_handle(&'a self, handle: &Handle) -> Option<&'a UndefinedStruct> {
self.iter()
.find(|smbios_struct| smbios_struct.header.handle() == *handle)
.and_then(|undefined_struct| Some(undefined_struct))
}
pub fn collect<T>(&'a self) -> Vec<T>
where
T: SMBiosStruct<'a>,
{
self.defined_struct_iter().collect()
}
pub fn try_load_from_file_offset(
file: &mut File,
table_offset: u64,
table_len: usize,
) -> Result<Self, Error> {
if table_len < Header::SIZE + 2 {
return Err(Error::new(
ErrorKind::InvalidData,
format!("The table has an invalid size: {}", table_len),
));
}
file.seek(SeekFrom::Start(table_offset))?;
let mut table = Vec::with_capacity(table_len);
table.resize(table_len, 0);
file.read_exact(&mut table)?;
Ok(table.into())
}
}
impl From<Vec<u8>> for UndefinedStructTable {
fn from(data: Vec<u8>) -> Self {
const DOUBLE_ZERO_SIZE: usize = 2usize;
const MIN_STRUCT_SIZE: usize = Header::SIZE + DOUBLE_ZERO_SIZE;
let mut result = Self::new();
let mut current_index = 0usize;
loop {
match data.get(current_index..current_index + MIN_STRUCT_SIZE) {
Some(min_struct) => {
let struct_len = min_struct[Header::LENGTH_OFFSET] as usize;
if struct_len < Header::SIZE {
break;
}
match data.get(current_index + struct_len..) {
Some(strings_etc) => {
match strings_etc
.windows(DOUBLE_ZERO_SIZE)
.position(|x| x[0] == x[1] && x[1] == 0)
{
Some(double_zero_position) => {
let next_index = current_index
+ struct_len
+ double_zero_position
+ DOUBLE_ZERO_SIZE;
result.add(UndefinedStruct::new(
&data[current_index..next_index].to_vec(),
));
current_index = next_index;
}
None => break,
}
}
None => break,
};
}
None => break,
}
}
result
}
}
impl IntoIterator for UndefinedStructTable {
type Item = UndefinedStruct;
type IntoIter = std::vec::IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}