Total bike-shedding here, but I'm wondering about the relative efficiency of
let mut buf = [0u8];
input.read(&mut buf)?;
Ok(buf[0] as i32)
vs
let mut buf = [0u8; 4];
input.read(&mut buf[3..4])?;
Ok(i32::from_be_bytes(buf))
Even on aesthetic grounds its hard to choose between these since in isolation I prefer the first set of code, but I have parallel methods for reading 16-, 24- and 32-bit integer data that follow the latter pattern which makes it more appealing in context.