Created
January 10, 2024 22:39
-
-
Save BurntNail/b46b5ecc65d5fdfb9097de2d01ad1966 to your computer and use it in GitHub Desktop.
Serialising Rust structs to and from binary directly
This file contains 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
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