#byte #read-write #io #networking

no-std octs

最后,一个优秀的字节操作库

6个版本 (3个重大更新)

0.4.2 2024年7月20日
0.4.1 2024年7月12日
0.3.0 2024年4月14日
0.2.0 2024年4月13日
0.1.0 2024年4月13日

#990 in 网络编程

Download history 5/week @ 2024-05-20 104/week @ 2024-07-01 100/week @ 2024-07-08 225/week @ 2024-07-15 228/week @ 2024-07-22 107/week @ 2024-07-29 81/week @ 2024-08-05 74/week @ 2024-08-12

每月535次下载
用于3个包 (2个直接使用)

MIT/Apache

48KB
1K SLoC

octs

crates.io docs.rs

最后,一个优秀的字节操作库。

这个包基于bytes定义的类型,通过替换其panic的getput函数为可错误的、非panic的readwrite函数,通过octs::Readocts::Write

功能

  • 基于bytes - 提供了用于字节操作的有用类型,并允许通过引用计数廉价地克隆字节分配。非常适合编写零拷贝网络代码。

  • panic函数是错误 - 在网络中,你不能信任你的输入。那么为什么在错误输入上panic是可能的呢?? 在octs中,所有可能失败的功能都返回一个Result

  • 您的类型是一等公民 - 而不是get_u16put_f32等,只需使用一个readwrite函数来处理所有类型。这意味着您可以实现Decode并能够从任何缓冲区中读取它,同样适用于Encodewrite

  • 专用 varints - 网络原语的基本要素之一在这里实现,无需任何扩展。只需像任何其他值一样 readwrite 一个 VarInt

  • 零不安全 - 我还不够聪明,无法编写不安全代码。

  • #![no_std] - 就像 bytes 一样,但它仍然需要 alloc

示例

写入

use octs::{Read, Write, VarInt};

fn write_packet(
    mut buf: octs::BytesMut,
    //       ^^^^^^^^^^^^^^
    //       | re-exports the core `bytes` types
    packet_id: u16,
    timestamp: u64,
    payload: &[u8],
) -> Result<(), octs::BufTooShort> {
    //          ^^^^^^^^^^^^^^^^^
    //          | the main error type
    buf.write(packet_id)?;
    //  ^^^^^
    //  | one `write` function for all your types

    buf.write(timestamp)?;
    //  +---------------^
    //  | just use ? for errors
    //  | no panics

    buf.write(VarInt(payload.len()))?;
    //       ^^^^^^^
    //       | inbuilt support for varints
    //       | using the Protocol Buffers spec

    buf.write_from(payload)?;
    //  ^^^^^^^^^^
    //  | copy from an existing buffer 

    Ok(())
}

读取

use core::num::NonZeroU8;

use octs::{Bytes, BufError, Decode, Read, BufTooShortOr, VarInt};

#[derive(Debug)]
struct Fragment {
    num_frags: NonZeroU8,
    payload: Bytes,
}

#[derive(Debug)]
enum FragmentError {
    InvalidNumFrags,
    PayloadTooLarge,
}

impl Decode for Fragment {
//   ^^^^^^
//   | implement this trait to be able to `read`
//   | this value from a buffer

    type Error = FragmentError;

    fn decode(mut buf: impl Read) -> Result<Self, BufTooShortOr<Self::Error>> {
        let num_frags = buf
            .read::<NonZeroU8>()
            .map_err(|e| e.map_or(|_| FragmentError::InvalidNumFrags))?;
        // +--------------^^^^^^^
        // | map the `InvalidValue` error of reading
        // | a `NonZeroU8` to your own error value

        let VarInt(payload_len) = buf
            .read::<VarInt<usize>>()
            .map_err(|e| e.map_or(|_| FragmentError::PayloadTooLarge))?;

        let payload = buf.read_next(payload_len)?;
        // +-------------^^^^^^^^^^
        // | read the next `payload_len` bytes directly into `Bytes`
        // | if `buf` is also a `Bytes`, this is zero-copy!

        Ok(Self {
            num_frags,
            payload
        })
    }
}

灵感来源

依赖项

~175KB