resp-rs 0.1.1

Zero-copy RESP2 and RESP3 protocol parser and serializer
Documentation

resp-rs

Zero-copy RESP2 and RESP3 protocol parser and serializer for Rust.

Crates.io Documentation CI License

A high-performance Rust library for parsing and serializing the Redis Serialization Protocol (RESP), supporting both RESP2 and RESP3.

Features

  • Zero-copy parsing -- uses bytes::Bytes to slice into the input buffer without copying
  • RESP2 and RESP3 -- full support for both protocol versions with separate frame types
  • Streaming parser -- handles partial reads and pipelining for incremental TCP data
  • High performance -- 4.8-8.0 GB/s throughput in benchmarks
  • Minimal dependencies -- only bytes and thiserror
  • No async runtime -- pure sync parsing that works in any context

Usage

Add to your Cargo.toml:

[dependencies]
resp-rs = "0.1"

Parse a RESP3 frame

use bytes::Bytes;
use resp_rs::resp3;

let data = Bytes::from("+OK\r\n");
let (frame, remaining) = resp3::parse_frame(data).unwrap();
assert_eq!(frame, resp3::Frame::SimpleString(Bytes::from("OK")));
assert!(remaining.is_empty());

Parse a RESP2 frame

use bytes::Bytes;
use resp_rs::resp2;

let data = Bytes::from("*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n");
let (frame, _) = resp2::parse_frame(data).unwrap();
// frame is Array([BulkString("SET"), BulkString("key"), BulkString("value")])

Serialize a frame

use bytes::Bytes;
use resp_rs::resp2::{Frame, frame_to_bytes};

let frame = Frame::Array(Some(vec![
    Frame::BulkString(Some(Bytes::from("GET"))),
    Frame::BulkString(Some(Bytes::from("mykey"))),
]));
let wire = frame_to_bytes(&frame);
assert_eq!(wire, Bytes::from("*2\r\n$3\r\nGET\r\n$5\r\nmykey\r\n"));

Streaming parser (incremental TCP reads)

use bytes::Bytes;
use resp_rs::resp3::Parser;

let mut parser = Parser::new();

// Feed partial data
parser.feed(Bytes::from("+HEL"));
assert!(parser.next_frame().unwrap().is_none()); // incomplete

// Feed the rest
parser.feed(Bytes::from("LO\r\n"));
let frame = parser.next_frame().unwrap().unwrap(); // complete!

RESP3 types

RESP3 adds several types beyond RESP2:

use bytes::Bytes;
use resp_rs::resp3::{self, Frame};

// Null
let (frame, _) = resp3::parse_frame(Bytes::from("_\r\n")).unwrap();
assert_eq!(frame, Frame::Null);

// Boolean
let (frame, _) = resp3::parse_frame(Bytes::from("#t\r\n")).unwrap();
assert_eq!(frame, Frame::Boolean(true));

// Double
let (frame, _) = resp3::parse_frame(Bytes::from(",3.14\r\n")).unwrap();
assert_eq!(frame, Frame::Double(3.14));

// Map
let data = Bytes::from("%2\r\n+key1\r\n:1\r\n+key2\r\n:2\r\n");
let (frame, _) = resp3::parse_frame(data).unwrap();
// frame is Map([(SimpleString("key1"), Integer(1)), (SimpleString("key2"), Integer(2))])

Supported RESP3 types

Type Tag Example
Simple String + +OK\r\n
Simple Error - -ERR msg\r\n
Integer : :42\r\n
Bulk String $ $5\r\nhello\r\n
Null _ _\r\n
Boolean # #t\r\n
Double , ,3.14\r\n
Big Number ( (123456\r\n
Blob Error ! !5\r\nERROR\r\n
Verbatim String = =15\r\ntxt:hello world\r\n
Array * *2\r\n:1\r\n:2\r\n
Map % %1\r\n+k\r\n+v\r\n
Set ~ ~2\r\n:1\r\n:2\r\n
Attribute | |1\r\n+k\r\n+v\r\n
Push > >2\r\n+msg\r\n:1\r\n

Streaming variants (chunked strings, arrays, maps, sets, attributes, pushes) are also fully supported.

Benchmarks

Run benchmarks with:

cargo bench

Minimum Supported Rust Version

The MSRV is 1.85 (Rust 2024 edition).

License

Licensed under either of

at your option.

Contributing

Contributions are welcome. Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.