use alloc::vec::Vec;
use super::super::FnEncoder;
use crate::compressor::core::data::Stream;
use crate::compressor::core::error::NodeError;
use crate::compressor::core::exec::NodeCx;
use crate::shared::numeric::{elements, max_value_for_width, push_le};
use crate::shared::varint;
const INDICES_PID: i32 = crate::graph::params::sentinel::INDICES;
const VALUE_PID: i32 = crate::graph::params::sentinel::VALUE;
pub(crate) static SENTINEL_BYTE: FnEncoder = FnEncoder(sentinel_byte);
pub(crate) static SENTINEL_NUM: FnEncoder = FnEncoder(sentinel_num);
fn byte_encode(bytes: &[u8], width: usize) -> (Vec<u8>, Vec<u8>) {
let mut values = Vec::with_capacity(bytes.len() / width);
let mut exceptions = Vec::new();
for value in elements(bytes, width) {
if value < 255 {
values.push(value as u8);
} else {
values.push(255);
push_le(&mut exceptions, value, width);
}
}
(values, exceptions)
}
fn sentinel_byte<'a>(cx: &mut NodeCx<'_, 'a>, inputs: &[Stream<'a>]) -> Result<(), NodeError> {
let input = &inputs[0];
let width = input.width();
if width == 1 {
return Err(NodeError::InvalidInput(
"sentinel_byte does not accept 1-byte inputs",
));
}
let (values, exceptions) = byte_encode(input.bytes(), width);
cx.emit(0, 1, values)?;
cx.emit(1, width, exceptions)?;
Ok(())
}
fn encode(
bytes: &[u8],
width: usize,
indices: &[u64],
sentinel: u64,
) -> Result<(Vec<u8>, Vec<u8>), NodeError> {
let mut values = Vec::with_capacity(bytes.len());
let mut exceptions = Vec::with_capacity(indices.len() * width);
let mut next = 0;
for (position, value) in elements(bytes, width).enumerate() {
if indices.get(next) == Some(&(position as u64)) {
push_le(&mut values, sentinel, width);
push_le(&mut exceptions, value, width);
next += 1;
} else if value == sentinel {
return Err(NodeError::InvalidInput(
"Sentinel encode validation failed",
));
} else {
push_le(&mut values, value, width);
}
}
if next != indices.len() {
return Err(NodeError::InvalidInput(
"Sentinel encode validation failed",
));
}
Ok((values, exceptions))
}
fn sentinel_num<'a>(cx: &mut NodeCx<'_, 'a>, inputs: &[Stream<'a>]) -> Result<(), NodeError> {
let input = &inputs[0];
let width = input.width();
let indices: Vec<u64> = elements(
cx.copy_param(INDICES_PID)
.ok_or(NodeError::InvalidParam("Missing indices"))?,
8,
)
.collect();
let max_value = max_value_for_width(width);
let mut sentinel = max_value;
if let Some(bytes) = super::param_ref(cx, VALUE_PID) {
let bytes: [u8; 8] = bytes
.try_into()
.map_err(|_| NodeError::InvalidParam("Sentinel value must be 64 bits"))?;
sentinel = u64::from_le_bytes(bytes);
}
sentinel &= max_value;
let (values, exceptions) = encode(input.bytes(), width, &indices, sentinel)?;
if sentinel != max_value {
let mut header = Vec::new();
varint::push(&mut header, sentinel);
cx.send_header(&header)?;
}
cx.emit(0, width, values)?;
cx.emit(1, width, exceptions)?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn large_values_become_exceptions() {
let bytes: Vec<u8> = [3u32, 254, 255, 70_000]
.iter()
.flat_map(|v| v.to_le_bytes())
.collect();
let (values, exceptions) = byte_encode(&bytes, 4);
assert_eq!(values, [3, 254, 255, 255]);
assert_eq!(
exceptions,
[255u32, 70_000].iter().flat_map(|v| v.to_le_bytes()).collect::<Vec<_>>()
);
assert_eq!(byte_encode(&[], 2), (Vec::new(), Vec::new()));
}
#[test]
fn indices_mark_exceptions_and_are_validated() {
let bytes = [1u8, 9, 3, 9, 5];
assert_eq!(encode(&bytes, 1, &[1, 3], 200), Ok((alloc::vec![1, 200, 3, 200, 5], alloc::vec![9, 9])));
assert_eq!(encode(&bytes, 1, &[], 200).map(|(v, _)| v), Ok(bytes.to_vec()));
assert!(encode(&bytes, 1, &[1], 9).is_err());
assert!(encode(&bytes, 1, &[1, 9], 200).is_err());
assert!(encode(&bytes, 1, &[3, 1], 200).is_err());
}
}