从文件读取原始结构的最佳方法

Car*_*son 3 io unsafe rust

背景(可跳过)

在 Linux 上,该文件/var/run/utmp包含多个utmp结构,每个结构都是原始二进制格式,在一个文件中彼此跟随。utmp本身就比较大(我的机器上是384字节)。我正在尝试读取该文件的原始数据,并在数据有意义后实施检查。我对 Rust 并不陌生,但这是我第一次真正体验到事物不安全的一面。

问题陈述

我有一个包含多个 c 的文件sturct utmp文档)。在 Rust 中,我想将整个文件读入Vec<libc::utmpx>. 更具体地说,如果读者打开该文件,我该如何阅读struct utmp

到目前为止我所拥有的

下面是 的三种不同实现read_raw,它接受一个 reader 并返回一个RawEntry(我的 别名struct utmp)。哪种方法最正确?我正在尝试编写尽可能高性能的代码,并且我担心read_raw0如果涉及 memcpys,可能会比其他代码慢。完成此行为的最佳/最快方法是什么?

use std::io::Read;
use libc::utmpx as RawEntry;

const RawEntrySize = std::mem::size_of::<RawEntry>();
type RawEntryBuffer = [u8; RawEntrySize];

/// Read a raw utmpx struct
// After testing, this method doesn't work
pub fn read_raw0<R: Read>(reader: &mut R) -> RawEntry {
    let mut entry: RawEntry = unsafe { std::mem::zeroed() };
    unsafe {
        let mut entry_buf = std::mem::transmute::<RawEntry, RawEntryBuffer>(entry);
        reader.read_exact(&mut entry_buf[..]);
    }
    return entry;
}

/// Read a raw utmpx struct
pub fn read_raw1<R: Read>(reader: &mut R) -> RawEntry {
    // Worried this could cause alignment issues, or maybe it's okay 
    // because transmute copies
    let mut buffer: RawEntryBuffer = [0; RawEntrySize];
    reader.read_exact(&mut buffer[..]);
    let entry = unsafe {
        std::mem::transmute::<RawEntryBuffer, RawEntry>(buffer)
    };
    return entry;
}

/// Read a raw utmpx struct
pub fn read_raw2<R: Read>(reader: &mut R) -> RawEntry {
    let mut entry: RawEntry = unsafe { std::mem::zeroed() };
    unsafe {
        let entry_ptr = std::mem::transmute::<&mut RawEntry, *mut u8>(&mut entry);
        let entry_slice = std::slice::from_raw_parts_mut(entry_ptr, RawEntrySize);
        reader.read_exact(entry_slice);
    }
    return entry;
}
Run Code Online (Sandbox Code Playgroud)

注意:经过更多测试,它似乎read_raw0不起作用。我相信这是因为 transmute 创建了一个新的缓冲区而不是引用该结构。

Loc*_*cke 5

这就是我想出的办法,我想它应该与读取单个条目一样快。它遵循您上一个条目的精神,但避免了转换(转换&mut T*mut u8可以通过两次转换来完成t as *mut T as *mut u8:)。它还使用MaybeUninit而不是zeroed更明确一点(程序集在优化后可能是相同的)。最后,无论哪种方式,该函数都是不安全的,因此我们不妨将其标记为不安全,并删除这些unsafe块。

use std::io::{self, Read};
use std::slice::from_raw_parts_mut;
use std::mem::{MaybeUninit, size_of};

pub unsafe fn read_raw_struct<R: Read, T: Sized>(src: &mut R) -> io::Result<T> {
    let mut buffer = MaybeUninit::uninit();
    let buffer_slice = from_raw_parts_mut(buffer.as_mut_ptr() as *mut u8, size_of::<T>());
    
    src.read_exact(buffer_slice)?;
    Ok(buffer.assume_init())
}
Run Code Online (Sandbox Code Playgroud)