我需要分配一个缓冲区来读取 a File,但是这个缓冲区必须与缓存行的大小(64 字节)对齐。我正在寻找一个有点像这样的功能Vec:
pub fn with_capacity_and_aligned(capacity: usize, alignment: u8) -> Vec<T>
Run Code Online (Sandbox Code Playgroud)
这将为我提供所需的 64 字节对齐。这显然不存在,但可能有一些我不知道的等价物(即“黑客”)。
所以,当我使用这个函数(它会给我所需的对齐)时,我可以安全地编写这段代码:
#[repr(C)]
struct Header {
magic: u32,
some_data1: u32,
some_data2: u64,
}
let cache_line_size = 64; // bytes
let buffer: Vec<u8> = Vec::<u8>::with_capacity_and_alignment(some_size, cache_line_size);
match file.read_to_end(&mut buffer) {
Ok(_) => {
let header: Header = {
// and since the buffer is aligned to 64 bytes, I wont get any SEGFAULT
unsafe { transmute(buffer[0..(size_of::<Header>())]) }
};
}
}
Run Code Online (Sandbox Code Playgroud)
并且不会因为对齐问题(例如启动指令)而出现任何恐慌。
您可以使用 强制将类型对齐到特定大小#[repr(align(...))]。我们还用于repr(C)确保此类型具有与字节数组相同的内存布局。
然后,您可以创建对齐类型的向量并将其转换为适当类型的向量:
use std::mem;
#[repr(C, align(64))]
struct AlignToSixtyFour([u8; 64]);
unsafe fn aligned_vec(n_bytes: usize) -> Vec<u8> {
// Lazy math to ensure we always have enough.
let n_units = (n_bytes / mem::size_of::<AlignToSixtyFour>()) + 1;
let mut aligned: Vec<AlignToSixtyFour> = Vec::with_capacity(n_units);
let ptr = aligned.as_mut_ptr();
let len_units = aligned.len();
let cap_units = aligned.capacity();
mem::forget(aligned);
Vec::from_raw_parts(
ptr as *mut u8,
len_units * mem::size_of::<AlignToSixtyFour>(),
cap_units * mem::size_of::<AlignToSixtyFour>(),
)
}
Run Code Online (Sandbox Code Playgroud)
Vec<u8>如果您重新分配数据,则无法保证将保持对齐。这意味着您无法重新分配,因此您需要预先知道分配多少。
该功能是unsafe出于同样的原因。当类型被删除时,内存必须回到它原来的分配状态,但是这个函数不能控制它。
感谢 BurntSushi5 的更正和补充。
也可以看看:
由于上述限制和不安全性,另一个潜在的想法是分配一个足够大的缓冲区(可能有一些摆动空间),然后使用它align_to来获得一个正确对齐的块。您可以使用与AlignToSixtyFour上述相同的类型,然后将其&[AlignToSixtyFour]转换为&[u8]具有类似逻辑的 a。
此技术可用于给出对齐的(可选可变的)切片。由于它们是切片,因此您不必担心用户重新分配或删除它们。这将允许您将其包装成更好的类型。
话虽如此,我认为这里依赖对齐方式不适合您从文件中读取结构的实际目标。只需读取字节 ( u32, u32, u64) 并构建结构:
use byteorder::{LittleEndian, ReadBytesExt}; // 1.3.4
use std::{fs::File, io};
#[derive(Debug)]
struct Header {
magic: u32,
some_data1: u32,
some_data2: u64,
}
impl Header {
fn from_reader(mut reader: impl io::Read) -> Result<Self, Box<dyn std::error::Error>> {
let magic = reader.read_u32::<LittleEndian>()?;
let some_data1 = reader.read_u32::<LittleEndian>()?;
let some_data2 = reader.read_u64::<LittleEndian>()?;
Ok(Self {
magic,
some_data1,
some_data2,
})
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut f = File::open("/etc/hosts")?;
let header = Header::from_reader(&mut f)?;
println!("{:?}", header);
Ok(())
}
Run Code Online (Sandbox Code Playgroud)
也可以看看:
| 归档时间: |
|
| 查看次数: |
1912 次 |
| 最近记录: |