Fondo (Omitible)
En Linux, el archivo /var/run/utmp
contiene varias utmp
estructuras, cada una en formato binario sin procesar, que se suceden en un archivo. utmp
en sí mismo es relativamente grande (384 bytes en mi máquina). Estoy tratando de leer este archivo en sus datos sin procesar, y luego implementan controles después del hecho de que los datos tienen sentido. No soy nuevo en el óxido, pero esta es mi primera experiencia real con el lado inseguro de las cosas.
Planteamiento del problema
Tengo un archivo que contiene varios c sturct utmp
s ( docs ). En rust, me gustaría leer el archivo completo en una matriz de archivos Vec<libc::utmpx>
. Más específicamente, dado un lector abierto a este archivo, ¿cómo podría leer uno struct utmp
?
What I have so far
A continuación se muestran tres implementaciones diferentes de read_raw
, que acepta un lector y devuelve un RawEntry
(mi alias para struct utmp
). ¿Qué método es el más correcto? Estoy tratando de escribir un código con el mayor rendimiento posible, y me preocupa que read_raw0
pueda ser más lento que los demás si se trata de memcpys. ¿Cuál es la forma mejor/más rápida de lograr este comportamiento?
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;
}
Nota: Después de más pruebas, parece read_raw0
que no funciona. Creo que esto se debe a que transmutar crea un nuevo búfer en lugar de hacer referencia a la estructura.
Solución del problema
Esto es lo que se me ocurrió, que imagino que debería ser lo más rápido posible para leer una sola entrada. Sigue el espíritu de tu última entrada, pero evita la transmutación (Transmutar &mut T
a *mut u8
se puede hacer con dos lanzamientos: t as *mut T as *mut u8
). También usa MaybeUninit
en lugar de zeroed
ser un poco más explícito (el ensamblaje probablemente sea el mismo una vez optimizado). Por último, la función no será segura de ninguna manera, por lo que también podemos marcarla como tal y eliminar los unsafe
bloqueos.
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())
}
No hay comentarios:
Publicar un comentario