Byteorder reading entire array in reverse?

I'm using the byteorder crate in order to serialize data as little endian into a buffer. Specifically, I'm trying to write the Vector x:0, y:0, z:1 (all axes are f32) to a 12-byte buffer. I wrote a test to assert that what was read back, once written was equal to 0, 0, 1. The test passes, however, I opened the generates binary file that the write test outputs and find this:

00 00 80 3F 00 00 00 00 00 00 00 00

I know that 0x3F800000 is equal to 1.0 as a float value, but the first four bytes above should be at the end, not the start. Is byteorder reading the entire buffer in reverse? Because it should only read 4 bytes at a time in reverse, as per what little endian aligns to.

Here's the read/write code, and the tests:

pub trait ReadVector3: ReadBytesExt
{
  #[inline]
  fn read_v3<T: ByteOrder>(&mut self) -> Result<Vector3>
  {
    let mut buf = [0; 12];
    self.read_exact(&mut buf)?;
    Ok(Vector3
    {
      x: T::read_f32(&buf[0..4]),
      y: T::read_f32(&buf[4..8]),
      z: T::read_f32(&buf[8..12]),
    })
  }
}

impl <T: Read + ?Sized> ReadVector3 for T {}

pub trait WriteVector3: WriteBytesExt
{
  #[inline]
  fn write_v3<T: ByteOrder>(&mut self, n: Vector3) -> Result<()>
  {
    let mut buf = [0; 12];
    T::write_f32(&mut buf, n.x);
    T::write_f32(&mut buf, n.y);
    T::write_f32(&mut buf, n.z);
    self.write_all(&buf)
  }
}

impl <T: Write + ?Sized> WriteVector3 for T {}

#[test]
fn v3_write()
{
  use std::fs::File;
  let mut f = File::create("fwd_v3.bin").unwrap();
  let v3 = Vector3::fwd();
  f.write_v3::<LittleEndian>(v3).unwrap();
}

#[test]
fn v3_read()
{
  use std::fs::File;
  let mut f = File::open("fwd_v3.bin").unwrap();
  let v3 = f.read_v3::<LittleEndian>().unwrap();
  assert_eq!(v3, Vector3::fwd());
}

write_v3 writes x y and z all to the first 4 bytes of the buffer.

2 Likes

Aww nuts. Would slicing it work as I had done for read_v3()?

1 Like

Yep. You'll need to tell it the exact location to write to.

1 Like

If you're doing sequential read/writes, you can also use a Cursor - it maintains a position in the underlying buffer where it's reading/writing from. That saves you from either forgetting to slice or using an incorrect slice.

1 Like

I like @vitalyd's approach. If you change from u32 to u64 at some point later on then using something flexible like a cursor will prevent you from stuffing up the indices. For something as trivial as this Vector3 it may not be worth the effort though...