Skip to content

Instantly share code, notes, and snippets.

@BurntNail
Created January 10, 2024 22:39
Show Gist options
  • Save BurntNail/b46b5ecc65d5fdfb9097de2d01ad1966 to your computer and use it in GitHub Desktop.
Save BurntNail/b46b5ecc65d5fdfb9097de2d01ad1966 to your computer and use it in GitHub Desktop.
Serialising Rust structs to and from binary directly
use std::{
alloc::Layout,
fs::File,
io::{Read, Write},
};
#[derive(Debug, Copy, Clone)]
struct ExampleData {
a: u8,
b: u64,
c: [u8; 7],
d: u32,
}
fn example_data() -> ExampleData {
ExampleData {
a: 0,
b: u64::MAX,
c: [10, 11, 12, 13, 14, 15, 16],
d: 123456789,
}
}
fn main() -> std::io::Result<()> {
print_layouts();
let eg = example_data();
println!("{eg:?}");
// let bytes = unsafe { any_as_u8_slice(&eg) };
// let bytes = bytes.to_vec();
/* let bytes = vec![
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x15, 0xCD, 0x5B, 0x07, 0x00, 0x0A, 0x0B,
0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x00, 0x00, 0x00, 0x00,
]; */
// let read_in = read_in(bytes)?;
// let read_in = read_in_straight_to_pointer("out.bin").unwrap();
println!("{read_in:?}");
Ok(())
}
unsafe fn any_as_u8_slice<T: Sized>(p: &T) -> &[u8] {
::core::slice::from_raw_parts((p as *const T) as *const u8, ::core::mem::size_of::<T>())
}
fn print_layouts() {
println!(
"Vec<u8>: {:?}",
Layout::array::<u8>(std::mem::size_of::<ExampleData>())
);
println!("ExampleData: {:?}", Layout::new::<ExampleData>());
}
fn read_in_straight_to_pointer (file_name: &str) -> std::io::Result<Option<ExampleData>> {
unsafe {
const STRUCT_SIZE: usize = std::mem::size_of::<ExampleData>();
const LAYOUT: Layout = Layout::new::<ExampleData>();
let mut ptr = std::alloc::alloc(LAYOUT);
let mut bytes_read_total = 0;
let mut reader = File::open(file_name)?;
let mut tmp = [0; 32];
loop {
match reader.read(&mut tmp)? {
0 => break,
n => {
let n = n.min(STRUCT_SIZE - bytes_read_total); //ensure we don't write past the boundary
for i in 0..n {
ptr.write(tmp[i]);
ptr = ptr.add(1);
}
bytes_read_total += n;
if bytes_read_total == STRUCT_SIZE {
break;
}
}
}
}
if bytes_read_total < STRUCT_SIZE {
std::alloc::dealloc(ptr, LAYOUT);
Ok(None)
} else {
ptr = ptr.sub(STRUCT_SIZE);
Ok(Some(*Box::from_raw(ptr as *mut ExampleData)))
}
}
}
fn read_in(mut bytes: Vec<u8>) -> std::io::Result<ExampleData> {
/* let ptr = bytes.as_ptr() as *const ExampleData;
let derefed = unsafe { *ptr };
Ok(derefed) */
//NB: requires Copy, Clone for `ExampleData`
/* let boxed = unsafe { Box::from_raw(bytes.as_mut_ptr() as *mut ExampleData) };
std::mem::forget(bytes);
Ok(*boxed) */
//NB: This is UB because of poor alignment (if we don't pack our struct)
return Ok(unsafe {
let mut ptr = std::alloc::alloc(Layout::new::<ExampleData>());
for b in bytes {
unsafe { ptr.write(b) };
ptr = ptr.add(1);
}
ptr = ptr.sub(std::mem::size_of::<ExampleData>());
*Box::from_raw(ptr as *mut ExampleData)
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment