pub const BLOCK_SIZE: usize = 2880;
pub const CARD_SIZE: usize = 80;
pub const CARDS_PER_BLOCK: usize = BLOCK_SIZE / CARD_SIZE;
pub const HEADER_PAD_BYTE: u8 = 0x20;
pub const DATA_PAD_BYTE: u8 = 0x00;
pub const fn blocks_needed(num_bytes: usize) -> usize {
if num_bytes == 0 {
return 0;
}
num_bytes.div_ceil(BLOCK_SIZE)
}
pub const fn padded_byte_len(num_bytes: usize) -> usize {
blocks_needed(num_bytes) * BLOCK_SIZE
}
fn copy_and_pad(dest: &mut [u8], src: &[u8], pad_byte: u8) {
let len = src.len();
dest[..len].copy_from_slice(src);
let remaining = &mut dest[len..];
let mut i = 0;
while i < remaining.len() {
remaining[i] = pad_byte;
i += 1;
}
}
pub fn pad_header_blocks(dest: &mut [u8], src: &[u8]) {
assert_eq!(
dest.len(),
padded_byte_len(src.len()),
"dest length must equal the padded block length of src"
);
copy_and_pad(dest, src, HEADER_PAD_BYTE);
}
pub fn pad_data_blocks(dest: &mut [u8], src: &[u8]) {
assert_eq!(
dest.len(),
padded_byte_len(src.len()),
"dest length must equal the padded block length of src"
);
copy_and_pad(dest, src, DATA_PAD_BYTE);
}
pub fn read_block(dest: &mut [u8; BLOCK_SIZE], src: &[u8], block_index: usize) {
let start = block_index * BLOCK_SIZE;
let end = start + BLOCK_SIZE;
assert!(
src.len() >= end,
"src does not contain block {}",
block_index
);
dest.copy_from_slice(&src[start..end]);
}
pub fn write_block(dest: &mut [u8], block: &[u8; BLOCK_SIZE], block_index: usize) {
let start = block_index * BLOCK_SIZE;
let end = start + BLOCK_SIZE;
assert!(
dest.len() >= end,
"dest does not have room for block {}",
block_index
);
dest[start..end].copy_from_slice(block);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn blocks_needed_zero() {
assert_eq!(blocks_needed(0), 0);
}
#[test]
fn blocks_needed_one_byte() {
assert_eq!(blocks_needed(1), 1);
}
#[test]
fn blocks_needed_exactly_one_block() {
assert_eq!(blocks_needed(BLOCK_SIZE), 1);
}
#[test]
fn blocks_needed_one_over() {
assert_eq!(blocks_needed(BLOCK_SIZE + 1), 2);
}
#[test]
fn blocks_needed_exactly_two_blocks() {
assert_eq!(blocks_needed(2 * BLOCK_SIZE), 2);
}
#[test]
fn blocks_needed_partial() {
assert_eq!(blocks_needed(100), 1);
assert_eq!(blocks_needed(2879), 1);
assert_eq!(blocks_needed(2881), 2);
assert_eq!(blocks_needed(5760), 2);
assert_eq!(blocks_needed(5761), 3);
}
#[test]
fn padded_byte_len_zero() {
assert_eq!(padded_byte_len(0), 0);
}
#[test]
fn padded_byte_len_aligned() {
assert_eq!(padded_byte_len(BLOCK_SIZE), BLOCK_SIZE);
assert_eq!(padded_byte_len(2 * BLOCK_SIZE), 2 * BLOCK_SIZE);
}
#[test]
fn padded_byte_len_unaligned() {
assert_eq!(padded_byte_len(1), BLOCK_SIZE);
assert_eq!(padded_byte_len(BLOCK_SIZE + 1), 2 * BLOCK_SIZE);
}
#[test]
fn constant_relationships() {
assert_eq!(BLOCK_SIZE, 2880);
assert_eq!(CARD_SIZE, 80);
assert_eq!(CARDS_PER_BLOCK, 36);
assert_eq!(CARDS_PER_BLOCK * CARD_SIZE, BLOCK_SIZE);
}
#[test]
fn header_pad_full_block() {
let src = [0x41u8; BLOCK_SIZE]; let mut dest = [0u8; BLOCK_SIZE];
pad_header_blocks(&mut dest, &src);
assert_eq!(&dest[..], &src[..]);
}
#[test]
fn header_pad_partial_block() {
let src = [0x41u8; 80]; let mut dest = [0u8; BLOCK_SIZE];
pad_header_blocks(&mut dest, &src);
assert_eq!(&dest[..80], &src[..]);
for &b in &dest[80..] {
assert_eq!(b, HEADER_PAD_BYTE);
}
}
#[test]
fn header_pad_empty() {
let src: &[u8] = &[];
let mut dest: [u8; 0] = [];
pad_header_blocks(&mut dest, src);
}
#[test]
fn header_pad_multi_block() {
let src = [0x42u8; BLOCK_SIZE + 100];
let mut dest = [0u8; 2 * BLOCK_SIZE];
pad_header_blocks(&mut dest, &src);
assert_eq!(&dest[..BLOCK_SIZE + 100], &src[..]);
for &b in &dest[BLOCK_SIZE + 100..] {
assert_eq!(b, HEADER_PAD_BYTE);
}
}
#[test]
fn data_pad_full_block() {
let src = [0xFFu8; BLOCK_SIZE];
let mut dest = [0xAA; BLOCK_SIZE];
pad_data_blocks(&mut dest, &src);
assert_eq!(&dest[..], &src[..]);
}
#[test]
fn data_pad_partial_block() {
let src = [0xFFu8; 100];
let mut dest = [0xAA; BLOCK_SIZE];
pad_data_blocks(&mut dest, &src);
assert_eq!(&dest[..100], &src[..]);
for &b in &dest[100..] {
assert_eq!(b, DATA_PAD_BYTE);
}
}
#[test]
fn data_pad_empty() {
let src: &[u8] = &[];
let mut dest: [u8; 0] = [];
pad_data_blocks(&mut dest, src);
}
#[test]
fn data_pad_multi_block() {
let src = [0xABu8; BLOCK_SIZE + 500];
let mut dest = [0u8; 2 * BLOCK_SIZE];
pad_data_blocks(&mut dest, &src);
assert_eq!(&dest[..BLOCK_SIZE + 500], &src[..]);
for &b in &dest[BLOCK_SIZE + 500..] {
assert_eq!(b, DATA_PAD_BYTE);
}
}
#[test]
fn read_block_first() {
let mut data = [0u8; 2 * BLOCK_SIZE];
for (i, b) in data.iter_mut().enumerate() {
*b = (i % 256) as u8;
}
let mut block = [0u8; BLOCK_SIZE];
read_block(&mut block, &data, 0);
assert_eq!(&block[..], &data[..BLOCK_SIZE]);
}
#[test]
fn read_block_second() {
let mut data = [0u8; 2 * BLOCK_SIZE];
for (i, b) in data.iter_mut().enumerate() {
*b = (i % 256) as u8;
}
let mut block = [0u8; BLOCK_SIZE];
read_block(&mut block, &data, 1);
assert_eq!(&block[..], &data[BLOCK_SIZE..2 * BLOCK_SIZE]);
}
#[test]
#[should_panic(expected = "src does not contain block")]
fn read_block_out_of_bounds() {
let data = [0u8; BLOCK_SIZE];
let mut block = [0u8; BLOCK_SIZE];
read_block(&mut block, &data, 1);
}
#[test]
fn write_block_round_trip() {
let mut original = [0u8; BLOCK_SIZE];
for (i, b) in original.iter_mut().enumerate() {
*b = (i % 256) as u8;
}
let mut buffer = [0u8; 3 * BLOCK_SIZE];
write_block(&mut buffer, &original, 1);
let mut readback = [0u8; BLOCK_SIZE];
read_block(&mut readback, &buffer, 1);
assert_eq!(&readback[..], &original[..]);
}
#[test]
#[should_panic(expected = "dest does not have room for block")]
fn write_block_out_of_bounds() {
let block = [0u8; BLOCK_SIZE];
let mut dest = [0u8; BLOCK_SIZE];
write_block(&mut dest, &block, 1);
}
#[test]
#[should_panic(expected = "dest length must equal the padded block length")]
fn header_pad_wrong_dest_size() {
let src = [0u8; 100];
let mut dest = [0u8; 100]; pad_header_blocks(&mut dest, &src);
}
#[test]
#[should_panic(expected = "dest length must equal the padded block length")]
fn data_pad_wrong_dest_size() {
let src = [0u8; 100];
let mut dest = [0u8; 100]; pad_data_blocks(&mut dest, &src);
}
}