Skip to content

Commit

Permalink
feature: frame encoding
Browse files Browse the repository at this point in the history
  • Loading branch information
rcrwhyg committed Sep 5, 2024
1 parent 9c098a1 commit 0c47ab5
Show file tree
Hide file tree
Showing 6 changed files with 289 additions and 25 deletions.
61 changes: 61 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@ license = "MIT"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
anyhow = "1.0.86"
bytes = "1.7.1"
enum_dispatch = "0.3.13"
3 changes: 2 additions & 1 deletion deny.toml
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,8 @@ ignore = [
# [possible values: any SPDX 3.11 short identifier (+ optional exception)].
allow = [
"MIT",
#"Apache-2.0",
"Apache-2.0",
"Unicode-DFS-2016",
#"Apache-2.0 WITH LLVM-exception",
]
# The confidence threshold for detecting a license from license text.
Expand Down
1 change: 1 addition & 0 deletions src/resp/decode.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// empty file
169 changes: 153 additions & 16 deletions src/resp/encode.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,28 @@
/*
- 如何解析 Frame
- simple string: "+OK\r\n"
- error: "-Error message\r\n"
- bulk error: "!<length>\r\n<error>\r\n"
- integer: ":[<+|->]<value>\r\n"
- bulk string: "$<length>\r\n<data>\r\n"
- null bulk string: "$-1\r\n"
- array: "*<number-of-elements>\r\n<element-1>...<element-n>"
- "*2\r\n$3\r\nget\r\n$5\r\nhello\r\n"
- null array: "*-1\r\n"
- null: "_\r\n"
- boolean: "#<t|f>\r\n"
- double: ",[<+|->]<integral>[.<fractional>][<E|e>[sign]<exponent>]\r\n"
- map: "%<number-of-entries>\r\n<key-1><value-1>...<key-n><value-n>"
- set: "~<number-of-elements>\r\n<element-1>...<element-n>"
*/

use super::{
BulkString, RespArray, RespEncode, RespFrame, RespMap, RespNull, RespNullBulkString, RespSet,
SimpleError, SimpleString,
BulkString, RespArray, RespEncode, RespMap, RespNull, RespNullArray, RespNullBulkString,
RespSet, SimpleError, SimpleString,
};

const BUF_CAP: usize = 4096;

impl RespEncode for RespFrame {
fn encode(self) -> Vec<u8> {
todo!()
}
}

// - simple string: "+OK\r\n"
impl RespEncode for SimpleString {
fn encode(self) -> Vec<u8> {
Expand Down Expand Up @@ -51,8 +63,7 @@ impl RespEncode for RespNullBulkString {
}
}

// - array: "*<length>\r\n<item>\r\n<item>\r\n..."
const ARRAY_CAP: usize = 4096;
// - array: "*<number-of-elements>\r\n<element-1>...<element-n>"
impl RespEncode for RespArray {
fn encode(self) -> Vec<u8> {
let mut buf = Vec::with_capacity(BUF_CAP);
Expand All @@ -64,6 +75,13 @@ impl RespEncode for RespArray {
}
}

// - null array: "*-1\r\n"
impl RespEncode for RespNullArray {
fn encode(self) -> Vec<u8> {
b"*-1\r\n".to_vec()
}
}

// - null: "_\r\n"
impl RespEncode for RespNull {
fn encode(self) -> Vec<u8> {
Expand All @@ -78,12 +96,12 @@ impl RespEncode for bool {
}
}

// - double: ",[<+|->]<integeral>[.<fractional>][<E|e>[sign]<exponent>]]\r\n"
// - double: ",[<+|->]<integral>[.<fractional>][<E|e>[sign]<exponent>]]\r\n"
impl RespEncode for f64 {
fn encode(self) -> Vec<u8> {
let mut buf = Vec::with_capacity(32);
let ret = if self.abs() > 1e+8 {
format!("{:+e}", self)
let ret = if self.abs() > 1e+8 || self.abs() < 1e-8 {
format!(",{:+e}\r\n", self)
} else {
let sign = if self < 0.0 { "" } else { "+" };
format!(",{}{}\r\n", sign, self)
Expand All @@ -94,7 +112,7 @@ impl RespEncode for f64 {
}
}

// - map: "%<length>\r\n<key>\r\n<value>\r\n<key>\r\n<value>\r\n..."
// - map: "%<number-of-entries>\r\n<key-1><value-1>...<key-n><value-n>"
impl RespEncode for RespMap {
fn encode(self) -> Vec<u8> {
let mut buf = Vec::with_capacity(BUF_CAP);
Expand All @@ -107,14 +125,133 @@ impl RespEncode for RespMap {
}
}

// - set: "~<length>\r\n<item>\r\n<item>\r\n..."
// - set: "~<number-of-elements>\r\n<element-1>...<element-n>"
impl RespEncode for RespSet {
fn encode(self) -> Vec<u8> {
let mut buf = Vec::with_capacity(BUF_CAP);
buf.extend_from_slice(&format!("~{}\r\n", self.0.len()).into_bytes());
buf.extend_from_slice(&format!("~{}\r\n", self.len()).into_bytes());
for frame in self.0 {
buf.extend_from_slice(&frame.encode());
}
buf
}
}

#[cfg(test)]
mod tests {
use crate::RespFrame;

use super::*;

#[test]
fn test_simple_string_encode() {
let frame: RespFrame = SimpleString::new("OK").into();
assert_eq!(frame.encode(), b"+OK\r\n");
}

#[test]
fn test_error_encode() {
let frame: RespFrame = SimpleError::new("Error message").into();
assert_eq!(frame.encode(), b"-Error message\r\n");
}

#[test]
fn test_integer_encode() {
let frame: RespFrame = 123.into();
assert_eq!(frame.encode(), b":+123\r\n");

let frame: RespFrame = (-123).into();
assert_eq!(frame.encode(), b":-123\r\n");
}

#[test]
fn test_bulk_string_encode() {
let frame: RespFrame = BulkString::new("hello".as_bytes()).into();
assert_eq!(frame.encode(), b"$5\r\nhello\r\n");
}

#[test]
fn test_null_bulk_string_encode() {
let frame: RespFrame = RespNullBulkString.into();
assert_eq!(frame.encode(), b"$-1\r\n");
}

#[test]
fn test_array_encode() {
let frame: RespFrame = RespArray::new(vec![
BulkString::new("set").into(),
BulkString::new("hello").into(),
BulkString::new("world").into(),
])
.into();

assert_eq!(
&frame.encode(),
b"*3\r\n$3\r\nset\r\n$5\r\nhello\r\n$5\r\nworld\r\n"
);
}

#[test]
fn test_null_array_encode() {
let frame: RespFrame = RespNullArray.into();
assert_eq!(frame.encode(), b"*-1\r\n");
}

#[test]
fn test_null_encode() {
let frame: RespFrame = RespNull.into();
assert_eq!(frame.encode(), b"_\r\n");
}

#[test]
fn test_boolean_encode() {
let frame: RespFrame = true.into();
assert_eq!(frame.encode(), b"#t\r\n");

let frame: RespFrame = false.into();
assert_eq!(frame.encode(), b"#f\r\n");
}

#[test]
fn test_double_encode() {
let frame: RespFrame = 123.456.into();
assert_eq!(frame.encode(), b",+123.456\r\n");

let frame: RespFrame = (-123.456).into();
assert_eq!(frame.encode(), b",-123.456\r\n");

let frame: RespFrame = 1.23456e+8.into();
assert_eq!(frame.encode(), b",+1.23456e8\r\n");

let frame: RespFrame = (-1.23456e-9).into();
assert_eq!(frame.encode(), b",-1.23456e-9\r\n");
}

#[test]
fn test_map_encode() {
let mut map = RespMap::new();
map.insert("hello".into(), BulkString::new("world").into());
map.insert("foo".into(), (-123456.789).into());

let frame: RespFrame = map.into();

assert_eq!(
&frame.encode(),
b"%2\r\n+foo\r\n,-123456.789\r\n+hello\r\n$5\r\nworld\r\n"
);
}

#[test]
fn test_set_encode() {
let frame: RespFrame = RespSet::new(vec![
RespArray::new([1234.into(), true.into()]).into(),
BulkString::new("world").into(),
])
.into();

assert_eq!(
&frame.encode(),
b"~2\r\n*2\r\n:+1234\r\n#t\r\n$5\r\nworld\r\n"
);
}
}
Loading

0 comments on commit 0c47ab5

Please # to comment.