use crate::buf::WriteBuf;
use crate::error::{Error, Result};
const MAX_RUN: usize = 254;
#[inline]
#[must_use]
pub const fn max_encoded_len(len: usize) -> usize {
let overhead = if len == 0 { 1 } else { len.div_ceil(MAX_RUN) };
len.saturating_add(overhead)
}
pub fn encode(src: &[u8], out: &mut WriteBuf<'_>) -> Result<()> {
let dst = out.spare_mut();
if dst.len() < max_encoded_len(src.len()) && dst.len() < encoded_len(src) {
return Err(Error::BufferFull);
}
let mut code_at = 0;
let mut write = 1;
let mut code: u8 = 1;
for (i, &byte) in src.iter().enumerate() {
if byte != 0 {
dst[write] = byte;
write += 1;
code += 1;
}
if byte == 0 || (code == 0xFF && i + 1 < src.len()) {
dst[code_at] = code;
code_at = write;
write += 1;
code = 1;
}
}
dst[code_at] = code;
out.commit(write);
Ok(())
}
fn encoded_len(src: &[u8]) -> usize {
let mut len = 1;
let mut code: u8 = 1;
for (i, &byte) in src.iter().enumerate() {
if byte != 0 {
len += 1;
code += 1;
}
if byte == 0 || (code == 0xFF && i + 1 < src.len()) {
len += 1;
code = 1;
}
}
len
}
pub fn decode(src: &[u8], out: &mut WriteBuf<'_>) -> Result<()> {
let mut len = 0;
for_each_run(src, |run, zero_follows| {
len += run.len() + usize::from(zero_follows);
Ok(())
})?;
let Some(dst) = out.spare_mut().get_mut(..len) else {
return Err(Error::BufferFull);
};
let mut write = 0;
for_each_run(src, |run, zero_follows| {
dst[write..write + run.len()].copy_from_slice(run);
write += run.len();
if zero_follows {
dst[write] = 0;
write += 1;
}
Ok(())
})?;
out.commit(len);
Ok(())
}
pub fn decode_in_place(buf: &mut [u8]) -> Result<usize> {
validate(buf)?;
let mut read = 0;
let mut write = 0;
while read < buf.len() {
let code = usize::from(buf[read]);
let run_start = read + 1;
let run_end = read + code;
buf.copy_within(run_start..run_end, write);
write += code - 1;
read = run_end;
if code != 0xFF && read < buf.len() {
buf[write] = 0;
write += 1;
}
}
Ok(write)
}
fn validate(src: &[u8]) -> Result<()> {
for_each_run(src, |_, _| Ok(()))
}
fn for_each_run<'a>(
src: &'a [u8],
mut emit: impl FnMut(&'a [u8], bool) -> Result<()>,
) -> Result<()> {
if src.is_empty() {
return Err(Error::InvalidEncoding);
}
let mut read = 0;
while read < src.len() {
let code = usize::from(src[read]);
if code == 0 {
return Err(Error::InvalidEncoding);
}
let run = src
.get(read + 1..read + code)
.ok_or(Error::InvalidEncoding)?;
if run.contains(&0) {
return Err(Error::InvalidEncoding);
}
read += code;
emit(run, code != 0xFF && read < src.len())?;
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
fn encode_vec(src: &[u8]) -> Vec<u8> {
let mut out = vec![0u8; max_encoded_len(src.len())];
let mut buf = WriteBuf::new(&mut out);
encode(src, &mut buf).unwrap();
let n = buf.position();
out.truncate(n);
out
}
fn decode_vec(src: &[u8]) -> Result<Vec<u8>> {
let mut out = vec![0u8; src.len()];
let mut buf = WriteBuf::new(&mut out);
decode(src, &mut buf)?;
let n = buf.position();
out.truncate(n);
Ok(out)
}
#[test]
fn wikipedia_vectors() {
let cases: [(&[u8], &[u8]); 6] = [
(&[], &[0x01]),
(&[0x00], &[0x01, 0x01]),
(&[0x00, 0x00], &[0x01, 0x01, 0x01]),
(&[0x00, 0x11, 0x00], &[0x01, 0x02, 0x11, 0x01]),
(&[0x11, 0x22, 0x00, 0x33], &[0x03, 0x11, 0x22, 0x02, 0x33]),
(&[0x11, 0x22, 0x33, 0x44], &[0x05, 0x11, 0x22, 0x33, 0x44]),
];
for (raw, encoded) in cases {
assert_eq!(encode_vec(raw), encoded, "encode {raw:?}");
assert_eq!(decode_vec(encoded).unwrap(), raw, "decode {encoded:?}");
let mut block = encoded.to_vec();
let len = decode_in_place(&mut block).unwrap();
assert_eq!(&block[..len], raw);
}
}
#[test]
fn long_runs() {
let raw: Vec<u8> = (1..=254).map(|i| i as u8).collect();
let encoded = encode_vec(&raw);
assert_eq!(encoded.len(), 255);
assert_eq!(encoded[0], 0xFF);
assert_eq!(decode_vec(&encoded).unwrap(), raw);
let raw: Vec<u8> = (0..255).map(|i| (i % 255) as u8 + 1).collect();
let encoded = encode_vec(&raw);
assert_eq!(encoded.len(), max_encoded_len(255));
assert_eq!(&encoded[255..], &[0x02, raw[254]]);
assert_eq!(decode_vec(&encoded).unwrap(), raw);
let mut raw: Vec<u8> = vec![0x7F; 254];
raw.push(0);
let encoded = encode_vec(&raw);
assert_eq!(decode_vec(&encoded).unwrap(), raw);
}
#[test]
fn round_trip_mixed_lengths() {
for len in [0usize, 1, 253, 254, 255, 508, 509, 1000] {
let raw: Vec<u8> = (0..len).map(|i| (i * 7 % 5) as u8).collect();
let encoded = encode_vec(&raw);
assert!(encoded.len() <= max_encoded_len(len));
assert!(!encoded.contains(&0));
assert_eq!(decode_vec(&encoded).unwrap(), raw);
}
}
#[test]
fn malformed_blocks() {
let cases: [&[u8]; 5] = [
&[],
&[0x00],
&[0x03, 0x11],
&[0x02, 0x00],
&[0x02, 0x11, 0x00, 0x01],
];
for bad in cases {
assert_eq!(decode_vec(bad), Err(Error::InvalidEncoding), "{bad:?}");
let mut block = bad.to_vec();
assert_eq!(decode_in_place(&mut block), Err(Error::InvalidEncoding));
}
}
#[test]
fn encode_is_atomic_on_buffer_full() {
for (src, room) in [
(&[0x11u8, 0x22][..], 2usize),
(&[0x00, 0x00], 2),
(&[0x11], 0),
] {
let mut out = vec![0xEEu8; room];
let mut buf = WriteBuf::new(&mut out);
assert_eq!(encode(src, &mut buf), Err(Error::BufferFull), "{src:?}");
assert_eq!(buf.position(), 0);
assert!(
out.iter().all(|&b| b == 0xEE),
"nothing written for {src:?}"
);
}
let raw: Vec<u8> = vec![0x00; 300];
let mut out = vec![0u8; encoded_len(&raw)];
assert!(out.len() < max_encoded_len(raw.len()) || raw.is_empty());
encode(&raw, &mut WriteBuf::new(&mut out)).unwrap();
}
#[test]
fn encoded_len_matches_encode() {
for len in [0usize, 1, 253, 254, 255, 508, 600] {
for zero_every in [1usize, 2, 7, 1000] {
let raw: Vec<u8> = (0..len).map(|i| u8::from(i % zero_every != 0)).collect();
let written = encode_vec(&raw).len();
assert_eq!(encoded_len(&raw), written, "len={len} every={zero_every}");
}
}
}
#[test]
fn non_canonical_trailing_block_is_accepted() {
let mut long = vec![0xFFu8];
long.extend(core::iter::repeat_n(0x07u8, 254));
let mut with_empty = long.clone();
with_empty.push(0x01);
assert_eq!(decode_vec(&long).unwrap(), decode_vec(&with_empty).unwrap());
assert_eq!(encode_vec(&decode_vec(&with_empty).unwrap()), long);
}
#[test]
fn decode_is_atomic_on_error() {
let mut out = [0xEEu8; 2];
let mut buf = WriteBuf::new(&mut out);
assert_eq!(decode(&[0x04, 1, 2, 3], &mut buf), Err(Error::BufferFull));
assert_eq!(
decode(&[0x02, 1, 0x02, 2], &mut buf),
Err(Error::BufferFull)
);
assert_eq!(
decode(&[0x02, 1, 0x05], &mut buf),
Err(Error::InvalidEncoding)
);
assert_eq!(buf.position(), 0);
assert_eq!(out, [0xEE, 0xEE], "nothing is written on error");
let mut block = [0x02, 0x11, 0x03, 0x22];
assert_eq!(decode_in_place(&mut block), Err(Error::InvalidEncoding));
assert_eq!(
block,
[0x02, 0x11, 0x03, 0x22],
"in-place input unchanged on error"
);
}
#[test]
fn max_encoded_len_saturates() {
assert_eq!(max_encoded_len(usize::MAX), usize::MAX);
}
}