Created
October 30, 2024 01:23
-
-
Save BurntNail/1c5f33afd4b10b4bc10bb89a387dce94 to your computer and use it in GitHub Desktop.
Unsigned Integer Compressed Serialisation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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