Skip to content

Instantly share code, notes, and snippets.

@BurntNail
Created October 30, 2024 01:23
Show Gist options
  • Save BurntNail/1c5f33afd4b10b4bc10bb89a387dce94 to your computer and use it in GitHub Desktop.
Save BurntNail/1c5f33afd4b10b4bc10bb89a387dce94 to your computer and use it in GitHub Desktop.
Unsigned Integer Compressed Serialisation
const MAX_BYTES: usize = u128::BITS as usize / 8;
const ONE_BYTE_MAX_SIZE: usize = u8::MAX as usize - MAX_BYTES;
struct Integer {
content: [u8; MAX_BYTES],
bytes_used: usize
}
impl From<usize> for Integer {
fn from (n: usize) -> Self {
let mut content = [0_u8; MAX_BYTES];
let mut last_non_zero_byte = 0;
for (i, b) in n.to_le_bytes().into_iter().enumerate() {
content[i] = b;
if b != 0 {
last_non_zero_byte = i;
}
}
Integer {
content,
bytes_used: last_non_zero_byte + 1
}
}
}
impl Integer {
pub fn ser (&self) -> Vec<u8> {
if self.bytes_used <= 1 {
let first_byte = self.content[0];
if first_byte as usize <= ONE_BYTE_MAX_SIZE {
return vec![first_byte];
}
}
let mut output = vec![];
output.push((self.bytes_used + ONE_BYTE_MAX_SIZE) as u8);
output.extend(&self.content[0..self.bytes_used]);
output
}
pub fn deser (bytes: &[u8]) -> Self {
let first_byte = bytes[0];
let mut content = [0_u8; MAX_BYTES];
if first_byte as usize <= ONE_BYTE_MAX_SIZE {
content[0] = first_byte;
let bytes_used = usize::from(content[0] > 0);
return Self {
content, bytes_used
};
}
let len = first_byte as usize - ONE_BYTE_MAX_SIZE;
for (i, b) in (&bytes[1..(len + 1)]).iter().copied().enumerate() {
content[i] = b;
}
Self {
content,
bytes_used: len
}
}
}
impl TryFrom<Integer> for usize {
type Error = ();
fn try_from (n: Integer) -> Result<usize, Self::Error> {
const T_BYTES: usize = usize::BITS as usize / 8;
if n.bytes_used > T_BYTES {
return Err(());
}
let mut output = [0_u8; T_BYTES];
for (i, b) in n.content.into_iter().take(n.bytes_used).enumerate() {
output[i] = b;
}
Ok(usize::from_le_bytes(output))
}
}
#[cfg(test)]
mod tests {
use super::*;
use proptest::prelude::*;
proptest!{
#[test]
fn check_in_memory (n: usize) {
let int = Integer::from(n);
let back = usize::try_from(int).unwrap();
prop_assert_eq!(back, n);
}
#[test]
fn check_serialised (n: usize) {
let int = Integer::from(n);
let sered = int.ser();
let back_int = Integer::deser(&sered);
let back_n = usize::try_from(back_int).unwrap();
prop_assert_eq!(back_n, n);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment