16 releases (1 stable)

1.0.0 Oct 21, 2024
0.5.0 Mar 21, 2022
0.4.0 Mar 21, 2022
0.3.1 Dec 30, 2021
0.1.7 Nov 20, 2020

#140 in Parser implementations

Download history 1707/week @ 2024-08-23 1454/week @ 2024-08-30 1992/week @ 2024-09-06 1481/week @ 2024-09-13 1643/week @ 2024-09-20 1419/week @ 2024-09-27 1501/week @ 2024-10-04 1909/week @ 2024-10-11 2600/week @ 2024-10-18 2128/week @ 2024-10-25 1681/week @ 2024-11-01 1832/week @ 2024-11-08 2103/week @ 2024-11-15 2047/week @ 2024-11-22 1590/week @ 2024-11-29 1532/week @ 2024-12-06

7,615 downloads per month
Used in 7 crates (6 directly)

Apache-2.0

52KB
402 lines

fixed-buffer

crates.io version license: Apache 2.0 unsafe forbidden pipeline status

This is a Rust library with fixed-size buffers, useful for network protocol parsers and file parsers.

Features

  • forbid(unsafe_code)
  • Depends only on std
  • Write bytes to the buffer and read them back
  • Lives on the stack
  • Does not allocate
  • Use it to read a stream, search for a delimiter, and save leftover bytes for the next read.
  • No macros
  • Good test coverage (100%)
  • Async support by enabling cargo features async-std-feature, futures-io, smol-feature, or tokio.

Limitations

  • Not a circular buffer. You can call shift() periodically to move unread bytes to the front of the buffer.

Examples

Read and handle requests from a remote client:

use fixed_buffer::{deframe_line, FixedBuf};
use std::io::Error;
use std::net::TcpStream;

fn handle_conn(mut tcp_stream: TcpStream) -> Result<(), Error> {
    let mut buf: FixedBuf<4096> = FixedBuf::new();
    loop {
        // Read a line
        // and leave leftover bytes in `buf`.
        let line_bytes = match buf.read_frame(
            &mut tcp_stream, deframe_line)? {
                Some(line_bytes) => line_bytes,
                None => return Ok(()),
            };
        let request = Request::parse(line_bytes)?;
        handle_request(request)?;
    }
}

For a complete example, see tests/server.rs.

Read and process records:

use fixed_buffer::FixedBuf;
use std::io::{Error, ErrorKind, Read};
use std::net::TcpStream;

fn try_process_record(b: &[u8]) -> Result<usize, Error> {
    if b.len() < 2 {
        return Ok(0);
    }
    if b.starts_with("ab".as_bytes()) {
        println!("found record");
        Ok(2)
    } else {
        Err(Error::new(ErrorKind::InvalidData, "bad record"))
    }
}

fn read_and_process<R: Read>(mut input: R) -> Result<(), Error> {
    let mut buf: FixedBuf<1024> = FixedBuf::new();
    loop {
        // Read a chunk into the buffer.
        if buf.copy_once_from(&mut input)? == 0 {
            return if buf.len() == 0 {
                // EOF at record boundary
                Ok(())
            } else {
                // EOF in the middle of a record
                Err(Error::from(
                    ErrorKind::UnexpectedEof))
            };
        }
        // Process records in the buffer.
        loop {
            let num_to_consume =
                try_process_record(buf.readable())?;
            if num_to_consume == 0 {
                break;
            }
            buf.try_read_exact(num_to_consume).unwrap();
        }
        // Shift data in the buffer to free up
        // space at the end for writing.
        buf.shift();
    }
}
#

The From<&[u8; SIZE]> implementation is useful in tests. Example:

use core::convert::From;
assert_eq!(3, FixedBuf::from(b"abc").len());

Cargo Geiger Safety Report


Metric output format: x/y
    x = unsafe code used by the build
    y = total unsafe code found in the crate

Symbols: 
    🔒  = No `unsafe` usage found, declares #![forbid(unsafe_code)]= No `unsafe` usage found, missing #![forbid(unsafe_code)]
    ☢️  = `unsafe` usage found

Functions  Expressions  Impls  Traits  Methods  Dependency

0/0        0/0          0/0    0/0     0/0      🔒  fixed-buffer 1.0.0
0/0        0/0          0/0    0/0     0/0      ❓  ├── futures-io 0.3.31
0/25       0/1782       0/102  0/1     0/69     ❓  └── tokio 1.12.0
0/17       0/607        0/13   0/1     0/19     ❓      ├── bytes 1.1.0
0/90       0/596        0/2    0/0     0/69     ❓      ├── libc 0.2.161
0/37       0/2025       0/0    0/0     0/16     ❓      ├── memchr 2.4.1
0/90       0/596        0/2    0/0     0/69     ❓      │   └── libc 0.2.161
0/0        0/72         0/0    0/0     0/0      ❓      ├── num_cpus 1.13.1
0/90       0/596        0/2    0/0     0/69     ❓      │   └── libc 0.2.161
0/1        0/85         0/6    0/0     0/3      ❓      ├── once_cell 1.8.0
0/0        0/191        0/0    0/0     0/2      ❓      ├── pin-project-lite 0.2.14
0/0        0/12         0/0    0/0     0/0      🔒      ├── signal-hook-registry 1.4.0
0/90       0/596        0/2    0/0     0/69     ❓      │   └── libc 0.2.161
0/0        0/0          0/0    0/0     0/0      ❓      └── tokio-macros 1.7.0
0/0        0/12         0/0    0/0     0/3      ❓          ├── proc-macro2 1.0.36
0/0        0/0          0/0    0/0     0/0      🔒          │   └── unicode-xid 0.2.2
0/0        0/0          0/0    0/0     0/0      ❓          ├── quote 1.0.16
0/0        0/12         0/0    0/0     0/3      ❓          │   └── proc-macro2 1.0.36
0/0        0/47         0/3    0/0     0/2      ❓          └── syn 1.0.89
0/0        0/12         0/0    0/0     0/3      ❓              ├── proc-macro2 1.0.36
0/0        0/0          0/0    0/0     0/0      ❓              ├── quote 1.0.16
0/0        0/0          0/0    0/0     0/0      🔒              └── unicode-xid 0.2.2

0/170      0/5429       0/126  0/2     0/183  

Alternatives

Changelog

  • v1.0.0 2024-10-20 - From<&[u8; SIZE]>, FixedBuffer::from(b"x")
  • v0.5.0 2022-03-21 - Move ReadWriteChain and ReadWriteTake to new read-write-ext crate.
  • v0.4.0 2022-03-21
    • From<[u8; SIZE]>, FixedBuffer::from([0])
    • write_bytes to take AsRef<[u8]>
    • Rename try_read_exact to read_and_copy_exact.
    • Rename try_read_bytes to try_read_exact.
    • Remove empty, filled, read_byte, read_bytes, try_parse, and write_str.
    • deframe to allow consuming bytes without returning a frame
    • write_bytes to write as many bytes as it can, and return new NoWritableSpace error only when it cannot write any bytes. Remove NotEnoughSpaceError. The method now behaves like std::io::Write::write.
Older changelog entries
  • v0.3.1 - Implement From<NotEnoughSpaceError> and From<MalformedInputError> for String.
  • v0.3.0 - Breaking API changes:
    • Change type parameter to const buffer size. Example: FixedBuf<1024>.
    • Remove new arg.
    • Remove capacity.
    • Remove Copy impl.
    • Change writable return type to &mut [u8].
  • v0.2.3
  • v0.2.2 - Add badges to readme
  • v0.2.1 - Add deframe and mem, needed by AsyncFixedBuf::read_frame.
  • v0.2.0
  • v0.1.7 - Add FixedBuf::escape_ascii.
  • v0.1.6 - Add filled constructor.
  • v0.1.5 - Change read_delimited to return Option<&[u8]>, for clean EOF handling.
  • v0.1.4 - Add clear().
  • v0.1.3
    • Thanks to freax13 for these changes:
      • Support any buffer size. Now you can make FixedBuf<[u8; 42]>.
      • Support any AsRef<[u8]> + AsMut<[u8]> value for internal memory:
        • [u8; N]
        • Box<[u8; N]>
        • &mut [u8]
        • Vec<u8>
    • Renamed new_with_mem to new. Use FixedBuf::default() to construct any FixedBuf<T: Default>, which includes arrays of sizes up to 32.
  • v0.1.2 - Updated documentation.
  • v0.1.1 - First published version

TO DO

Release Process

  1. Edit Cargo.toml and bump version number.
  2. Run ../release.sh

License: Apache-2.0

Dependencies

~0–5.5MB
~19K SLoC