rust 获取`&mut u8`对`&mut u32`部分的引用

s5a0g9ez  于 2023-05-29  发布在  其他
关注(0)|答案(3)|浏览(206)

要将u32转换为它的字节,我知道已经有了:

  • u32::to_le_bytes
  • u32::to_be_bytes
  • u32::to_ne_bytes
  • u32::from_le_bytes
  • u32::from_be_bytes
  • u32::from_ne_bytes

但是,是否有任何可靠的跨平台方法将&mut u32转换为其字节的引用,如&mut u8
像这样:

fn as_be_bytes_mut(num: &mut u32) -> [&mut u8; 4] {
    todo!()
}

fn main() {
    let mut num: u32 = 0x12345678;

    // Prints `0x12345678 - [12, 34, 56, 78]`
    println!("{:#x} - {:x?}", num, num.to_be_bytes());

    let parts = as_be_bytes_mut(&mut num);
    *parts[2] = 0xfe;

    // Should print `0x1234fe78 - [12, 34, fe, 78]`
    println!("{:#x} - {:x?}", num, num.to_be_bytes());
}

其基本原理是理论上应该是可能的,因为无论如何修改其底层字节,u32都没有无效状态。
尝试:

fn as_ne_bytes_mut(num: &mut u32) -> [&mut u8; 4] {
    unsafe {
        let ptr: *mut u8 = (num as *mut u32).cast();
        [
            &mut *ptr.add(0),
            &mut *ptr.add(1),
            &mut *ptr.add(2),
            &mut *ptr.add(3),
        ]
    }
}

fn main() {
    let mut num: u32 = 0x12345678;

    println!("{:#x} - {:x?}", num, num.to_be_bytes());

    let parts = as_ne_bytes_mut(&mut num);
    *parts[1] = 0xfe;

    println!("{:#x} - {:x?}", num, num.to_be_bytes());
}
0x12345678 - [12, 34, 56, 78]
0x1234fe78 - [12, 34, fe, 78]

我认为(tm)这是合理的,因为u32u8的打包数组,u8总是正确对齐的,生存期也应该匹配。我还没有找到实现as_be/le_bytes_mut的方法。我也不是100%肯定这是声音,所以一些反馈会有所帮助。

xxe27gdn

xxe27gdn1#

它 * 应该 * 是健全的得到一个&mut [u8; 4]了。

pub fn as_ne_bytes_mut(num: &mut u32) -> &mut [u8; 4] {
    unsafe {
        let arr: *mut [u8; 4] = (num as *mut u32).cast();
        &mut *arr
    }
}

这比[&mut u8; 4]好得多,因为它是无操作的。但是,您将无法创建lebe版本,除非您交换位并返回一个impl DerefMut<Target = &mut [u8; 4]>保护,该保护在丢弃时将位交换回来。
我可能会使用四个函数,每个函数返回一个字节,而不是返回[&mut u8; 4]lebe函数。虽然如果你使用它们相同,他们应该优化到相同的东西。

pub fn most_sig_byte_0(num: &mut u32) -> &mut u8 {
    if cfg!(target_endian = "big") {
        &mut as_ne_bytes_mut(num)[0]
    } else {
        &mut as_ne_bytes_mut(num)[3]
    }
}

pub fn most_sig_byte_1(num: &mut u32) -> &mut u8 {
    if cfg!(target_endian = "big") {
        &mut as_ne_bytes_mut(num)[1]
    } else {
        &mut as_ne_bytes_mut(num)[2]
    }
}

pub fn most_sig_byte_2(num: &mut u32) -> &mut u8 {
    if cfg!(target_endian = "big") {
        &mut as_ne_bytes_mut(num)[2]
    } else {
        &mut as_ne_bytes_mut(num)[1]
    }
}

pub fn most_sig_byte_3(num: &mut u32) -> &mut u8 {
    if cfg!(target_endian = "big") {
        &mut as_ne_bytes_mut(num)[3]
    } else {
        &mut as_ne_bytes_mut(num)[0]
    }
}

米莉认为这些都很好,至少。您可以使它们成为一个单一的const泛型函数,但给予在0..4范围内将不太符合人体工程学。(playground)

mnemlml8

mnemlml82#

这可以在cfg(target_endian = "...")的帮助下实现:

pub fn as_ne_bytes_mut(num: &mut u32) -> [&mut u8; 4] {
    unsafe {
        let ptr: *mut u8 = (num as *mut u32).cast();
        [
            &mut *ptr.add(0),
            &mut *ptr.add(1),
            &mut *ptr.add(2),
            &mut *ptr.add(3),
        ]
    }
}

pub fn as_be_bytes_mut(num: &mut u32) -> [&mut u8; 4] {
    let mut b = as_ne_bytes_mut(num);

    #[cfg(target_endian = "little")]
    b.reverse();

    b
}

pub fn as_le_bytes_mut(num: &mut u32) -> [&mut u8; 4] {
    let mut b = as_be_bytes_mut(num);
    b.reverse();
    b
}

fn main() {
    let mut num: u32 = 0x12345678;

    // Prints `0x12345678 - [12, 34, 56, 78]`
    println!("{:#x} - {:x?}", num, num.to_be_bytes());

    let parts = as_be_bytes_mut(&mut num);
    *parts[2] = 0xfe;

    // Should print `0x1234fe78 - [12, 34, fe, 78]`
    println!("{:#x} - {:x?}", num, num.to_be_bytes());
}
0x12345678 - [12, 34, 56, 78]
0x1234fe78 - [12, 34, fe, 78]

虽然它看起来有点复杂,但编译器设法optimize it perfectly

example::as_ne_bytes_mut:
        mov     rax, rdi
        lea     rcx, [rsi + 1]
        lea     rdx, [rsi + 2]
        mov     qword ptr [rdi], rsi
        add     rsi, 3
        mov     qword ptr [rdi + 8], rcx
        mov     qword ptr [rdi + 16], rdx
        mov     qword ptr [rdi + 24], rsi
        ret

example::as_be_bytes_mut:
        mov     rax, rdi
        lea     rcx, [rsi + 1]
        lea     rdx, [rsi + 2]
        lea     rdi, [rsi + 3]
        mov     qword ptr [rax], rdi
        mov     qword ptr [rax + 24], rsi
        mov     qword ptr [rax + 8], rdx
        mov     qword ptr [rax + 16], rcx
        ret

example::as_le_bytes_mut:
        mov     rax, rdi
        lea     rcx, [rsi + 1]
        lea     rdx, [rsi + 2]
        mov     qword ptr [rdi], rsi
        add     rsi, 3
        mov     qword ptr [rdi + 24], rsi
        mov     qword ptr [rdi + 8], rcx
        mov     qword ptr [rdi + 16], rdx
        ret
vu8f3i0k

vu8f3i0k3#

不需要指针运算也可以做到这一点。
代码:

pub fn as_le_bytes_mut(num: &mut u32)->[&mut u8; 4]{
    let num_slice = std::slice::from_mut(num);
    let (pref, middle, suff): (_,&mut [u8],_) = unsafe{
        num_slice.align_to_mut()
    };
    // This would be always true when we cast to u8
    assert!(pref.is_empty() && suff.is_empty());
    
    match middle {
        #[cfg(target_endian = "little")]
        [a, b, c, d] => [a, b, c, d],
        #[cfg(target_endian = "big")]
        [a, b, c, d] => [d, c, b, a],
        _ => unreachable!()
    }
}

pub fn as_be_bytes_mut(num: &mut u32)->[&mut u8; 4]{
    let mut r = as_le_bytes_mut(num);
    r.reverse();
    r
}

它也编译成了一个很好的汇编:神栓链接。

相关问题