Crate fixed_buffer
source · [−]Expand description
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 (99%)
- Async support by enabling cargo features
async-std-feature,futures-io,smol-feature, ortokio.
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());Alternatives
bytesbuf_redux, circular buffer supportstd::io::BufReaderstd::io::BufWriterstatic-buffer, updated in 2016block-buffer, for processing fixed-length blocks of dataarrayvec, vector with fixed capacity.
Changelog
- v0.5.0 - Move
ReadWriteChainandReadWriteTaketo newread-write-extcrate. - v0.4.0
From<&[u8]>write_bytesto takeAsRef<[u8]>- Rename
try_read_exacttoread_and_copy_exact. - Rename
try_read_bytestotry_read_exact. - Remove
empty,filled,read_byte,read_bytes,try_parse, andwrite_str. deframeto allow consuming bytes without returning a framewrite_bytesto write as many bytes as it can, and return newNoWritableSpaceerror only when it cannot write any bytes. RemoveNotEnoughSpaceError. The method now behaves likestd::io::Write::write.
- v0.3.1 - Implement
From<NotEnoughSpaceError>andFrom<MalformedInputError>forString.
Older changelog entries
- v0.3.0 - Breaking API changes:
- Change type parameter to const buffer size. Example:
FixedBuf<1024>. - Remove
newarg. - Remove
capacity. - Remove
Copyimpl. - Change
writablereturn type to&mut [u8].
- Change type parameter to const buffer size. Example:
- v0.2.3
- Add
read_byte,try_read_byte,try_read_bytes,try_read_exact,try_parse. - Implement
UnwindSafe
- Add
- v0.2.2 - Add badges to readme
- v0.2.1 - Add
deframeandmem, needed byAsyncFixedBuf::read_frame. - v0.2.0
- Move tokio support to
fixed_buffer_tokio. - Add
copy_once_from,read_block,ReadWriteChain, andReadWriteTake.
- Move tokio support to
- v0.1.7 - Add
FixedBuf::escape_ascii. - v0.1.6 - Add
filledconstructor. - v0.1.5 - Change
read_delimitedto returnOption<&[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>
- Support any buffer size. Now you can make
- Renamed
new_with_memtonew. UseFixedBuf::default()to construct anyFixedBuf<T: Default>, which includes arrays of sizes up to 32.
- Thanks to freax13 for these changes:
- v0.1.2 - Updated documentation.
- v0.1.1 - First published version
TO DO
- Change links in docs to standard style. Don’t link to
docs.rs. - Idea:
buf.slice(buf.read_frame(&mut reader, deframe_crlf)) - Add an
frame_copy_iterfunction. Because of borrowing rules, this function must return non-borrowed (allocated and copied) data. - Set up CI on:
- DONE - Linux x86 64-bit
- macOS
- Windows
- https://crate-ci.github.io/pr/testing.html#travisci
- Linux ARM 64-bit (Raspberry Pi 3 and newer)
- Linux ARM 32-bit (Raspberry Pi 2)
- RISCV & ESP32 firmware?
- DONE - Try to make this crate comply with the Rust API Guidelines.
- DONE - Find out how to include Readme.md info in the crate’s docs.
- DONE - Make the repo public
- DONE - Set up continuous integration tests and banner.
- DONE - Add some documentation tests
- DONE - Set up public repository on Gitlab.com
- DONE - Publish to crates.io
- DONE - Read through https://crate-ci.github.io/index.html
- DONE - Get a code review from an experienced rustacean
- DONE - Add and update a changelog
- Update it manually
- https://crate-ci.github.io/release/changelog.html
Release Process
- Edit
Cargo.tomland bump version number. - Run
../release.sh
Structs
A fixed-length byte buffer. You can write bytes to it and then read them back.
Functions
Deframes lines that are terminated by b"\r\n".
Ignores solitary b'\n'.
Deframes lines that are terminated by either b'\n' or b"\r\n".
Convert a byte slice into a string. Includes printable ASCII characters as-is. Converts non-printable or non-ASCII characters to strings like “\n” and “\x19”.