将 Vec<u32> 就地转换为 Vec<u8> 并且开销最小
Converting a Vec<u32> to Vec<u8> in-place and with minimal overhead
我正在尝试将 u32
的 Vec
转换为 u8
的 Vec
,最好就地转换并且没有太多开销。
我当前的解决方案依赖于不安全的代码来重新构造 Vec
。有没有更好的方法来做到这一点,我的解决方案有哪些相关风险?
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)
}
每当写一个 unsafe
块时,我 强烈地 鼓励人们在块上添加评论,解释 为什么你认为该代码实际上是安全的。这类信息对以后阅读代码的人很有用。
不要添加关于 "magic number" 4 的注释,只需使用 mem::size_of::<u32>
。我什至会使用 size_of
作为 u8
并执行除法以获得最大的清晰度。
您可以 return 来自 unsafe
块的 newly-created Vec。
如评论中所述,"dumping"这样的数据块使数据格式平台依赖;你会在小端和大端系统上得到不同的答案。这可能会导致将来出现大量调试问题。文件格式要么将平台字节序编码到文件中(使 reader 的工作更难),要么只将特定的字节序写入文件(使编写者的工作更难)。
我可能会将整个 unsafe
块移动到一个函数中并为其命名,仅用于组织目的。
不需要导入Vec
,在前奏中
use std::mem;
fn main() {
let mut vec32 = vec![1u32, 2];
// I copy-pasted this code from Whosebug 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
相关的内存对齐。
Rust 的底层分配器 allocates and deallocates memory with a specific Layout
。 Layout
包含了指针的size和alignment等信息。
我假设这段代码需要 Layout
来匹配对 alloc
和 dealloc
的成对调用。如果是这样的话,dropping the Vec<u8>
constructed from a Vec<u32>
might tell the allocator the wrong alignment since that information is based on the element type.
如果没有更好的知识,"best" 要做的就是离开 Vec<u32>
as-is 并简单地得到一个 &[u8]
。切片与分配器没有交互,避免了这个问题。
即使不与分配器交互,您也需要注意对齐!
另请参阅:
如果 in-place convert 不是强制性的,像这样的东西管理 bytes order 控制并避免不安全的块:
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);
}
我正在尝试将 u32
的 Vec
转换为 u8
的 Vec
,最好就地转换并且没有太多开销。
我当前的解决方案依赖于不安全的代码来重新构造 Vec
。有没有更好的方法来做到这一点,我的解决方案有哪些相关风险?
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)
}
每当写一个
unsafe
块时,我 强烈地 鼓励人们在块上添加评论,解释 为什么你认为该代码实际上是安全的。这类信息对以后阅读代码的人很有用。不要添加关于 "magic number" 4 的注释,只需使用
mem::size_of::<u32>
。我什至会使用size_of
作为u8
并执行除法以获得最大的清晰度。您可以 return 来自
unsafe
块的 newly-created Vec。如评论中所述,"dumping"这样的数据块使数据格式平台依赖;你会在小端和大端系统上得到不同的答案。这可能会导致将来出现大量调试问题。文件格式要么将平台字节序编码到文件中(使 reader 的工作更难),要么只将特定的字节序写入文件(使编写者的工作更难)。
我可能会将整个
unsafe
块移动到一个函数中并为其命名,仅用于组织目的。不需要导入
Vec
,在前奏中
use std::mem;
fn main() {
let mut vec32 = vec![1u32, 2];
// I copy-pasted this code from Whosebug 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
相关的内存对齐。
Rust 的底层分配器 allocates and deallocates memory with a specific Layout
。 Layout
包含了指针的size和alignment等信息。
我假设这段代码需要 Layout
来匹配对 alloc
和 dealloc
的成对调用。如果是这样的话,dropping the Vec<u8>
constructed from a Vec<u32>
might tell the allocator the wrong alignment since that information is based on the element type.
如果没有更好的知识,"best" 要做的就是离开 Vec<u32>
as-is 并简单地得到一个 &[u8]
。切片与分配器没有交互,避免了这个问题。
即使不与分配器交互,您也需要注意对齐!
另请参阅:
如果 in-place convert 不是强制性的,像这样的东西管理 bytes order 控制并避免不安全的块:
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);
}