Вы можете принудительно настроить выравнивание типа по определенному размеру, используя #[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>(),
)
}
Нет никаких гарантий, что Vec<u8>
останется выровненным, если вы перераспределите данные. Это означает, что вы не можете перераспределить, поэтому вам нужно знать, насколько велика сумма для первоначального распределения.
Функция по этой же причине unsafe
. Когда тип отброшен, память должна вернуться к своему первоначальному распределению , но эта функция не может это контролировать.
Благодаря BurntSushi5 для исправлений и дополнений .
См. Также:
Из-за ограничений и небезопасности, приведенных выше, другой потенциальной идеей может быть выделение достаточно большого буфера (возможно, с некоторым пространством для маневра), а затем использование align_to
, чтобы получить правильно выровненный фрагмент. Вы можете использовать тот же тип AlignToSixtyFour
, что и выше, а затем преобразовать &[AlignToSixtyFour]
в &[u8]
с аналогичными логинами c.
Этот метод может использоваться для выдачи (необязательно изменяемых) слайсов которые выровнены. Поскольку они представляют собой кусочки, вам не нужно беспокоиться о том, что пользователь перераспределяет или удаляет их. Это позволит вам обернуть его в более приятный тип.
Несмотря на это, я думаю, что полагаться на выравнивание здесь неуместно для вашей реальной цели чтения структуры из файла. Просто прочитайте байты (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(())
}
См. Также: