What about using byteorder::ByteOrder::write_f32()
to write the bytes into an array directly?
Maybe something like this:
pub struct LLVector3 {
pub x: f32,
pub y: f32,
pub z: f32,
}
impl LLVector3 {
pub fn to_le_bytes_byteorder(&self) -> [u8; 12] {
self.write_with_order::<LittleEndian>()
}
fn write_with_order<B>(&self) -> [u8; 12]
where
B: ByteOrder,
{
let mut buffer = [0; 12];
B::write_f32(&mut buffer[0..], self.x);
B::write_f32(&mut buffer[4..], self.y);
B::write_f32(&mut buffer[8..], self.z);
buffer
}
}
See the playground for a more elaborate version that also double-checks the implementations behave identically for some hand-picked values.
You can easily create your own BufferWriter
type which wraps a buffer to provide a more ergonomic syntax (e.g. method chaining or whatever).
Interestingly, here is the assembly generated by your version using f32::to_le_bytes()
:
playground::LLVector3::to_le_bytes: # @playground::LLVector3::to_le_bytes
# %bb.0:
pushq %rbp
pushq %rbx
movl (%rdi), %r8d
movl 4(%rdi), %edx
movl %edx, %esi
movzbl %dh, %ebp
movzbl %dl, %eax
movl %edx, %ecx
shrl $16, %ecx
shrl $24, %esi
movl 8(%rdi), %ebx
movl %ebx, %edi
shrl $24, %edi
shlq $24, %rdi
movzbl %bl, %edx
orq %rdi, %rdx
movzbl %bh, %edi
shrl $16, %ebx
movzbl %bl, %ebx
shlq $16, %rbx
shlq $8, %rdi
orq %rbx, %rdx
orq %rdi, %rdx
shlq $56, %rsi
orq %r8, %rsi
movzbl %cl, %ecx
shlq $48, %rcx
shlq $40, %rbp
shlq $32, %rax
orq %rsi, %rax
orq %rcx, %rax
orq %rbp, %rax
popq %rbx
popq %rbp
retq
# -- End function
Compared to the byteorder
version which just treats the floats as bytes.
playground::LLVector3::to_le_bytes_byteorder: # @playground::LLVector3::to_le_bytes_byteorder
# %bb.0:
movl 8(%rdi), %edx
movq (%rdi), %rax
retq
# -- End function
I'm guessing LLVM couldn't see what we were trying to achieve among all the low-level byte operations.