use std::num::Wrapping;
use bytes::BufMut;
use thiserror::Error;
pub fn calculate_checksum(data: &[u8]) -> u32 {
let chunks = data.chunks_exact(4);
let last = u32::from_be_bytes(match chunks.remainder() {
&[] => [0; 4],
&[b0] => [b0, 0, 0, 0],
&[b0, b1] => [b0, b1, 0, 0],
&[b0, b1, b2] => [b0, b1, b2, 0],
_ => unreachable!("ChunksExact::remainder is guaranteed to return a slice of length < n"),
});
(chunks
.map(|slice| Wrapping(u32::from_be_bytes(slice.try_into().unwrap())))
.sum::<Wrapping<u32>>()
+ Wrapping(last))
.0
}
#[derive(Debug, Error)]
pub enum ChecksumError {
#[error("Truncated `head` table")]
Truncated,
}
pub fn set_checksum_adjustment(head_table: &mut [u8], value: u32) -> Result<(), ChecksumError> {
if head_table.len() < 12 {
return Err(ChecksumError::Truncated);
}
let mut checksum_field = &mut head_table[8..12];
checksum_field.put_u32(value);
Ok(())
}
pub fn calculate_font_checksum_adjustment(font: &[u8]) -> u32 {
const CHECKSUM_MINUEND: u32 = 0xB1B0AFBA;
let checksum = calculate_checksum(font);
CHECKSUM_MINUEND.wrapping_sub(checksum)
}