I can do this with a Cursor but I want to do it without the std lib. Also, I need to return the ammount of bytes written, because the output buffer is possibly pre-allocated into the ROM so it has a much bigger size then needed (worst case size).
What would be the most elegant way and less error prone?
To avoid a panic, you can do output.get_mut(i..j).ok_or_else(|| Error::new(..))? instead of output[i..j].
Do you need to return the amount of bytes written even if it encounters an Err(_)? The way your function signature is currently written, the amount of bytes are only reported for the Ok(_) case.
Depending on what you are really trying to accomplish here, you may also consider implementing a serializer with serde.
Yes, it does work without std or alloc … well, no, not without core – who uses Rust without core?
If you don’t mind the exact binary representation, you can also just use an existing one. E.g. postcard. (Note that they did e.g. choose variable-length integer encoding as a default.)
If you want to serialize a specific format of data, like to match a communication protocol or storage format, then I'd suggest the binrw crate. It's a pleasure to work with for these kind of jobs.
And it works great in no_std environments. Example
#![no_std]
#![no_main]
#[binrw]
#[brw(little)]
struct A {
a: u8,
b: u16,
c: u32,
}
#[no_mangle]
pub extern "C" fn main(_argc: isize, _argv: *const *const u8) -> isize {
let value1 = A {
a: 0x11,
b: 0x2233,
c: 0x44556677,
};
let mut buf = [0u8; 16];
let mut cursor = binrw::io::Cursor::new(&mut buf[..]);
value1.write(&mut cursor).unwrap();
unsafe {
libc::printf(
"wrote %u bytes:\0".as_ptr() as *const _,
cursor.position() as libc::c_uint,
);
}
let n = cursor.position() as usize;
let buf = cursor.into_inner();
for &b in &buf[0..n] {
unsafe {
libc::printf(" %02X\0".as_ptr() as *const _, b as libc::c_uint);
}
}
unsafe {
libc::printf("\n\0".as_ptr() as *const _);
}
0
}