#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
#[cfg(feature = "std")]
use std::vec::Vec;
use crate::error::DecodeError;
#[inline]
fn get_tag(ctrl: &[u8], i: usize) -> u8 {
(ctrl[i / 4] >> ((i % 4) * 2)) & 0x03
}
pub(super) fn encode_into_1234(values: &[u64], out: &mut Vec<u8>) {
let n = values.len();
if n == 0 {
return;
}
let ctrl_len = n.div_ceil(4);
let ctrl_start = out.len();
out.resize(ctrl_start + ctrl_len, 0u8);
for (i, &v) in values.iter().enumerate() {
let (tag, count): (u8, usize) = if v <= 0xFF {
(0, 1)
} else if v <= 0xFFFF {
(1, 2)
} else if v <= 0xFF_FFFF {
(2, 3)
} else {
(3, 4)
};
out[ctrl_start + i / 4] |= tag << ((i % 4) * 2);
out.extend_from_slice(&(v as u32).to_le_bytes()[..count]);
}
}
pub(super) fn decode_1234_from_raw(
ctrl: &[u8],
data: &[u8],
n: usize,
out: &mut Vec<u64>,
) -> Result<(), DecodeError> {
out.reserve(n);
let mut pos = 0usize;
for i in 0..n {
let tag = (ctrl[i / 4] >> ((i % 4) * 2)) & 3;
let count = (tag + 1) as usize;
if pos + count > data.len() {
return Err(DecodeError::DataTruncated { index: i });
}
let mut bytes = [0u8; 4];
bytes[..count].copy_from_slice(&data[pos..pos + count]);
out.push(u32::from_le_bytes(bytes) as u64);
pos += count;
}
Ok(())
}
pub(super) fn decode_1248_from_raw(
ctrl: &[u8],
data: &[u8],
n: usize,
out: &mut Vec<u64>,
) -> Result<(), DecodeError> {
out.reserve(n);
let mut pos = 0usize;
for i in 0..n {
let tag = (ctrl[i / 4] >> ((i % 4) * 2)) & 3;
let count = WIDTHS_1248[tag as usize];
if pos + count > data.len() {
return Err(DecodeError::DataTruncated { index: i });
}
let mut bytes = [0u8; 8];
bytes[..count].copy_from_slice(&data[pos..pos + count]);
out.push(u64::from_le_bytes(bytes));
pos += count;
}
Ok(())
}
pub(super) fn decode_into_1234(
data: &[u8],
n: usize,
out: &mut Vec<u64>,
) -> Result<(), DecodeError> {
if n == 0 {
return Ok(());
}
let ctrl_len = n.div_ceil(4);
if data.len() < ctrl_len {
return Err(DecodeError::ControlStreamTooShort {
need: ctrl_len,
have: data.len(),
});
}
let ctrl = &data[..ctrl_len];
let mut pos = ctrl_len;
out.reserve(n);
for i in 0..n {
let tag = get_tag(ctrl, i);
let count = (tag + 1) as usize;
if pos + count > data.len() {
return Err(DecodeError::DataTruncated { index: i });
}
let mut bytes = [0u8; 4];
bytes[..count].copy_from_slice(&data[pos..pos + count]);
out.push(u32::from_le_bytes(bytes) as u64);
pos += count;
}
Ok(())
}
#[allow(dead_code)]
pub(super) fn encoded_data_len_1234(ctrl: &[u8], n: usize) -> usize {
let mut sum = n;
let full = n / 4;
let rem = n % 4;
for &b in &ctrl[..full] {
sum += ((b & 0x03) + ((b >> 2) & 0x03) + ((b >> 4) & 0x03) + ((b >> 6) & 0x03)) as usize;
}
for j in 0..rem {
sum += ((ctrl[full] >> (j * 2)) & 0x03) as usize;
}
sum
}
const WIDTHS_1248: [usize; 4] = [1, 2, 4, 8];
pub(super) fn encode_into_1248(values: &[u64], out: &mut Vec<u8>) {
let n = values.len();
if n == 0 {
return;
}
let ctrl_len = n.div_ceil(4);
let ctrl_start = out.len();
out.resize(ctrl_start + ctrl_len, 0u8);
for (i, &v) in values.iter().enumerate() {
let (tag, count): (u8, usize) = if v <= 0xFF {
(0, 1)
} else if v <= 0xFFFF {
(1, 2)
} else if v <= 0xFFFF_FFFF {
(2, 4)
} else {
(3, 8)
};
out[ctrl_start + i / 4] |= tag << ((i % 4) * 2);
out.extend_from_slice(&v.to_le_bytes()[..count]);
}
}
pub(super) fn decode_into_1248(
data: &[u8],
n: usize,
out: &mut Vec<u64>,
) -> Result<(), DecodeError> {
if n == 0 {
return Ok(());
}
let ctrl_len = n.div_ceil(4);
if data.len() < ctrl_len {
return Err(DecodeError::ControlStreamTooShort {
need: ctrl_len,
have: data.len(),
});
}
let ctrl = &data[..ctrl_len];
let mut pos = ctrl_len;
out.reserve(n);
for i in 0..n {
let tag = get_tag(ctrl, i);
let count = WIDTHS_1248[tag as usize];
if pos + count > data.len() {
return Err(DecodeError::DataTruncated { index: i });
}
let mut bytes = [0u8; 8];
bytes[..count].copy_from_slice(&data[pos..pos + count]);
out.push(u64::from_le_bytes(bytes));
pos += count;
}
Ok(())
}
#[allow(dead_code)]
pub(super) fn encoded_data_len_1248(ctrl: &[u8], n: usize) -> usize {
let mut sum = 0usize;
let full = n / 4;
let rem = n % 4;
for &b in &ctrl[..full] {
for j in 0..4 {
sum += WIDTHS_1248[((b >> (j * 2)) & 0x03) as usize];
}
}
for j in 0..rem {
sum += WIDTHS_1248[((ctrl[full] >> (j * 2)) & 0x03) as usize];
}
sum
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(not(feature = "std"))]
use alloc::vec;
fn enc_1234(v: &[u64]) -> Vec<u8> {
let mut out = Vec::new();
encode_into_1234(v, &mut out);
out
}
fn dec_1234(d: &[u8], n: usize) -> Result<Vec<u64>, DecodeError> {
let mut out = Vec::new();
decode_into_1234(d, n, &mut out)?;
Ok(out)
}
fn enc_1248(v: &[u64]) -> Vec<u8> {
let mut out = Vec::new();
encode_into_1248(v, &mut out);
out
}
fn dec_1248(d: &[u8], n: usize) -> Result<Vec<u64>, DecodeError> {
let mut out = Vec::new();
decode_into_1248(d, n, &mut out)?;
Ok(out)
}
#[test]
fn coder1234_spec_example_encode() {
let got = enc_1234(&[0, 0xFF_FFFF, 0xFFFF_FFFF]);
assert_eq!(got, [0x38, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]);
}
#[test]
fn coder1234_spec_example_decode() {
let data = [0x38u8, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF];
assert_eq!(dec_1234(&data, 3).unwrap(), [0, 0xFF_FFFF, 0xFFFF_FFFF]);
}
#[test]
fn coder1234_roundtrip_empty() {
assert_eq!(dec_1234(&enc_1234(&[]), 0).unwrap(), &[] as &[u64]);
}
#[test]
fn coder1234_roundtrip_boundaries() {
let vals = [
0u64,
0xFF,
0x100,
0xFFFF,
0x10000,
0xFF_FFFF,
0x100_0000,
u32::MAX as u64,
];
assert_eq!(dec_1234(&enc_1234(&vals), vals.len()).unwrap(), vals);
}
#[test]
fn coder1234_data_len() {
let vals = [0u64, 0xFF_FFFF, 0xFFFF_FFFF];
let enc = enc_1234(&vals);
let ctrl_len = vals.len().div_ceil(4);
assert_eq!(
encoded_data_len_1234(&enc[..ctrl_len], vals.len()),
enc.len() - ctrl_len
);
}
#[test]
fn coder1234_truncates_large_values() {
let large = 0x1_DEAD_BEEFu64; let enc = enc_1234(&[large]);
assert_eq!(dec_1234(&enc, 1).unwrap(), [0xDEAD_BEEFu64]);
}
#[test]
fn coder1234_error_ctrl_too_short() {
assert!(matches!(
dec_1234(&[0x00], 5),
Err(DecodeError::ControlStreamTooShort { need: 2, have: 1 })
));
}
#[test]
fn coder1234_error_data_truncated() {
assert!(matches!(
dec_1234(&[0x01], 1),
Err(DecodeError::DataTruncated { index: 0 })
));
}
#[test]
fn coder1248_spec_example_encode() {
let got = enc_1248(&[0u64, 0x1_0000_0000u64]);
assert_eq!(
got,
[0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00]
);
}
#[test]
fn coder1248_spec_example_decode() {
let data = [0x0Cu8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00];
assert_eq!(dec_1248(&data, 2).unwrap(), [0u64, 0x1_0000_0000u64]);
}
#[test]
fn coder1248_roundtrip_empty() {
assert_eq!(dec_1248(&enc_1248(&[]), 0).unwrap(), &[] as &[u64]);
}
#[test]
fn coder1248_roundtrip_boundaries() {
let vals = [
0u64,
0xFF,
0x100,
0xFFFF,
0x10000,
0xFFFF_FFFF,
0x1_0000_0000,
u64::MAX,
];
assert_eq!(dec_1248(&enc_1248(&vals), vals.len()).unwrap(), vals);
}
#[test]
fn coder1248_mid_range_uses_4_bytes() {
let vals = [0x10000u64, 0xFF_FFFFu64];
let enc = enc_1248(&vals);
assert_eq!(enc[0], 0x0A);
assert_eq!(enc.len(), 1 + 8);
assert_eq!(dec_1248(&enc, 2).unwrap(), vals);
}
#[test]
fn coder1248_data_len() {
let vals = [0u64, 0x1_0000_0000u64];
let enc = enc_1248(&vals);
let ctrl_len = vals.len().div_ceil(4);
assert_eq!(
encoded_data_len_1248(&enc[..ctrl_len], vals.len()),
enc.len() - ctrl_len
);
}
#[test]
fn coder1248_error_ctrl_too_short() {
assert!(matches!(
dec_1248(&[0x00], 5),
Err(DecodeError::ControlStreamTooShort { need: 2, have: 1 })
));
}
#[test]
fn coder1248_error_data_truncated() {
assert!(matches!(
dec_1248(&[0x0C], 1),
Err(DecodeError::DataTruncated { index: 0 })
));
}
}