#byte #immutability #endian #pread #pwrite

无 std scroll

一套强大的、可扩展的、通用的、端序感知的字节缓冲区读/写特质

15 个版本 (重大更改)

0.12.0 2024 年 1 月 1 日
0.11.0 2022 年 1 月 2 日
0.10.2 2020 年 10 月 9 日
0.10.1 2019 年 11 月 3 日
0.1.0 2016 年 11 月 23 日

#18 in 数据结构

Download history 140256/week @ 2024-04-30 149236/week @ 2024-05-07 162868/week @ 2024-05-14 148329/week @ 2024-05-21 168732/week @ 2024-05-28 158676/week @ 2024-06-04 158552/week @ 2024-06-11 148949/week @ 2024-06-18 151418/week @ 2024-06-25 138918/week @ 2024-07-02 153673/week @ 2024-07-09 163926/week @ 2024-07-16 175152/week @ 2024-07-23 165839/week @ 2024-07-30 188852/week @ 2024-08-06 163010/week @ 2024-08-13

每月 722,506 次下载
1,111 包中使用 (90 直接)

MIT 许可证

96KB
1.5K SLoC

Actions crates.io version

Scroll - 施放一些魔法

         _______________
    ()==(              (@==()
         '______________'|
           |             |
           |   ἀρετή     |
         __)_____________|
    ()==(               (@==()
         '--------------'

文档

https://docs.rs/scroll

用法

添加到您的 Cargo.toml

[dependencies]
scroll = "0.11"

概述

Scroll 为读/写泛型容器(目前默认实现为字节缓冲区)实现了多个特质。最熟悉的可能就是 Pread 特质,它基本接受对 self 的不可变引用、读取的不可变偏移量(以及一个解析上下文,稍后会详细介绍),然后返回反序列化的值。

由于 self 是不可变的,所有读取都可以并行执行,因此可以很容易地并行化。

一个简单的示例展示了它的灵活性

use scroll::{ctx, Pread, LE};

fn main() -> Result<(), scroll::Error> {
    let bytes: [u8; 4] = [0xde, 0xad, 0xbe, 0xef];

    // reads a u32 out of `b` with the endianness of the host machine, at offset 0, turbofish-style
    let number: u32 = bytes.pread::<u32>(0)?;
    // ...or a byte, with type ascription on the binding.
    let byte: u8 = bytes.pread(0)?;

    //If the type is known another way by the compiler, say reading into a struct field, we can omit the turbofish, and type ascription altogether!

    // If we want, we can explicitly add a endianness to read with by calling `pread_with`.
    // The following reads a u32 out of `b` with Big Endian byte order, at offset 0
    let be_number: u32 = bytes.pread_with(0, scroll::BE)?;
    // or a u16 - specify the type either on the variable or with the beloved turbofish
    let be_number2 = bytes.pread_with::<u16>(2, scroll::BE)?;

    // Scroll has core friendly errors (no allocation). This will have the type `scroll::Error::BadOffset` because it tried to read beyond the bound
    let byte: scroll::Result<i64> = bytes.pread(0);

    // Scroll is extensible: as long as the type implements `TryWithCtx`, then you can read your type out of the byte array!

    // We can parse out custom datatypes, or types with lifetimes
    // if they implement the conversion trait `TryFromCtx`; here we parse a C-style \0 delimited &str (safely)
    let hello: &[u8] = b"hello_world\0more words";
    let hello_world: &str = hello.pread(0)?;
    assert_eq!("hello_world", hello_world);

    // ... and this parses the string if its space separated!
    use scroll::ctx::*;
    let spaces: &[u8] = b"hello world some junk";
    let world: &str = spaces.pread_with(6, StrCtx::Delimiter(SPACE))?;
    assert_eq!("world", world);
    Ok(())
}

推导 PreadPwrite

Scroll 实现了一个自定义 derive,可以为您的结构体提供 PreadPwrite 实现。

use scroll::{Pread, Pwrite, BE};

#[derive(Pread, Pwrite)]
struct Data {
    one: u32,
    two: u16,
    three: u8,
}

fn main() -> Result<(), scroll::Error> {
    let bytes: [u8; 7] = [0xde, 0xad, 0xbe, 0xef, 0xfa, 0xce, 0xff];
    // Read a single `Data` at offset zero in big-endian byte order.
    let data: Data = bytes.pread_with(0, BE)?;
    assert_eq!(data.one, 0xdeadbeef);
    assert_eq!(data.two, 0xface);
    assert_eq!(data.three, 0xff);

    // Write it back to a buffer
    let mut out: [u8; 7] = [0; 7];
    out.pwrite_with(data, 0, BE)?;
    assert_eq!(bytes, out);
    Ok(())
}

此功能默认是禁用的,您必须启用 Cargo.toml 中的 derive 功能才能使用它

[dependencies]
scroll = { version = "0.10", features = ["derive"] }

std::io API

Scroll 还可以从实现 std::io::Readstd::io::Write 的任何内容中读取/写入简单类型。内置的数值类型由您负责。如果您想读取自定义类型,您需要实现 FromCtx(《如何解析》)和 SizeWith(《解析后的东西有多大》)特质。您必须使用默认功能进行编译。例如

use std::io::Cursor;
use scroll::IOread;

fn main() -> Result<(), scroll::Error> {
    let bytes_ = [0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xef,0xbe,0x00,0x00,];
    let mut bytes = Cursor::new(bytes_);

    // this will bump the cursor's Seek
    let foo = bytes.ioread::<usize>()?;
    // ..ditto
    let bar = bytes.ioread::<u32>()?;
    Ok(())
}

类似地,我们可以将内容写入任何实现 std::io::Write 的内容,相当自然

use scroll::{IOwrite, LE, BE};
use std::io::{Write, Cursor};

fn main() -> Result<(), scroll::Error> {
    let mut bytes = [0x0u8; 10];
    let mut cursor = Cursor::new(&mut bytes[..]);
    cursor.write_all(b"hello")?;
    cursor.iowrite_with(0xdeadbeef as u32, BE)?;
    assert_eq!(cursor.into_inner(), [0x68, 0x65, 0x6c, 0x6c, 0x6f, 0xde, 0xad, 0xbe, 0xef, 0x0]);
    Ok(())
}

高级用法

滚动设计为高度可配置 - 它允许您实现各种上下文(Ctx)敏感的特性,从而使得实现者可以获得Pread和/或Pwrite特性的自动使用。

例如,假设我们有一个数据类型,我们想指定如何从一个任意的字节缓冲区中解析或序列化这个数据类型。为了做到这一点,我们需要为我们自己的数据类型提供一个TryFromCtx实现。

特别是,如果我们使用[u8]目标,遵循(usize, YourCtx)约定,您将自动获得调用pread_with::<YourDatatype>在字节数组上的权限。

use scroll::{ctx, Pread, BE, Endian};

struct Data<'a> {
  name: &'a str,
  id: u32,
}

// note the lifetime specified here
impl<'a> ctx::TryFromCtx<'a, Endian> for Data<'a> {
  type Error = scroll::Error;
  // and the lifetime annotation on `&'a [u8]` here
  fn try_from_ctx (src: &'a [u8], endian: Endian)
    -> Result<(Self, usize), Self::Error> {
    let offset = &mut 0;
    let name = src.gread::<&str>(offset)?;
    let id = src.gread_with(offset, endian)?;
    Ok((Data { name: name, id: id }, *offset))
  }
}

fn main() -> Result<(), scroll::Error> {
    let bytes = b"UserName\x00\x01\x02\x03\x04";
    let data = bytes.pread_with::<Data>(0, BE)?;
    assert_eq!(data.id, 0x01020304);
    assert_eq!(data.name.to_string(), "UserName".to_string());
    Ok(())
}

请参阅官方文档或简单的示例以获取更多信息。

贡献

欢迎任何想法、观点或贡献!

依赖项

~110KB