use crate::Ticks;
use num_enum::{IntoPrimitive, TryFromPrimitive};
use std::{error::Error, fmt, iter, mem};
#[derive(Debug, Clone)]
#[repr(transparent)]
pub struct EncodedFlux(Vec<u8>);
impl EncodedFlux {
pub fn new(
flux_timings: impl IntoIterator<Item = Ticks>,
nfa_threshold: Ticks,
nfa_period: Ticks,
) -> Self {
fn write_28bit(encoded: &mut Vec<u8>, value: u32) {
assert!(
value & 0xf0000000 == 0,
"flux-op operand overflowed 28 bits"
);
encoded.extend(
[value << 1, value >> 6, value >> 13, value >> 20]
.into_iter()
.map(|value| (1 | value as u8)),
);
}
let mut bytes: Vec<u8> = Vec::new();
for Ticks(flux) in flux_timings {
if flux == 0 {
continue;
} else if flux > nfa_threshold.0 {
bytes.push(255);
bytes.push(FluxOp::Space.into());
write_28bit(&mut bytes, flux);
bytes.push(255);
bytes.push(FluxOp::Astable.into());
write_28bit(&mut bytes, nfa_period.into());
} else if flux < 250 {
bytes.push(flux as u8);
} else if flux < 1525 {
let low = ((flux - 250) % 255) as u8;
let high = ((flux - 250) / 255) as u8;
bytes.push(250 + high);
bytes.push(1 + low);
} else {
bytes.push(255);
bytes.push(FluxOp::Space.into());
write_28bit(&mut bytes, flux - 249);
bytes.push(249);
}
}
bytes.push(0);
Self(bytes)
}
#[inline]
pub fn from_encoded_bytes(bytes: Vec<u8>) -> Self {
Self(bytes)
}
#[inline]
pub fn as_bytes(&self) -> &[u8] {
self.0.as_slice()
}
pub fn decode(&self) -> impl Iterator<Item = Result<FluxDecodeValue<Ticks>, FluxDecodeError>> {
let mut iter = self.0.iter().copied().enumerate();
let mut flux_ticks: u32 = 0;
let mut index_ticks: u32 = 0;
let mut last_index_value: u32 = 0;
iter::from_fn(
move || -> Option<Result<FluxDecodeValue<Ticks>, FluxDecodeError>> {
while let Some((i, byte)) = iter.next() {
macro_rules! next {
() => {
match iter.next() {
None => {
return Some(Err(FluxDecodeError::UnexpectedEndOfStream {
index: i,
}))
}
Some((i, 0)) => {
return Some(Err(FluxDecodeError::ZeroByte { index: i }))
}
Some((_, byte)) => byte,
}
};
}
if byte == 0 {
if iter.len() == 0 {
return None;
} else {
return Some(Err(FluxDecodeError::NotZeroTerminated));
}
} else if byte == 255 {
let byte = next!();
let bytes = [next!(), next!(), next!(), next!()];
let Ok(flux_op) = FluxOp::try_from(byte) else {
return Some(Err(FluxDecodeError::InvalidFluxOp {
index: i,
flux_op: byte,
}));
};
let bytes = bytes.map(|byte| u32::from(byte) & 254);
let value =
(bytes[0] >> 1) + (bytes[1] << 6) + (bytes[2] << 13) + (bytes[3] << 20);
match flux_op {
FluxOp::Index => {
index_ticks = index_ticks
.checked_add(value)
.expect("index value overflowed a u32")
.checked_sub(last_index_value)
.expect("last_index_value was too big!");
last_index_value = value;
return Some(Ok(FluxDecodeValue::Index(Ticks(mem::take(
&mut index_ticks,
)))));
}
FluxOp::Space => {
flux_ticks = flux_ticks
.checked_add(value)
.expect("flux value overflowed a u32");
index_ticks = index_ticks
.checked_add(value)
.expect("index value overflowed a u32");
}
FluxOp::Astable => {
return Some(Ok(FluxDecodeValue::Flux(Ticks(mem::take(
&mut flux_ticks,
)))));
}
}
} else {
let byte = u32::from(byte);
let value = if let Some(high) = byte.checked_sub(250) {
let byte = next!();
let low = u32::from(byte) - 1;
250 + high * 255 + low
} else {
byte
};
flux_ticks = flux_ticks
.checked_add(value)
.expect("flux value overflowed a u32");
index_ticks = index_ticks
.checked_add(value)
.expect("index value overflowed a u32");
return Some(Ok(FluxDecodeValue::Flux(Ticks(mem::take(&mut flux_ticks)))));
}
}
None
},
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FluxDecodeValue<T> {
Flux(T),
Index(T),
}
impl<T> FluxDecodeValue<T> {
#[inline]
pub fn map<U>(self, f: impl FnOnce(T) -> U) -> FluxDecodeValue<U> {
match self {
Self::Flux(value) => FluxDecodeValue::Flux(f(value)),
Self::Index(value) => FluxDecodeValue::Index(f(value)),
}
}
}
impl<T> FromIterator<FluxDecodeValue<T>> for (Vec<T>, Vec<T>) {
fn from_iter<I: IntoIterator<Item = FluxDecodeValue<T>>>(iter: I) -> Self {
let mut flux_timings: Vec<T> = Vec::new();
let mut index_timings: Vec<T> = Vec::new();
for val in iter {
match val {
FluxDecodeValue::Flux(value) => flux_timings.push(value),
FluxDecodeValue::Index(value) => index_timings.push(value),
}
}
(flux_timings, index_timings)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum FluxDecodeError {
NotZeroTerminated,
ZeroByte {
index: usize,
},
UnexpectedEndOfStream {
index: usize,
},
InvalidFluxOp {
index: usize,
flux_op: u8,
},
}
impl fmt::Display for FluxDecodeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
FluxDecodeError::NotZeroTerminated => {
write!(f, "the last byte of the stream was not zero")
}
FluxDecodeError::ZeroByte { index } => {
write!(f, "a zero byte was encountered at index {index}")
}
FluxDecodeError::UnexpectedEndOfStream { index } => {
write!(
f,
"the end of the stream was reached in the middle of a multi-byte sequence \
starting at index {index}"
)
}
FluxDecodeError::InvalidFluxOp { index, flux_op } => {
write!(
f,
"an invalid flux-op type {flux_op} was encountered at index {index}"
)
}
}
}
}
impl Error for FluxDecodeError {}
#[derive(TryFromPrimitive, IntoPrimitive)]
#[repr(u8)]
enum FluxOp {
Index = 1,
Space = 2,
Astable = 3,
}
#[cfg(test)]
mod tests {
use super::*;
use std::{fs::File, io::Read, path::Path, time::Duration};
#[test]
fn decode_flux() {
let bytes = read_u8s("test_files/decode_bytes.bin");
let expected_flux = read_u32s("test_files/decode_flux.bin");
let expected_index = read_u32s("test_files/decode_index.bin");
let (decoded_flux, decoded_index) = EncodedFlux::from_encoded_bytes(bytes)
.decode()
.collect::<Result<_, _>>()
.unwrap();
for (i, (Ticks(decoded), expected)) in
decoded_flux.iter().zip(expected_flux.iter()).enumerate()
{
if decoded != expected {
panic!(
"decoded flux mismatch at index {i}\n\
decoded: {:?}\n\
expected: {:?}",
decoded_flux[i..].chunks(8).next().unwrap(),
expected_flux[i..].chunks(8).next().unwrap(),
);
}
}
for (i, (Ticks(decoded), expected)) in
decoded_index.iter().zip(expected_index.iter()).enumerate()
{
if decoded != expected {
panic!(
"decoded index mismatch at index {i}\n\
decoded: {:?}\n\
expected: {:?}",
decoded_index[i..].chunks(8).next().unwrap(),
expected_index[i..].chunks(8).next().unwrap(),
);
}
}
assert_eq!(
decoded_flux.len(),
expected_flux.len(),
"decoded flux length mismatch"
);
assert_eq!(
decoded_index.len(),
expected_index.len(),
"decoded index length mismatch"
);
}
#[test]
fn encode_flux() {
let flux = read_u32s("test_files/encode_flux.bin");
let expected_bytes = read_u8s("test_files/encode_bytes.bin");
const SAMPLE_FREQ: u32 = 72_000_000;
let encoded = EncodedFlux::new(
flux.into_iter().map(Ticks),
Ticks::from_duration(Duration::from_nanos(150_000), SAMPLE_FREQ),
Ticks::from_duration(Duration::from_nanos(1_250), SAMPLE_FREQ),
);
let encoded_bytes = encoded.as_bytes();
for (i, (encoded, expected)) in encoded_bytes.iter().zip(expected_bytes.iter()).enumerate()
{
if encoded != expected {
panic!(
"encoded bytes mismatch at index {i}\n\
encoded: {:?}\n\
expected: {:?}",
encoded_bytes[i..].chunks(8).next().unwrap(),
expected_bytes[i..].chunks(8).next().unwrap(),
);
}
}
assert_eq!(
encoded.as_bytes().len(),
expected_bytes.len(),
"encoded bytes length mismatch"
);
}
fn read_u8s(path: impl AsRef<Path>) -> Vec<u8> {
let mut buf: Vec<u8> = Vec::new();
File::open(path).unwrap().read_to_end(&mut buf).unwrap();
buf
}
fn read_u32s(path: impl AsRef<Path>) -> Vec<u32> {
read_u8s(path)
.chunks_exact(size_of::<u32>())
.map(|bytes| u32::from_le_bytes(bytes.try_into().unwrap()))
.collect()
}
}