Skip to main content

Crate redust_resp

Crate redust_resp 

Source
Expand description

§Redust RESP

Redust Documentation

A simple RESP parser for Rust.

This crate implements RESP2, the protocol Redis uses to communicate with clients, on top of serde. It powers the redust Redis client but has no dependency on any particular network stack.

  • serde integration: deserialize RESP directly into your own types with from_bytes, or serialize them into RESP with to_bytes.
  • Dynamic data: the Data enum represents any RESP value when the shape of a response isn’t known ahead of time. Convert between Data and Rust types with from_data and to_data.
  • Zero-copy: parsed strings borrow from the input wherever possible. Owned data from the codec shares the read buffer instead of copying out of it.
  • Errors as results: RESP errors are returned as Error::Redis rather than as data.

§Usage

Deserialize RESP bytes into Rust types:

use serde::Deserialize;

#[derive(Debug, Deserialize, PartialEq)]
struct Reply<'a> {
    #[serde(borrow, with = "serde_bytes")]
    name: &'a [u8],
    count: i64,
}

let bytes = b"*4\r\n+name\r\n$5\r\nredis\r\n+count\r\n:42\r\n";
let (reply, remaining) = redust_resp::from_bytes::<Reply>(bytes).unwrap();

assert_eq!(reply, Reply { name: b"redis", count: 42 });
assert!(remaining.is_empty());

Or work with dynamic Data:

use redust_resp::{from_bytes, from_data, to_bytes, Data};

let (data, _) = from_bytes::<Data>(b"*2\r\n$3\r\nGET\r\n$3\r\nfoo\r\n").unwrap();
assert_eq!(data, Data::Array(vec![Data::bulk_string("GET"), Data::bulk_string("foo")]));

let parts: Vec<&[u8]> = from_data(data.clone()).unwrap();
assert_eq!(parts, [b"GET", b"foo"]);

let mut buf = Vec::new();
to_bytes(&data, &mut buf).unwrap();
assert_eq!(buf, b"*2\r\n$3\r\nGET\r\n$3\r\nfoo\r\n");

The util module contains serde helpers for common Redis encodings, such as numbers stored in bulk strings.

§Features

  • codec: a tokio-util Codec which incrementally decodes RESP from a byte stream into Data.

§Benchmarks

In-memory comparison with the parser built into redis-rs 1.8.0 and redis-protocol 6.0.0 (the parser used by fred). Times are criterion’s median estimate; lower is better.

§Parsing into dynamic data

redust borrows from the input. redust (owned) also calls Data::into_owned, as the codec does, which is the fairer comparison since the other parsers return owned values.

Inputredustredust (owned)redis-rsredis-protocol
+OK18.5 ns34.1 ns390 ns80.2 ns
:123456789018.5 ns20.5 ns429 ns58.7 ns
bulk string, 1 KiB14.3 ns91.0 ns545 ns119 ns
bulk string, 64 KiB15.1 ns1.31 µs8.51 µs1.49 µs
array of 100 × 16 B1.93 µs9.80 µs23.4 µs9.40 µs
array of 1000 × 16 B18.8 µs90.9 µs217 µs87.5 µs

§Encoding a SET command

Value sizeredustredis-rsredis-protocol
16 B210 ns362 ns400 ns
1 KiB219 ns384 ns440 ns
64 KiB1.21 µs3.08 µs3.57 µs

Measured on an AMD Ryzen 9 3900X running Windows 10 and Rust 1.95. To reproduce, see the benches crate.

Re-exports§

pub use resp2::data::de::from_data;
pub use resp2::data::ser::to_data;
pub use resp2::data::Bulk;
pub use resp2::data::Data;
pub use resp2::de::from_bytes;
pub use resp2::ser::to_bytes;

Modules§

resp2
util
Utils for RESP (de)serialization.

Macros§

array
Macro to simplify making a Data::Array.

Enums§

Error
Errors that can be encountered when interacting with RESP.
Needed
The amount of additional data needed to finish parsing.
ParseError
An error encountered while parsing RESP.

Type Aliases§

HipStr
Thread-safe shared string.
Result
Result with an error type defaulting to Error.