代码之家  ›  专栏  ›  技术社区  ›  Finomnis

获取对“&mut u32”的一部分的“@mut u8”引用`

  •  1
  • Finomnis  · 技术社区  · 2 年前

    转换 u32 到它的字节,我知道已经有:

    然而,有没有任何声音和跨平台的方式来转换 &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)这是声音,因为 u32型 是的压缩数组 u8 的和 u8 的始终正确对齐,并且生存期也应该匹配。我还没有找到实现as_be/le_bytes_mut的方法。此外,我也不是100%确定这是正确的,所以一些反馈会有所帮助。

    2 回复  |  直到 2 年前
        1
  •  1
  •   drewtato    2 年前

    应该 要获得一个 &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] 因为这是一个禁忌。但是,您将无法创建 le be 版本,除非您交换位并返回 impl DerefMut<Target = &mut [u8; 4]> 当它掉下来时把比特交换回来的防护装置。

    我可能会使用四个函数,每个函数返回一个字节,而不是 返回的函数 [禁用u8;4] 。尽管如果你使用它们相同,它们应该优化到相同的东西。

    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)

        2
  •  1
  •   Finomnis    2 年前

    这可以在以下方面的帮助下实现 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
    
        3
  •  1
  •   Angelicos Phosphoros    2 年前

    在没有指针算法的情况下可以做到这一点。

    代码:

    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
    }
    

    它也编译成了漂亮的汇编: godbolt link

    推荐文章