#![allow(clippy::precedence, clippy::while_let_on_iterator)]
#![feature(iter_array_chunks)]
#![cfg_attr(not(feature = "std"), no_std)]
pub mod dialect;
pub mod error;
pub mod pattern;
pub use dialect::{Dialect, VariableDialect};
pub use error::DialectError;
pub use pattern::{Pattern, PatternWord, VariablePattern};
#[cfg(test)]
#[cfg(feature = "std")]
mod tests {
use super::{dialect::*, pattern::*};
use core::slice;
use rand::{self, Rng};
#[test]
fn test_pattern_consistency() {
let pattern: Pattern<3> = Pattern::from([b'a', b'b', b'c']);
for sample in 0..64u8 {
let encoded: PatternWord = pattern.encode(sample);
let decoded = pattern.decode(encoded);
assert_eq!(sample, decoded);
}
}
#[test]
fn test_iterative_roundtrip_single_dialect() {
const ITERATIONS: usize = 100;
let dialect: Dialect<2> = Dialect::new([
VariablePattern::Ternary(Pattern::<3>::from([b'a', b'b', b'c'])),
VariablePattern::Quaternary(Pattern::<4>::from([b'x', b'y', b'z', b'w'])),
]);
let mut rng: rand::prelude::ThreadRng = rand::rng();
for _ in 0..ITERATIONS {
let sample_size: usize = rng.random_range::<usize, _>(100..=1024);
let sample_data: Vec<u8> = (&mut rng).random_iter::<u8>().take(sample_size).collect();
let encoded: Vec<u8> = dialect.encode(&sample_data);
let decoded: Vec<u8> = dialect.decode(&encoded).unwrap();
assert!(encoded.len() != 0);
assert_eq!(sample_data, decoded);
}
}
#[test]
fn test_decode_in_place() {
let dialect: Dialect<2> = Dialect::new([
VariablePattern::Ternary(Pattern::<3>::from([b'a', b'b', b'c'])),
VariablePattern::Quaternary(Pattern::<4>::from([b'x', b'y', b'z', b'w'])),
]);
let mut encoded: [u8; 58] = *b"aaabbbccc xxxyyyzzww aaabbbbcccc xyyyyzw abbbbcc xyyyyzww ";
let in_ptr: *const u8 = &encoded as *const _ as *const u8;
let out_ptr: *mut u8 = &mut encoded as *mut _ as *mut u8;
let in_slice: &[u8] = unsafe { slice::from_raw_parts(in_ptr, encoded.len()) };
let out_slice: &mut [u8] = unsafe { slice::from_raw_parts_mut(out_ptr, encoded.len()) };
let bytes_written: usize = dialect.decode_slice(in_slice, out_slice).unwrap();
assert_eq!(bytes_written, 4);
assert_eq!(&encoded[..bytes_written], &[0xAA, 0xBB, 0xCC, 0xDD]);
}
#[test]
fn test_edge_case_empty() {
let dialect: Dialect<2> = Dialect::new([
VariablePattern::Ternary(Pattern::<3>::from([b'a', b'b', b'c'])),
VariablePattern::Quaternary(Pattern::<4>::from([b'x', b'y', b'z', b'w'])),
]);
let sample_data: Vec<u8> = Vec::new();
let encoded: Vec<u8> = dialect.encode(sample_data);
assert!(encoded.is_empty());
}
#[test]
fn test_edge_case_single_byte() {
let dialect: Dialect<2> = Dialect::new([
VariablePattern::Ternary(Pattern::<3>::from([b'a', b'b', b'c'])),
VariablePattern::Quaternary(Pattern::<4>::from([b'x', b'y', b'z', b'w'])),
]);
let sample_data: Vec<u8> = vec![255];
let encoded: Vec<u8> = dialect.encode(&sample_data);
let decoded: Vec<u8> = dialect.decode(&encoded).unwrap();
assert_eq!(sample_data, decoded);
}
#[test]
fn test_edge_case_double_byte() {
let dialect: Dialect<2> = Dialect::new([
VariablePattern::Ternary(Pattern::<3>::from([b'a', b'b', b'c'])),
VariablePattern::Quaternary(Pattern::<4>::from([b'x', b'y', b'z', b'w'])),
]);
let sample_data: Vec<u8> = vec![255, 63];
let encoded: Vec<u8> = dialect.encode(&sample_data);
let decoded: Vec<u8> = dialect.decode(&encoded).unwrap();
assert_eq!(sample_data, decoded);
}
}