use crate::types::{GeoLen, VAR_LEN_PADDING};
pub const MAX_BC_LEN: usize = 32;
#[inline]
pub fn pad_to_fixed<'a>(seq: &[u8], max_len: usize, buf: &'a mut [u8]) -> &'a [u8] {
let captured_len = seq.len();
debug_assert!(captured_len <= max_len);
debug_assert!(max_len <= buf.len());
buf[..captured_len].copy_from_slice(seq);
let deficit = max_len - captured_len;
if deficit > 0 {
let padding = VAR_LEN_PADDING[deficit];
buf[captured_len..captured_len + padding.len()].copy_from_slice(padding);
&buf[..max_len]
} else {
&buf[..max_len]
}
}
#[inline]
pub fn needs_padding(len: &GeoLen) -> bool {
matches!(len, GeoLen::Range(min, max) if min != max)
}
#[inline]
pub fn normalized_len(len: &GeoLen) -> Option<usize> {
match len {
GeoLen::Fixed(n) => Some(*n as usize),
GeoLen::Range(_, max) => Some(*max as usize),
GeoLen::Unbounded => None,
}
}
pub struct PadBuf {
buf: [u8; MAX_BC_LEN],
}
impl PadBuf {
pub fn new() -> Self {
Self {
buf: [0u8; MAX_BC_LEN],
}
}
#[inline]
pub fn pad(&mut self, seq: &[u8], max_len: usize) -> &[u8] {
pad_to_fixed(seq, max_len, &mut self.buf)
}
}
impl Default for PadBuf {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn pad_no_deficit() {
let mut buf = [0u8; 32];
let result = pad_to_fixed(b"ACGTACGTAC", 10, &mut buf);
assert_eq!(result, b"ACGTACGTAC");
}
#[test]
fn pad_deficit_1() {
let mut buf = [0u8; 32];
let result = pad_to_fixed(b"ACGTACGTA", 10, &mut buf);
assert_eq!(result, b"ACGTACGTAA"); }
#[test]
fn pad_deficit_2() {
let mut buf = [0u8; 32];
let result = pad_to_fixed(b"ACGTACGT", 10, &mut buf);
assert_eq!(result, b"ACGTACGTAC"); }
#[test]
fn pad_deficit_3() {
let mut buf = [0u8; 32];
let result = pad_to_fixed(b"ACGTACG", 10, &mut buf);
assert_eq!(result, b"ACGTACGAAG"); }
#[test]
fn pad_deficit_4() {
let mut buf = [0u8; 32];
let result = pad_to_fixed(b"ACGTAC", 10, &mut buf);
assert_eq!(result, b"ACGTACAAAT"); }
#[test]
fn no_collisions_across_lengths() {
let mut buf = [0u8; 32];
let padded_8 = pad_to_fixed(b"ACGTAC", 10, &mut buf).to_vec();
let padded_9 = pad_to_fixed(b"ACGTACG", 10, &mut buf).to_vec();
let padded_10 = pad_to_fixed(b"ACGTACGT", 10, &mut buf).to_vec();
let padded_full = pad_to_fixed(b"ACGTACGTNN", 10, &mut buf).to_vec();
assert_ne!(padded_8, padded_9);
assert_ne!(padded_8, padded_10);
assert_ne!(padded_8, padded_full);
assert_ne!(padded_9, padded_10);
assert_ne!(padded_9, padded_full);
assert_ne!(padded_10, padded_full);
}
#[test]
fn padbuf_reuse() {
let mut pb = PadBuf::new();
let r1 = pb.pad(b"ACGT", 6).to_vec(); assert_eq!(r1, b"ACGTAC");
let r2 = pb.pad(b"TTTTT", 6).to_vec(); assert_eq!(r2, b"TTTTTA");
}
#[test]
fn needs_padding_check() {
assert!(!needs_padding(&GeoLen::Fixed(16)));
assert!(!needs_padding(&GeoLen::Unbounded));
assert!(needs_padding(&GeoLen::Range(9, 10)));
assert!(!needs_padding(&GeoLen::Range(10, 10))); }
#[test]
fn normalized_len_check() {
assert_eq!(normalized_len(&GeoLen::Fixed(16)), Some(16));
assert_eq!(normalized_len(&GeoLen::Range(9, 10)), Some(10));
assert_eq!(normalized_len(&GeoLen::Unbounded), None);
}
}