首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >将Vec<u32>转换为就地的Vec<u8>,且开销最小

将Vec<u32>转换为就地的Vec<u8>,且开销最小
EN

Stack Overflow用户
提问于 2018-04-06 10:13:01
回答 3查看 6.2K关注 0票数 12

我正在尝试将Vec of u32s转换为Vec of u8s,最好是就地的,并且不需要太多的开销。

我的当前解决方案依赖于不安全的代码来重新构造Vec。是否有更好的方法来做到这一点,与我的解决方案相关的风险是什么?

代码语言:javascript
运行
复制
use std::mem;
use std::vec::Vec;

fn main() {
    let mut vec32 = vec![1u32, 2];
    let vec8;
    unsafe {
        let length = vec32.len() * 4; // size of u8 = 4 * size of u32
        let capacity = vec32.capacity() * 4; // ^
        let mutptr = vec32.as_mut_ptr() as *mut u8;
        mem::forget(vec32); // don't run the destructor for vec32

        // construct new vec
        vec8 = Vec::from_raw_parts(mutptr, length, capacity);
    }

    println!("{:?}", vec8)
}

铁锈游乐场链接

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2018-04-06 13:56:10

  1. 每当编写unsafe块时,我强烈鼓励人们在代码块上添加注释,解释为什么您认为代码实际上是安全的。这种类型的信息对于将来阅读代码的人是有用的。
  2. 不要添加关于“神奇数字”4的注释,只需使用mem::size_of::<u32>。我甚至会将size_of用于u8,并为达到最大的清晰度执行除法。
  3. 您可以从unsafe块返回新创建的Vec。
  4. 正如注释中提到的,“转储”这样的数据块使得数据格式平台依赖于;在小终端系统和大端终端系统上,您将得到不同的答案。这可能会导致未来的大规模调试头疼。文件格式要么将平台endianness编码到文件中(使读者的工作更加困难),要么只将特定的endinanness写入文件(使作者的工作更加困难)。
  5. 我可能会将整个unsafe块移动到一个函数中,并为其命名,这只是为了组织目的。
  6. 您不需要导入Vec,它就在序曲中。
代码语言:javascript
运行
复制
use std::mem;

fn main() {
    let mut vec32 = vec![1u32, 2];

    // I copy-pasted this code from StackOverflow without reading the answer 
    // surrounding it that told me to write a comment explaining why this code 
    // is actually safe for my own use case.
    let vec8 = unsafe {
        let ratio = mem::size_of::<u32>() / mem::size_of::<u8>();

        let length = vec32.len() * ratio;
        let capacity = vec32.capacity() * ratio;
        let ptr = vec32.as_mut_ptr() as *mut u8;

        // Don't run the destructor for vec32
        mem::forget(vec32);

        // Construct new Vec
        Vec::from_raw_parts(ptr, length, capacity)
    };

    println!("{:?}", vec8)
}

游乐场

我对这段代码最大的未知担忧在于与Vec关联的内存的对齐。

铁锈的底层分配器分配放行内存具有特定的https://doc.rust-lang.org/std/alloc/struct.Layout.htmlLayout包含指针的大小和对齐等信息。

我假设这段代码需要Layout在对allocdealloc的配对调用之间进行匹配。如果是这样的话,可能会告诉分配器对齐错误。,因为该信息是基于元素类型

如果没有更好的知识,“最好”的做法就是离开Vec<u32>,只需给它一个&[u8]。切片没有与分配器的交互,从而避免了这个问题。

即使没有与分配器交互,您也需要小心对齐!

另请参阅:

票数 17
EN

Stack Overflow用户

发布于 2018-04-06 21:15:36

如果就地转换不是强制性的,那么像这样的东西可以管理字节顺序控件并避免不安全的块:

代码语言:javascript
运行
复制
extern crate byteorder;

use byteorder::{WriteBytesExt, BigEndian};

fn main() {
    let vec32: Vec<u32> = vec![0xaabbccdd, 2];
    let mut vec8: Vec<u8> = vec![];

    for elem in vec32 {
        vec8.write_u32::<BigEndian>(elem).unwrap();
    }

    println!("{:?}", vec8);
}
票数 6
EN

Stack Overflow用户

发布于 2022-07-19 17:54:43

这就是我如何使用位转换副本解决问题的方法。

它在我的x64机器上工作,但我不确定我是否对小/大的endianism做出了不安全的假设。

如果可以在不需要副本的情况下在内存中执行此强制转换,则运行时性能将更快,但我还没有弄清楚如何做到这一点。

代码语言:javascript
运行
复制
/// Cast Vec<u32> to Vec<u8> without modifying underlying byte data
/// ```
/// # use fractals::services::vectors::vec_u32_to_u8;
/// assert_eq!( vec_u32_to_u8(&vec![ 0x12345678 ]), vec![ 0x12u8, 0x34u8, 0x56u8, 0x78u8 ]);
/// ```
#[allow(clippy::identity_op)]
pub fn vec_u32_to_u8(data: &Vec<u32>) -> Vec<u8> {
    // TODO: https://stackoverflow.com/questions/72631065/how-to-convert-a-u32-array-to-a-u8-array-in-place
    // TODO: https://stackoverflow.com/questions/29037033/how-to-slice-a-large-veci32-as-u8
    let capacity = 32/8 * data.len() as usize;  // 32/8 == 4
    let mut output = Vec::<u8>::with_capacity(capacity);
    for &value in data {
        output.push((value >> 24) as u8);  // r
        output.push((value >> 16) as u8);  // g
        output.push((value >>  8) as u8);  // b
        output.push((value >>  0) as u8);  // a
    }
    output
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/49690459

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档