Lokathor/bytemuck

cast_slice fails &[u8] -> &[f32]

IcanDivideBy0 opened this issue · 5 comments

Hello, I'm crossposting here because i'm not really sure if it's a bytemuck or glam issue (see bitshifter/glam-rs#266)

Consider the following:

fn main() {
    dbg!(bytemuck::cast_slice::<glam::Quat, u8>(&[glam::Quat::IDENTITY]));
    dbg!(bytemuck::cast_slice::<u8, glam::Quat>(&[0u8; 16]));
}

Works fine, but this one will panic

fn main() {
    dbg!(bytemuck::cast_slice::<u8, glam::Quat>(&[0u8; 16]));
}
thread 'main' panicked at 'cast_slice>TargetAlignmentGreaterAndInputNotAligned', /home/samuel/.cargo/registry/src/github.com-1ecc6299db9ec823/bytemuck-1.7.3/src/lib.rs:119:3
stack backtrace:
   0: rust_begin_unwind
             at /rustc/f1edd0429582dd29cccacaf50fd134b05593bd9c/library/std/src/panicking.rs:517:5
   1: core::panicking::panic_fmt
             at /rustc/f1edd0429582dd29cccacaf50fd134b05593bd9c/library/core/src/panicking.rs:100:14
   2: bytemuck::something_went_wrong
             at /home/samuel/.cargo/registry/src/github.com-1ecc6299db9ec823/bytemuck-1.7.3/src/lib.rs:119:3
   3: bytemuck::cast_slice
             at /home/samuel/.cargo/registry/src/github.com-1ecc6299db9ec823/bytemuck-1.7.3/src/lib.rs:319:15
   4: glam_bytemuck_fail::main
             at ./src/main.rs:6:10
   5: core::ops::function::FnOnce::call_once
             at /rustc/f1edd0429582dd29cccacaf50fd134b05593bd9c/library/core/src/ops/function.rs:227:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

This is really strange, why having a first unrelated cast change the behavior of the second one ?

rustc 1.57.0 (f1edd0429 2021-11-29)
simple reproduction: https://github.com/IcanDivideBy0/glam_bytemuck_fail

Ok, it seems unrelated to glam:

fn main() {
    // will not panic with this line uncommented
    // dbg!(bytemuck::cast_slice::<f32, u8>(&[0.0; 4]));
    dbg!(bytemuck::cast_slice::<u8, f32>(&[0u8; 16]));
}

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=1d70b67efac2e1cdb98d8a5bcd8ef86d

Things are getting more and more weird ...
patching the reproduction repository make it works under some conditions:

[patch.crates-io]
# With local bytemuck, it works (it's really just the main branch cloned)
#bytemuck = { path = "../bytemuck" }
# pulling from github make it fail again ...
bytemuck = { git = "https://github.com/Lokathor/bytemuck.git", branch = "main" }

Keeping the investigation...

this line : https://github.com/Lokathor/bytemuck/blob/main/src/lib.rs#L430
assumes the u8 slice start memory address will be aligned to a f32 representation, which might no be the case ... declaring an f32 right before the slice declaration forces the memory addess to be a multiple of 4

let arr: &[f32] = &[0.0]; // try comment or uncomment this line
let bytes: &[u8] = &[0u8; 16];
dbg!(bytes.as_ptr() as usize % align_of::<f32>());

To be more precise... this seems to always produce the wrong result

let f: &f32 = &0.0; // stack memory address aligned to %4
let u: &u8 = &0u8; // %4 + 1
let bytes: &[u8] = &[0u8; 16]; // off from f32 alignment
dbg!(bytes.as_ptr() as usize % align_of::<f32>()); // outputs 1 where bytemuck expect 0

Sooo I understand this is completly what the TargetAlignmentGreaterAndInputNotAligned error is saying ... sorry for the noise