use std::cmp;
#[derive(Clone, Debug)]
pub struct Map {
length: usize,
entries: Vec<Entry>,
header: Vec<u8>,
data: Vec<u8>,
}
assert_send_and_sync!(Map);
#[derive(Clone, Debug)]
struct Entry {
offset: usize,
length: usize,
field: &'static str,
}
impl Map {
pub(super) fn new(header: Vec<u8>) -> Self {
Map {
length: 0,
entries: Vec::new(),
header,
data: Vec::new(),
}
}
pub(super) fn add(&mut self, field: &'static str, length: usize) {
self.entries.push(Entry {
offset: self.length, length, field
});
self.length += length;
}
pub(super) fn finalize(&mut self, data: Vec<u8>) {
self.data = data;
}
pub fn iter(&self) -> impl Iterator<Item = Field> + Send + Sync {
Iter::new(self)
}
}
#[derive(Clone, Debug)]
pub struct Field<'a> {
name: &'static str,
offset: usize,
data: &'a [u8],
}
assert_send_and_sync!(Field<'_>);
impl<'a> Field<'a> {
fn new(map: &'a Map, i: usize) -> Option<Field<'a>> {
let has_length = map.header.len() > 1;
if i == 0 {
Some(Field {
offset: 0,
name: "CTB",
data: &map.header.as_slice()[..1],
})
} else if i == 1 && has_length {
Some(Field {
offset: 1,
name: "length",
data: &map.header.as_slice()[1..]
})
} else {
let offset_length = if has_length { 1 } else { 0 };
map.entries.get(i - 1 - offset_length).map(|e| {
let len = map.data.len();
let start = cmp::min(len, e.offset);
let end = cmp::min(len, e.offset + e.length);
Field {
offset: map.header.len() + e.offset,
name: e.field,
data: &map.data[start..end],
}
})
}
}
pub fn name(&self) -> &'a str {
self.name
}
pub fn offset(&self) -> usize {
self.offset
}
pub fn as_bytes(&self) -> &'a [u8] {
self.data
}
}
struct Iter<'a> {
map: &'a Map,
i: usize,
}
impl<'a> Iter<'a> {
fn new(map: &'a Map) -> Iter<'a> {
Iter {
map,
i: 0,
}
}
}
impl<'a> Iterator for Iter<'a> {
type Item = Field<'a>;
fn next(&mut self) -> Option<Self::Item> {
let field = Field::new(self.map, self.i);
if field.is_some() {
self.i += 1;
}
field
}
}