简体   繁体   中英

What's the equivalent to JavaScript's DataView in Rust?

In JavaScript, a DataView is used when you need to access a block of memory with:

  • differing width types ( uint8 , uint16 , uint32 , float32 etc)
  • non-aligned data
  • specific endianness

DataView s are commonly used for networking code, parsing and creating binary file formats, and for implementing virtual machines. While the first two could make do with only sequential access, virtual machines which use a DataView as their RAM need to be able to access it freely (randomly even!)

Is there a corresponding library in Rust? I've seen bytes and byteorder but they seem more designed for streaming/sequential access rather than free random access.

As a systems language, Rust natively has the ability to interact with raw memory, without requiring a specific wrapper.

Therefore, reading a u32 out of memory simply requires a sprinkling of unsafe :

fn read_u32(bytes: &[u8]) -> u32 {
    assert!(bytes.as_ptr() as usize % std::mem::align_of::<u32>() == 0);
    assert!(bytes.len() >= 4);

    unsafe { *(bytes.as_ptr() as *const u32) }
}

This raw ability can be used to build better abstractions. Notably, abstractions taking care of alignment and endianness.


The byteorder crate provides such abstraction: the LitteEndian and BigEndian types both implement the ByteOrder trait.

The above function can be improved to:

fn read_u32(bytes: &[u8]) -> u32 { LittleEndian::read_u32(bytes) }

which will take care of:

  • Performing the conversion.
  • Handling unaligned access evenly, instead of panicking.
  • With explicit endianness.

It only really accounts for primitive types, though, which is where the bytes crate steps in.

For example, let's decode a UDP Header :

use std::io::Cursor;
use bytes::buf::Buf;

struct UdpHeader {
    src_port: u16,
    dst_port: u16,
    length: u16,
    checksum: u16,
}

fn read_udp_header<T: AsRef<[u8]>>(bytes: &mut Cursor<T>) -> UdpHeader {
    UdpHeader {
        src_port: bytes.read_u16_be(),
        dst_port: bytes.read_u16_be(),
        length: bytes.read_u16_be(),
        checksum: bytes.read_u16_be(),
    }
}

Which uses the Cursor struct from standard library and its implementation of the Buf trait from bytes .

You can create the cursor around a slice of bytes ( &[u8] ) starting at any point of memory; reading from it advances it, positioning it for the next read, and it will handle alignment, endianness and bounds-checking.

Note: unfortunately, there doesn't appear to be a version returning Option<u16> ; I'd probably extend it if this was a concern.


Thus I think you've got the right idea with the listed crates, they cover all the requirements you laid out.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM