use crate::EnumerationAnomaly;
use crate::observation::Source;
pub(crate) const SIZING_ATTEMPTS: u32 = 3;
const _: () = assert!(
SIZING_ATTEMPTS >= 2,
"SIZING_ATTEMPTS below 2 absorbs no buffer growth at all"
);
#[derive(Clone, Copy)]
pub(crate) struct Record {
base: *const u8,
size: usize,
offset: usize,
}
impl Record {
pub(crate) fn size(self) -> usize {
self.size
}
pub(crate) fn offset(self) -> usize {
self.offset
}
pub(crate) unsafe fn read<T: Copy>(self, offset: usize) -> Option<T> {
let end = offset.checked_add(size_of::<T>())?;
if end > self.size {
return None;
}
Some(unsafe { self.base.add(offset).cast::<T>().read_unaligned() })
}
pub(crate) unsafe fn read_array<T: Copy>(self, offset: usize, count: usize) -> (Vec<T>, bool) {
let mut out = Vec::with_capacity(count.min(self.size / size_of::<T>().max(1)));
for index in 0..count {
let Some(at) = index
.checked_mul(size_of::<T>())
.and_then(|o| o.checked_add(offset))
else {
return (out, false);
};
match unsafe { self.read::<T>(at) } {
Some(value) => out.push(value),
None => return (out, false),
}
}
(out, true)
}
}
pub(crate) struct RecordWalk {
base: *const u8,
length: usize,
offset: usize,
size_offset: usize,
minimum: usize,
source: Source,
anomaly: Option<EnumerationAnomaly>,
}
impl RecordWalk {
pub(crate) unsafe fn new(
base: *const u8,
length: u32,
size_offset: usize,
minimum: usize,
source: Source,
) -> Self {
Self {
base,
length: length as usize,
offset: 0,
size_offset,
minimum,
source,
anomaly: None,
}
}
pub(crate) fn anomaly(&self) -> Option<EnumerationAnomaly> {
self.anomaly.clone()
}
}
impl Iterator for RecordWalk {
type Item = Record;
fn next(&mut self) -> Option<Record> {
if self.anomaly.is_some() {
return None;
}
let header = self.size_offset.saturating_add(size_of::<u32>());
let Some(header_end) = self.offset.checked_add(header) else {
self.anomaly = Some(EnumerationAnomaly::overruns(
self.source,
self.offset,
header,
self.length - self.offset,
));
return None;
};
if header_end > self.length {
if self.offset < self.length {
self.anomaly = Some(EnumerationAnomaly::trailing_bytes(
self.source,
self.offset,
self.length - self.offset,
));
}
return None;
}
let size = unsafe {
self.base
.add(self.offset + self.size_offset)
.cast::<u32>()
.read_unaligned()
} as usize;
if size < self.minimum {
self.anomaly = Some(EnumerationAnomaly::undersized(
self.source,
self.offset,
size,
self.minimum,
));
return None;
}
let Some(end) = self.offset.checked_add(size) else {
self.anomaly = Some(EnumerationAnomaly::overruns(
self.source,
self.offset,
size,
self.length - self.offset,
));
return None;
};
if end > self.length {
self.anomaly = Some(EnumerationAnomaly::overruns(
self.source,
self.offset,
size,
self.length - self.offset,
));
return None;
}
let record = Record {
base: unsafe { self.base.add(self.offset) },
size,
offset: self.offset,
};
self.offset = end;
Some(record)
}
}
#[cfg(test)]
mod tests;