use crate::DecodeError;
use core::cmp::Ordering;
#[cfg(feature = "alloc")]
use crate::max_encoded_len;
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
#[must_use]
pub fn encode(src: &[u8], dst: &mut [u8]) -> usize {
let src_len = src.len();
let mut code_index = 0;
let mut write_index = 1;
let mut code: u8 = 1;
let mut last_byte: u8 = 0;
if src_len != 0 {
let mut read_index = 0;
loop {
let byte = src[read_index];
read_index += 1;
last_byte = byte;
if byte == 0 {
dst[code_index] = code;
code_index = write_index;
write_index += 1;
code = 1;
if read_index >= src_len {
break;
}
} else {
dst[write_index] = byte;
write_index += 1;
code += 1;
if read_index >= src_len {
break;
}
if code == 0xFF {
dst[code_index] = code;
code_index = write_index;
write_index += 1;
code = 1;
}
}
}
}
if last_byte < code {
dst[code_index] = code;
} else {
dst[code_index] = last_byte;
write_index -= 1;
}
write_index
}
#[must_use]
pub fn encode_with_sentinel(src: &[u8], dst: &mut [u8], sentinel: u8) -> usize {
let n = encode(src, dst);
if sentinel != 0 {
for byte in &mut dst[..n] {
*byte ^= sentinel;
}
}
n
}
pub fn decode(src: &[u8], dst: &mut [u8]) -> Result<usize, DecodeError> {
decode_with_sentinel(src, dst, 0)
}
pub fn decode_with_sentinel(
src: &[u8],
dst: &mut [u8],
sentinel: u8,
) -> Result<usize, DecodeError> {
let src_len = src.len();
if src_len == 0 {
return Ok(0);
}
let mut write_index = 0;
let mut index = 0;
loop {
let code = src[index] ^ sentinel;
if code == 0 {
return Err(DecodeError::ZeroByte { index });
}
index += 1;
let block_end = index + usize::from(code) - 1;
let copy_end = block_end.min(src_len);
while index < copy_end {
let byte = src[index] ^ sentinel;
if byte == 0 {
return Err(DecodeError::ZeroByte { index });
}
*dst.get_mut(write_index)
.ok_or(DecodeError::OutputTooSmall)? = byte;
write_index += 1;
index += 1;
}
match block_end.cmp(&src_len) {
Ordering::Greater => {
*dst.get_mut(write_index)
.ok_or(DecodeError::OutputTooSmall)? = code;
write_index += 1;
break;
}
Ordering::Less => {
if code < 0xFF {
*dst.get_mut(write_index)
.ok_or(DecodeError::OutputTooSmall)? = 0;
write_index += 1;
}
}
Ordering::Equal => break,
}
}
Ok(write_index)
}
pub fn decode_in_place(buf: &mut [u8]) -> Result<usize, DecodeError> {
decode_in_place_with_sentinel(buf, 0)
}
pub fn decode_in_place_with_sentinel(buf: &mut [u8], sentinel: u8) -> Result<usize, DecodeError> {
let src_len = buf.len();
if src_len == 0 {
return Ok(0);
}
let mut write_index = 0;
let mut index = 0;
loop {
let code = buf[index] ^ sentinel;
if code == 0 {
return Err(DecodeError::ZeroByte { index });
}
index += 1;
let block_end = index + usize::from(code) - 1;
let copy_end = block_end.min(src_len);
while index < copy_end {
let byte = buf[index] ^ sentinel;
if byte == 0 {
return Err(DecodeError::ZeroByte { index });
}
buf[write_index] = byte;
write_index += 1;
index += 1;
}
match block_end.cmp(&src_len) {
Ordering::Greater => {
buf[write_index] = code;
write_index += 1;
break;
}
Ordering::Less => {
if code < 0xFF {
buf[write_index] = 0;
write_index += 1;
}
}
Ordering::Equal => break,
}
}
Ok(write_index)
}
#[cfg(feature = "alloc")]
#[must_use]
pub fn encode_to_vec(src: &[u8]) -> Vec<u8> {
let mut dst = alloc::vec![0u8; max_encoded_len(src.len())];
let n = encode(src, &mut dst);
dst.truncate(n);
dst
}
#[cfg(feature = "alloc")]
#[must_use]
pub fn encode_to_vec_with_sentinel(src: &[u8], sentinel: u8) -> Vec<u8> {
let mut dst = alloc::vec![0u8; max_encoded_len(src.len())];
let n = encode_with_sentinel(src, &mut dst, sentinel);
dst.truncate(n);
dst
}
#[cfg(feature = "alloc")]
pub fn decode_to_vec(src: &[u8]) -> Result<Vec<u8>, DecodeError> {
let mut dst = alloc::vec![0u8; src.len()];
let n = decode(src, &mut dst)?;
dst.truncate(n);
Ok(dst)
}
#[cfg(feature = "alloc")]
pub fn decode_to_vec_with_sentinel(src: &[u8], sentinel: u8) -> Result<Vec<u8>, DecodeError> {
let mut dst = alloc::vec![0u8; src.len()];
let n = decode_with_sentinel(src, &mut dst, sentinel)?;
dst.truncate(n);
Ok(dst)
}