代码之家  ›  专栏  ›  技术社区  ›  Carson

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

  •  1
  • Carson  · 技术社区  · 4 年前

    背景(可跳过)

    在linux上,文件 /var/run/utmp 包含几个 utmp 结构,每一个都是原始二进制格式,在一个文件中紧随其后。 utmp 它本身是一个相对较大的字节(在我的机器上是384字节)。我试图将这个文件读取到它的原始数据中,并在数据有意义后执行检查。我对生锈并不陌生,但这是我第一次真正体验到事物不安全的一面。

    问题陈述

    我有一个包含几个c语言的文件 sturct utmp s( docs ).在rust中,我想将整个文件读入 Vec<libc::utmpx> .更具体地说,如果一个读者打开了这个文件,我怎么能读呢 struct utmp ?

    到目前为止我所拥有的

    下面是的三种不同实现 read_raw ,它接受读取器并返回 RawEntry (我的化名是 结构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;
    }
    

    注意:经过更多测试后,它出现了 读_raw0 没用。我认为这是因为transmute创建了一个新的缓冲区,而不是引用结构。

    1 回复  |  直到 4 年前
        1
  •  1
  •   Locke    4 年前

    这就是我想到的,我想这应该和阅读一个条目的速度一样快。它遵循你最后一次进入的精神,但避免了蜕变(蜕变) &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>(mut src: 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())
    }