use crate::error::{Error, Result};
use super::{arith, rans4x16, ByteReader, MAX_CODEC_LEN};
mod token {
pub const TYPE: u8 = 0;
pub const STRING: u8 = 1;
pub const CHAR: u8 = 2;
pub const DIGITS0: u8 = 3;
pub const DZLEN: u8 = 4;
pub const DUP: u8 = 5;
pub const DIFF: u8 = 6;
pub const DIGITS: u8 = 7;
pub const DELTA: u8 = 8;
pub const DELTA0: u8 = 9;
pub const MATCH: u8 = 10;
pub const END: u8 = 12;
pub const COUNT: usize = 13;
}
const MAX_POSITIONS: usize = 128;
#[derive(Debug, Default, Clone)]
struct TokenStream {
data: Vec<u8>,
pos: usize,
}
impl TokenStream {
fn u8(&mut self, path: &str) -> Result<u8> {
let byte = *self
.data
.get(self.pos)
.ok_or_else(|| Error::corrupt(path, 0, "a name token stream ran out mid-name"))?;
self.pos += 1;
Ok(byte)
}
fn u32(&mut self, path: &str) -> Result<u32> {
let end = self.pos + 4;
let bytes = self
.data
.get(self.pos..end)
.ok_or_else(|| Error::corrupt(path, 0, "a name token stream ran out mid-value"))?;
self.pos = end;
Ok(u32::from_le_bytes(bytes.try_into().expect("four bytes")))
}
fn string(&mut self, path: &str) -> Result<&[u8]> {
let rest = &self.data[self.pos.min(self.data.len())..];
let end = memchr::memchr(0, rest)
.ok_or_else(|| Error::corrupt(path, 0, "a name token string with no terminator"))?;
self.pos += end + 1;
Ok(&rest[..end])
}
}
struct Streams {
positions: Vec<[TokenStream; token::COUNT]>,
}
impl Streams {
fn get(&mut self, position: usize, kind: u8, path: &str) -> Result<&mut TokenStream> {
self.positions
.get_mut(position)
.and_then(|slot| slot.get_mut(kind as usize))
.ok_or_else(|| {
Error::corrupt(
path,
0,
format!(
"a read name wants token stream ({position}, {kind}), which the block \
does not carry"
),
)
})
}
}
#[derive(Default)]
struct Arena {
bytes: Vec<u8>,
spans: Vec<(u32, u32)>,
names: Vec<Name>,
}
#[derive(Clone, Copy)]
struct Name {
tokens: u32,
count: u32,
start: u32,
end: u32,
}
impl Arena {
fn earlier(
&self,
previous: usize,
n: usize,
position: usize,
path: &str,
) -> Result<(u32, u32)> {
if previous == n {
return Err(Error::corrupt(
path,
0,
"the first read name of a block uses a match or delta token, which \
has nothing to refer to",
));
}
let name = self.names.get(previous).ok_or_else(|| {
Error::corrupt(path, 0, format!("a read name refers to name {previous}"))
})?;
if position == 0 || position > name.count as usize {
return Err(Error::corrupt(
path,
0,
format!("a read name matches token {position} of a name that has no such token"),
));
}
Ok(self.spans[name.tokens as usize + position - 1])
}
}
pub fn decode(data: &[u8], path: &str, offset: u64) -> Result<Vec<u8>> {
let mut reader = ByteReader::new(data, path, offset);
let uncompressed_len = reader.u32()? as usize;
let n_names = reader.u32()? as usize;
let use_arith = reader.u8()? != 0;
if uncompressed_len > MAX_CODEC_LEN {
return Err(Error::corrupt(
path,
offset,
format!("a name block declaring {uncompressed_len} bytes, past this reader's ceiling"),
));
}
if n_names > uncompressed_len {
return Err(Error::corrupt(
path,
offset,
format!("a name block of {n_names} names in {uncompressed_len} bytes"),
));
}
let mut streams = read_token_streams(&mut reader, n_names, use_arith, path, offset)?;
let mut arena = Arena {
bytes: Vec::with_capacity(uncompressed_len.min(1 << 22)),
spans: Vec::with_capacity(n_names.saturating_mul(4).min(1 << 22)),
names: Vec::with_capacity(n_names),
};
for n in 0..n_names {
decode_name(n, &mut streams, &mut arena, path)?;
}
Ok(arena.bytes)
}
fn read_token_streams(
reader: &mut ByteReader<'_>,
n_names: usize,
use_arith: bool,
path: &str,
offset: u64,
) -> Result<Streams> {
let mut positions: Vec<[TokenStream; token::COUNT]> = Vec::new();
let mut position: usize = usize::MAX;
while !reader.is_empty() {
let ttype = reader.u8()?;
let is_new_position = ttype & 128 != 0;
let is_duplicate = ttype & 64 != 0;
let kind = ttype & 63;
if kind as usize >= token::COUNT {
return Err(Error::corrupt(
path,
offset,
format!("a name token stream of type {kind}, which §5 does not define"),
));
}
if is_new_position {
position = position.wrapping_add(1);
if position >= MAX_POSITIONS {
return Err(Error::corrupt(
path,
offset,
format!("a read name of more than {MAX_POSITIONS} tokens"),
));
}
positions.resize_with(position + 1, Default::default);
if kind != token::TYPE {
let mut synthetic = vec![token::MATCH; n_names];
if let Some(first) = synthetic.first_mut() {
*first = kind;
}
positions[position][token::TYPE as usize] = TokenStream {
data: synthetic,
pos: 0,
};
}
}
if position == usize::MAX {
return Err(Error::corrupt(
path,
offset,
"a name token stream before any position was opened",
));
}
let decoded = if is_duplicate {
let from_position = reader.u8()? as usize;
let from_kind = reader.u8()? as usize;
positions
.get(from_position)
.and_then(|slot| slot.get(from_kind))
.ok_or_else(|| {
Error::corrupt(
path,
offset,
format!(
"a name token stream duplicating ({from_position}, {from_kind}), \
which comes later or not at all"
),
)
})?
.data
.clone()
} else {
let compressed_len = reader.length()?;
let bytes = reader.take(compressed_len)?;
if use_arith {
arith::decode(bytes, path, offset)?
} else {
rans4x16::decode(bytes, path, offset)?
}
};
positions[position][kind as usize] = TokenStream {
data: decoded,
pos: 0,
};
}
Ok(Streams { positions })
}
fn decode_name(n: usize, streams: &mut Streams, arena: &mut Arena, path: &str) -> Result<()> {
let kind = streams.get(0, token::TYPE, path)?.u8(path)?;
if kind != token::DUP && kind != token::DIFF {
return Err(Error::corrupt(
path,
0,
format!("read name {n} opens with token type {kind}, which is neither DUP nor DIFF"),
));
}
let distance = streams.get(0, kind, path)?.u32(path)? as usize;
let previous = n.checked_sub(distance).ok_or_else(|| {
Error::corrupt(
path,
0,
format!("read name {n} refers back {distance}, past the start of the block"),
)
})?;
if kind == token::DUP {
if previous == n {
return Err(Error::corrupt(
path,
0,
format!("read name {n} is marked a duplicate of itself"),
));
}
let source = *arena.names.get(previous).ok_or_else(|| {
Error::corrupt(path, 0, format!("a read name duplicates name {previous}"))
})?;
let start = arena.bytes.len() as u32;
arena
.bytes
.extend_from_within(source.start as usize..source.end as usize);
let end = arena.bytes.len() as u32;
arena.names.push(Name {
tokens: source.tokens,
count: source.count,
start,
end,
});
arena.bytes.push(0);
return Ok(());
}
let tokens = arena.spans.len() as u32;
let start = arena.bytes.len() as u32;
let mut count = 0u32;
let mut position = 1usize;
loop {
if position >= MAX_POSITIONS {
return Err(Error::corrupt(
path,
0,
format!("a read name of more than {MAX_POSITIONS} tokens"),
));
}
let kind = streams.get(position, token::TYPE, path)?.u8(path)?;
let from = arena.bytes.len() as u32;
match kind {
token::CHAR => {
let byte = streams.get(position, token::CHAR, path)?.u8(path)?;
arena.bytes.push(byte);
}
token::STRING => {
let text = streams.get(position, token::STRING, path)?.string(path)?;
arena.bytes.extend_from_slice(text);
}
token::DIGITS => {
let value = streams.get(position, token::DIGITS, path)?.u32(path)?;
push_digits(&mut arena.bytes, value);
}
token::DIGITS0 => {
let value = streams.get(position, token::DIGITS0, path)?.u32(path)?;
let width = streams.get(position, token::DZLEN, path)?.u8(path)? as usize;
push_padded(&mut arena.bytes, value, width);
}
token::DELTA => {
let span = arena.earlier(previous, n, position, path)?;
let base = parse_number(&arena.bytes[span.0 as usize..span.1 as usize], path)?;
let step = u32::from(streams.get(position, token::DELTA, path)?.u8(path)?);
push_digits(&mut arena.bytes, base.wrapping_add(step));
}
token::DELTA0 => {
let span = arena.earlier(previous, n, position, path)?;
let base = parse_number(&arena.bytes[span.0 as usize..span.1 as usize], path)?;
let width = (span.1 - span.0) as usize;
let step = u32::from(streams.get(position, token::DELTA0, path)?.u8(path)?);
push_padded(&mut arena.bytes, base.wrapping_add(step), width);
}
token::MATCH => {
let span = arena.earlier(previous, n, position, path)?;
arena
.bytes
.extend_from_within(span.0 as usize..span.1 as usize);
}
_ => {}
}
arena.spans.push((from, arena.bytes.len() as u32));
count += 1;
position += 1;
if kind == token::END {
break;
}
}
let end = arena.bytes.len() as u32;
arena.names.push(Name {
tokens,
count,
start,
end,
});
arena.bytes.push(0);
Ok(())
}
fn push_digits(out: &mut Vec<u8>, value: u32) {
let mut digits = [0u8; 10];
let mut rest = value;
let mut at = digits.len();
loop {
at -= 1;
digits[at] = b'0' + (rest % 10) as u8;
rest /= 10;
if rest == 0 {
break;
}
}
out.extend_from_slice(&digits[at..]);
}
fn push_padded(out: &mut Vec<u8>, value: u32, width: usize) {
let start = out.len();
push_digits(out, value);
let written = out.len() - start;
if let Some(pad) = width.checked_sub(written).filter(|pad| *pad > 0) {
out.resize(out.len() + pad, b'0');
out[start..].rotate_right(pad);
}
}
fn parse_number(token: &[u8], path: &str) -> Result<u32> {
let mut value: u32 = 0;
if token.is_empty() {
return Err(Error::corrupt(
path,
0,
"a read name delta against a token with no digits in it",
));
}
for &byte in token {
if !byte.is_ascii_digit() {
return Err(Error::corrupt(
path,
0,
"a read name delta against a token that is not a number",
));
}
value = value.wrapping_mul(10).wrapping_add(u32::from(byte - b'0'));
}
Ok(value)
}
#[cfg(test)]
mod tests {
use super::*;
struct Builder {
n_names: usize,
entries: Vec<(usize, u8, Vec<u8>)>,
}
impl Builder {
fn new(n_names: usize) -> Self {
Self {
n_names,
entries: Vec::new(),
}
}
fn stream(mut self, position: usize, kind: u8, data: Vec<u8>) -> Self {
self.entries.push((position, kind, data));
self
}
fn build(self, uncompressed_len: usize) -> Vec<u8> {
let mut out = Vec::new();
out.extend_from_slice(&(uncompressed_len as u32).to_le_bytes());
out.extend_from_slice(&(self.n_names as u32).to_le_bytes());
out.push(0); let mut last_position = usize::MAX;
for (position, kind, data) in self.entries {
let mut ttype = kind;
if position != last_position {
ttype |= 128;
last_position = position;
}
out.push(ttype);
let mut sub = vec![32u8]; let mut len = data.len() as u32;
let mut groups = Vec::new();
loop {
groups.push((len & 0x7f) as u8);
len >>= 7;
if len == 0 {
break;
}
}
for (i, group) in groups.iter().enumerate().rev() {
sub.push(if i == 0 { *group } else { group | 0x80 });
}
sub.extend_from_slice(&data);
let mut clen = sub.len() as u32;
let mut groups = Vec::new();
loop {
groups.push((clen & 0x7f) as u8);
clen >>= 7;
if clen == 0 {
break;
}
}
for (i, group) in groups.iter().enumerate().rev() {
out.push(if i == 0 { *group } else { group | 0x80 });
}
out.extend_from_slice(&sub);
}
out
}
}
fn names(decoded: &[u8]) -> Vec<String> {
decoded
.split(|b| *b == 0)
.filter(|s| !s.is_empty())
.map(|s| String::from_utf8_lossy(s).into_owned())
.collect()
}
#[test]
fn the_spec_example_names_rebuild_from_their_columns() {
let block = Builder::new(3)
.stream(0, token::TYPE, vec![token::DIFF; 3])
.stream(0, token::DIFF, {
let mut v = Vec::new();
for d in [0u32, 1, 1] {
v.extend_from_slice(&d.to_le_bytes());
}
v
})
.stream(
1,
token::TYPE,
vec![token::STRING, token::MATCH, token::MATCH],
)
.stream(1, token::STRING, b"I17_08765:2:\0".to_vec())
.stream(
2,
token::TYPE,
vec![token::DIGITS, token::MATCH, token::DELTA],
)
.stream(2, token::DIGITS, 123u32.to_le_bytes().to_vec())
.stream(2, token::DELTA, vec![1])
.stream(
3,
token::TYPE,
vec![token::CHAR, token::MATCH, token::MATCH],
)
.stream(3, token::CHAR, b":".to_vec())
.stream(4, token::TYPE, vec![token::DIGITS; 3])
.stream(4, token::DIGITS, {
let mut v = Vec::new();
for d in [61541u32, 1636, 45613] {
v.extend_from_slice(&d.to_le_bytes());
}
v
})
.stream(
5,
token::TYPE,
vec![token::CHAR, token::MATCH, token::MATCH],
)
.stream(5, token::CHAR, b":".to_vec())
.stream(6, token::TYPE, vec![token::DIGITS0; 3])
.stream(6, token::DIGITS0, {
let mut v = Vec::new();
for d in [1763u32, 8611, 16161] {
v.extend_from_slice(&d.to_le_bytes());
}
v
})
.stream(6, token::DZLEN, vec![5, 5, 5])
.stream(
7,
token::TYPE,
vec![token::STRING, token::MATCH, token::MATCH],
)
.stream(7, token::STRING, b"#9\0".to_vec())
.stream(8, token::TYPE, vec![token::END; 3])
.build(3 * 29);
let decoded = decode(&block, "test", 0).expect("decodes");
assert_eq!(
names(&decoded),
vec![
"I17_08765:2:123:61541:01763#9",
"I17_08765:2:123:1636:08611#9",
"I17_08765:2:124:45613:16161#9",
]
);
}
#[test]
fn a_missing_type_stream_is_rebuilt_as_one_type_then_matches() {
let block = Builder::new(3)
.stream(0, token::TYPE, vec![token::DIFF; 3])
.stream(0, token::DIFF, {
let mut v = Vec::new();
for d in [0u32, 1, 1] {
v.extend_from_slice(&d.to_le_bytes());
}
v
})
.stream(1, token::STRING, b"read\0".to_vec())
.stream(2, token::TYPE, vec![token::END; 3])
.build(3 * 5);
assert_eq!(
names(&decode(&block, "test", 0).expect("decodes")),
vec!["read", "read", "read"]
);
}
#[test]
fn a_duplicate_name_copies_the_one_it_names() {
let block = Builder::new(3)
.stream(0, token::TYPE, vec![token::DIFF, token::DIFF, token::DUP])
.stream(0, token::DIFF, {
let mut v = Vec::new();
for d in [0u32, 1] {
v.extend_from_slice(&d.to_le_bytes());
}
v
})
.stream(0, token::DUP, 2u32.to_le_bytes().to_vec())
.stream(1, token::TYPE, vec![token::STRING, token::STRING])
.stream(1, token::STRING, b"aaa\0bbb\0".to_vec())
.stream(2, token::TYPE, vec![token::END; 2])
.build(12);
assert_eq!(
names(&decode(&block, "test", 0).expect("decodes")),
vec!["aaa", "bbb", "aaa"]
);
}
#[test]
fn a_name_can_match_against_a_duplicate() {
let block = Builder::new(4)
.stream(
0,
token::TYPE,
vec![token::DIFF, token::DIFF, token::DUP, token::DIFF],
)
.stream(0, token::DIFF, {
let mut v = Vec::new();
for d in [0u32, 1, 1] {
v.extend_from_slice(&d.to_le_bytes());
}
v
})
.stream(0, token::DUP, 2u32.to_le_bytes().to_vec())
.stream(
1,
token::TYPE,
vec![token::STRING, token::STRING, token::MATCH],
)
.stream(1, token::STRING, b"aaa\0bbb\0".to_vec())
.stream(2, token::TYPE, vec![token::END, token::END, token::CHAR])
.stream(2, token::CHAR, vec![b'X'])
.stream(3, token::TYPE, vec![token::END])
.build(17);
assert_eq!(
names(&decode(&block, "test", 0).expect("decodes")),
vec!["aaa", "bbb", "aaa", "aaaX"]
);
}
#[test]
fn delta0_keeps_the_width_of_the_token_it_follows() {
let block = Builder::new(2)
.stream(0, token::TYPE, vec![token::DIFF; 2])
.stream(0, token::DIFF, {
let mut v = Vec::new();
for d in [0u32, 1] {
v.extend_from_slice(&d.to_le_bytes());
}
v
})
.stream(1, token::TYPE, vec![token::DIGITS0, token::DELTA0])
.stream(1, token::DIGITS0, 98u32.to_le_bytes().to_vec())
.stream(1, token::DZLEN, vec![5])
.stream(1, token::DELTA0, vec![3])
.stream(2, token::TYPE, vec![token::END; 2])
.build(12);
assert_eq!(
names(&decode(&block, "test", 0).expect("decodes")),
vec!["00098", "00101"]
);
}
#[test]
fn a_first_name_that_matches_is_refused_rather_than_read_from_itself() {
let block = Builder::new(1)
.stream(0, token::TYPE, vec![token::DIFF])
.stream(0, token::DIFF, 0u32.to_le_bytes().to_vec())
.stream(1, token::TYPE, vec![token::MATCH])
.stream(2, token::TYPE, vec![token::END])
.build(4);
assert!(decode(&block, "test", 0).is_err());
}
#[test]
fn a_name_duplicating_itself_is_refused_rather_than_looping() {
let block = Builder::new(1)
.stream(0, token::TYPE, vec![token::DUP])
.stream(0, token::DUP, 0u32.to_le_bytes().to_vec())
.build(4);
assert!(decode(&block, "test", 0).is_err());
}
#[test]
fn left_padding_never_truncates_a_number_wider_than_its_field() {
let padded = |value, width| {
let mut out = Vec::new();
push_padded(&mut out, value, width);
out
};
assert_eq!(padded(123, 5), b"00123");
assert_eq!(padded(123, 3), b"123");
assert_eq!(padded(123, 0), b"123");
assert_eq!(padded(0, 2), b"00");
let mut out = b"name:".to_vec();
push_padded(&mut out, 7, 3);
assert_eq!(out, b"name:007");
}
#[test]
fn digits_are_written_without_a_string_in_between() {
let digits = |value| {
let mut out = Vec::new();
push_digits(&mut out, value);
out
};
assert_eq!(digits(0), b"0");
assert_eq!(digits(7), b"7");
assert_eq!(digits(4_294_967_295), b"4294967295");
}
}