Crate octs

Source
Expand description

§octs

crates.io docs.rs

Finally, a good byte manipulation library.

This crate builds on top of the types defined by bytes by replacing its panicking get and put functions with fallible, non-panicking read and write functions via octs::Read and octs::Write.

§Features

  • Based on bytes - which provides useful types for byte manipulation, and allows cheaply cloning byte allocations via reference counting. Great for writing zero-copy networking code.

  • Panicking functions were a mistake - in networking, you can’t trust your inputs. So why should it ever be possible to panic on malformed input?? All functions in octs which can fail return a Result.

  • Your types are first-class citizens - instead of get_u16, put_f32, etc., just use one read and write function for all types. This means you can implement Decode and be able to read it from any buffer, and likewise for Encode and write.

  • Dedicated varints - one of the staples of networking primitives is implemented here, without needing any extensions. Just read or write a VarInt as you would any other value.

  • Zero unsafe - I’m not smart enough to write unsafe code.

  • #![no_std] - just like bytes, but it still requires alloc.

§Examples

§Writing

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(())
}

§Reading

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
        })
    }
}

§Inspirations

  • bytes - core byte manipulation primitives, such as the possibly-non-contiguous bytes::Buf trait, and the cheaply-cloneable bytes::Bytes type.
  • octets - general API style, and having varints be a core part of the API
  • safer-bytes - making a good version of the bytes API
  • integer-encoding - implementations of varint encode/decode

Re-exports§

pub use bytes;

Modules§

chunks
Allows splitting a byte buffer into non-overlapping chunks of bytes.
prim
Implementations of Decode and Encode for primitive types.
test
Utilities for testing trait implementations.

Structs§

BufTooShort
Performed an operation on a Read or Write which required more bytes available than were actually available.
Bytes
A cheaply cloneable and sliceable chunk of contiguous memory.
BytesMut
A unique reference to a contiguous slice of memory.
VarInt
Integer which is encoded in a variable amount of bytes.
VarIntTooLarge
Attempted to read a VarInt from a buffer, but the resulting integer would have been too large to fit into this VarInt.

Enums§

BufTooShortOr
Error which may represent either a BufTooShort or some other user-specified error type.

Traits§

Buf
Read bytes from a buffer.
BufError
Marker trait for an error type which can be used as the E parameter in BufTooShortOr.
BufMut
A trait for values that provide sequential write access to bytes.
Decode
Allows reading a value of this type from a Read.
Encode
Allows writing a value of this type into a Write.
EncodeLen
Gets how many bytes it takes to encode a value of this type.
FixedEncodeLen
Defines exactly how many bytes it will take to encode a value of this type.
FixedEncodeLenHint
Provides hints on how many bytes it may take to encode a value of this type.
Read
Allows reading bytes from a buffer.
Write
Allows writing bytes into a buffer.