Skip to main content

Crate slipspeed

Crate slipspeed 

Source
Expand description

§SLIPspeed

CI

SLIPspeed logo

A pure-Rust implementation of the Serial Line Internet Protocol (SLIP) encoder and decoder with support for in-memory buffers as well as streaming I/O.

The crate exposes convenience helpers for encoding and decoding byte slices as well as SlipWriter and SlipReader wrappers for any std::io::Write or std::io::Read implementor.

A C/C++ port of this library, focused on embedded systems, is available at libslipstream, (but sadly, the slipstream crate name on crates.io is already taken).

§Performance

SLIPspeed attempts to be as fast as the speed of light will allow.

Generally, passing more than one byte at a time can improve performance by reducing the overhead of multiple function calls and allowing for better optimization by utilizing memchr to quickly locate special characters in the input data. This approach uses vectorized instructions and efficient memory scanning techniques to process larger chunks of data in a single operation, leading to significant speed improvements compared to processing one byte at a time.

The integrated benchmark encodes ASCII fraes random frames of varying lengths and reports throughput for encoding and decoding. Performance is higher for ASCII frames, as random frames may include lots of characters that need escaping. To run the benchmark example:

RUSTFLAGS="-C target-cpu=native" cargo run --release --example benchmark

Example results on a AMD Ryzen 5 3600 CPU (64 byte frames, 5 million frames):

--- Benchmark: random bytes ---
Frames processed: 5000000
Encoded bytes: 650000616
Encoding took: 697.361468ms (139.47 ns/frame)
Encoding throughput: 932.09 MB/s
Decoding took: 908.336552ms (181.67 ns/frame)
Decoding throughput: 715.59 MB/s

--- Benchmark: ASCII-only bytes ---
Frames processed: 5000000
Encoded bytes: 645000000
Encoding took: 226.643427ms (45.33 ns/frame)
Encoding throughput: 2845.88 MB/s
Decoding took: 488.094976ms (97.62 ns/frame)
Decoding throughput: 1321.46 MB/s

§Frame Structure

SLIP is specified in RFC 1055 and uses the following conventions:

  • Frames are delimited by the byte 0xC0 (END).
  • Literal END bytes in the payload are escaped as the two-byte sequence 0xDB 0xDC (ESC, ESC_END).
  • Literal escape bytes 0xDB (ESC) are encoded as 0xDB 0xDD (ESC, ESC_ESC).
  • The decoder clears its buffer whenever it encounters an END, emitting the accumulated payload as a frame.

Note: This implementation does not add or verify any checksums. If you require integrity checks, consider adding a CRC or similar mechanism to your payloads.

In contrast to the simple_slip crate, we do not add extra END bytes before a frame. This allows for more efficient streaming scenarios where frames are sent back-to-back.

§Quick Start

use slipspeed::{decode_frame, encode_frame, Result};

fn main() -> Result<()> {
	let frame = encode_frame(b"hello");
	let payload = decode_frame(&frame)?;
	assert_eq!(payload, b"hello");
	Ok(())
}

Run cargo run --example basic for a complete program.

§Streaming I/O

use slipspeed::{SlipReader, SlipWriter, Result};
use std::io::Cursor;

fn main() -> Result<()> {
	let mut writer = SlipWriter::new(Vec::new());
	writer.write_frame(b"ping")?;
	writer.write_frame(b"pong")?;
	let encoded = writer.into_inner();

	let mut reader = SlipReader::new(Cursor::new(encoded));
	while let Some(frame) = reader.read_frame()? {
		println!("Frame: {:?}", frame);
	}
	Ok(())
}

See examples/stream.rs for the full example.

§Utilities

  • encode_frame, decode_frames, and decode_frames_with_remainder for slice-based workflows.
  • encoded_len and decoded_lengths to inspect frame sizes without materialising payloads.
  • SlipReader::read_frame_length and SlipReader::take_remainder for streaming scenarios that require sizing or recovery after truncated input.

§Cargo Features

  • async-codec enables a slipspeed::async_codec::SlipCodec implementing the asynchronous_codec traits for runtime-agnostic async I/O.
  • tokio-codec enables a slipspeed::tokio_codec::SlipCodec compatible with tokio_util::codec Framed adapters.

§Additional Examples

  • cargo run --example async_codec --features async-codec demonstrates the runtime-agnostic asynchronous_codec integration.
  • cargo run --example tokio_codec --features tokio-codec showcases usage with Tokio’s duplex streams and tokio_util::codec::Framed.
  • cargo run --example benchmark performs a reproducible encoding and decoding micro-benchmark over pseudo-random frames.

§Benchmark example

The examples/benchmark.rs program performs a reproducible micro-benchmark using a fixed-seed LCG (Linear Congruential Generator). By default it generates a large number of small random frames and reports:

  • total frames processed
  • total encoded bytes
  • wall-clock time for encoding and decoding
  • per-frame average (ns/frame)

This example is intended as a simple throughput sanity check rather than a rigorous benchmark (it prints elapsed times to stdout). To run the example:

cargo run --example benchmark

To make the example quicker during development, lower the FRAME_COUNT constant at the top of the example file (for example to 20_000). The RNG seed is fixed (0xDEADBEEF) for reproducible runs.

Structs§

FrameRemainder
Captures decoded bytes that were buffered when a stream ended without a terminating END byte.
SlipReader
Reader wrapper that decodes SLIP frames from an underlying byte stream.
SlipWriter
Writer wrapper that encodes outgoing frames as SLIP before forwarding them to the underlying writer.

Enums§

SlipError
Error type for SLIP encoding and decoding operations.

Constants§

END
SLIP END byte (0xC0).
ESC
SLIP ESC byte (0xDB).
ESC_END
SLIP ESC END byte (0xDC).
ESC_ESC
SLIP ESC ESC byte (0xDD).

Functions§

decode_frame
Decode a single SLIP frame from the provided bytes.
decode_frames
Decode all SLIP frames contained in the provided byte slice.
decode_frames_iter
Decode all SLIP frames produced by the given iterator over bytes.
decode_frames_iter_with_remainder
Iterator variant of decode_frames_with_remainder.
decode_frames_with_remainder
Decode SLIP frames and also return any buffered remainder when the input ends without a trailing END.
decoded_lengths
Determine the decoded length of each SLIP frame in the provided input without materialising the payloads.
decoded_lengths_iter
Iterator variant of decoded_lengths.
encode_frame
Encode arbitrary bytes as a SLIP frame and return the encoded data as a newly allocated Vec.
encode_into_writer
Encode bytes as SLIP and write the result directly into the provided writer.
encode_iter
Encode an arbitrary iterator of bytes as a SLIP frame and return the encoded data.
encoded_len
Compute the encoded length (including the trailing END delimiter) without allocating.

Type Aliases§

Result
Convenient result alias used throughout the crate.