Skip to content
This repository was archived by the owner on Mar 29, 2025. It is now read-only.

Adding support for RDB version <= 12 and refactoring #19

Merged
merged 35 commits into from
Mar 29, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
4d52810
Add new rdb version and datatypes
bimtauer Dec 19, 2024
865bea2
Begin port to clap and revised structure
bimtauer Dec 19, 2024
677b503
Refactor, fix previous test, add rust integration test
bimtauer Dec 19, 2024
40bea25
Introduce file output and simplify integration test
bimtauer Dec 19, 2024
a28198f
Change encoding of non ascii chars
bimtauer Dec 22, 2024
7b06391
Implement hash list pack
bimtauer Dec 22, 2024
4decc7b
Begin factoring out formatter from parser
bimtauer Dec 23, 2024
3e25fe5
Further refactor formatter
bimtauer Dec 23, 2024
2d11517
Fix tests for refactored json formatter, cleanup pending
bimtauer Dec 25, 2024
0633c9e
Refactor formatter, generic format method
bimtauer Dec 27, 2024
7aaf4ea
Implement skip for hash list pack
bimtauer Dec 27, 2024
8083786
Add integration tests for plain and protocol format
bimtauer Dec 27, 2024
f5c159b
Improve error handling
bimtauer Dec 27, 2024
036be25
Create protocol based integration test across versions
bimtauer Dec 29, 2024
e4bf3c2
Implement listpack handling, better errors, ensure that all previousl…
bimtauer Dec 29, 2024
0439a7b
Implement sorted set v2 and listpack encodings
bimtauer Dec 30, 2024
3115477
Add logging
bimtauer Dec 30, 2024
c0424ae
Small cleanup
bimtauer Dec 30, 2024
b16a169
Restructure code and public lib interface
bimtauer Jan 1, 2025
ca0cd68
Add python bindings
bimtauer Jan 2, 2025
bac656d
Update readme
bimtauer Jan 2, 2025
419a88c
Update changelog
bimtauer Jan 2, 2025
ead9e1a
Link changelog
bimtauer Jan 2, 2025
b7bcbb5
Fix start and end formatting
bimtauer Jan 2, 2025
fcdf100
Integrate shell script tests fully into rust integration tests
bimtauer Jan 2, 2025
8152adf
Remove makefile
bimtauer Jan 2, 2025
74b5836
Update ignore and rm python version
bimtauer Jan 2, 2025
361a432
Heed clippy's call
bimtauer Jan 2, 2025
279fce9
Add Rust CI workflow
bimtauer Jan 2, 2025
7a5bfac
Clean
bimtauer Jan 13, 2025
cd8a83a
Fix Redis Container Integration Test
bimtauer Jan 14, 2025
817f9b4
Adapt python CI
bimtauer Jan 14, 2025
ef4608a
Prep release
bimtauer Jan 14, 2025
2ce44ab
Removing token should use trusted publisher
bimtauer Jan 14, 2025
2a87c62
Rename python project
bimtauer Jan 15, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 9 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
[package]

name = "rdb"
version = "0.2.1"
authors = ["Jan-Erik Rediger <[email protected]>"]

keywords = ["redis", "database"]
edition = "2021"
version = "0.3.0"
authors = ["Jan-Erik Rediger <[email protected]>", "Tim Bauer <[email protected]>"]
keywords = ["redis", "database", "rdb", "parser"]
description = "Fast and efficient RDB parsing utility"

readme = "README.md"
license = "MIT"

homepage = "http://rdb.fnordig.de/"
documentation = "http://rdb.fnordig.de/doc/rdb/"
repository = "https://github.com/badboy/rdb-rs"
Expand All @@ -34,3 +31,8 @@ getopts = "0.2"
rustc-serialize = "0.3"
regex = "0.1"
byteorder = "0.5"
thiserror = "1.0"
clap = { version = "4.4", features = ["derive"] }

[dev-dependencies]
rstest = "0.23.0"
Binary file added dump.rdb
Binary file not shown.
54 changes: 27 additions & 27 deletions src/constants.rs
Original file line number Diff line number Diff line change
@@ -1,42 +1,42 @@
pub mod version {
pub const SUPPORTED_MINIMUM : u32 = 1;
pub const SUPPORTED_MAXIMUM : u32 = 12;
pub const SUPPORTED_MINIMUM: u32 = 1;
pub const SUPPORTED_MAXIMUM: u32 = 12;
}

pub mod constant {
pub const RDB_6BITLEN : u8 = 0;
pub const RDB_14BITLEN : u8 = 1;
pub const RDB_ENCVAL : u8 = 3;
pub const RDB_MAGIC : &'static str = "REDIS";
pub const RDB_6BITLEN: u8 = 0;
pub const RDB_14BITLEN: u8 = 1;
pub const RDB_ENCVAL: u8 = 3;
pub const RDB_MAGIC: &'static str = "REDIS";
}

pub mod op_code {
pub const MODULE_AUX: u8 = 247;
pub const IDLE: u8 = 248;
pub const FREQ: u8 = 249;
pub const AUX : u8 = 250;
pub const RESIZEDB : u8 = 251;
pub const EXPIRETIME_MS : u8 = 252;
pub const EXPIRETIME : u8 = 253;
pub const SELECTDB : u8 = 254;
pub const EOF : u8 = 255;
pub const AUX: u8 = 250;
pub const RESIZEDB: u8 = 251;
pub const EXPIRETIME_MS: u8 = 252;
pub const EXPIRETIME: u8 = 253;
pub const SELECTDB: u8 = 254;
pub const EOF: u8 = 255;
}

pub mod encoding_type {
pub const STRING : u8 = 0;
pub const LIST : u8 = 1;
pub const SET : u8 = 2;
pub const ZSET : u8 = 3;
pub const HASH : u8 = 4;
pub const STRING: u8 = 0;
pub const LIST: u8 = 1;
pub const SET: u8 = 2;
pub const ZSET: u8 = 3;
pub const HASH: u8 = 4;
pub const ZSET_2: u8 = 5;
pub const MODULE: u8 = 6;
pub const MODULE_2: u8 = 7;
pub const HASH_ZIPMAP : u8 = 9;
pub const LIST_ZIPLIST : u8 = 10;
pub const SET_INTSET : u8 = 11;
pub const ZSET_ZIPLIST : u8 = 12;
pub const HASH_ZIPLIST : u8 = 13;
pub const LIST_QUICKLIST : u8 = 14;
pub const HASH_ZIPMAP: u8 = 9;
pub const LIST_ZIPLIST: u8 = 10;
pub const SET_INTSET: u8 = 11;
pub const ZSET_ZIPLIST: u8 = 12;
pub const HASH_ZIPLIST: u8 = 13;
pub const LIST_QUICKLIST: u8 = 14;
pub const STREAM_LIST_PACKS: u8 = 15;
pub const HASH_LIST_PACK: u8 = 16;
pub const ZSET_LIST_PACK: u8 = 17;
Expand All @@ -47,8 +47,8 @@ pub mod encoding_type {
}

pub mod encoding {
pub const INT8 : u32 = 0;
pub const INT16 : u32 = 1;
pub const INT32 : u32 = 2;
pub const LZF : u32 = 3;
pub const INT8: u32 = 0;
pub const INT16: u32 = 1;
pub const INT32: u32 = 2;
pub const LZF: u32 = 3;
}
27 changes: 17 additions & 10 deletions src/filter.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,30 @@
use std::str;
use crate::types::Type;
use regex::Regex;
use types::Type;
use std::str;

pub trait Filter {
fn matches_db(&self, _db: u32) -> bool { true }
fn matches_type(&self, _enc_type: u8) -> bool { true }
fn matches_key(&self, _key: &[u8]) -> bool { true }

fn matches_db(&self, _db: u32) -> bool {
true
}
fn matches_type(&self, _enc_type: u8) -> bool {
true
}
fn matches_key(&self, _key: &[u8]) -> bool {
true
}
}

#[derive(Default)]
pub struct Simple {
databases: Vec<u32>,
types: Vec<Type>,
keys: Option<Regex>
keys: Option<Regex>,
}

impl Simple {
pub fn new() -> Simple { Simple::default() }
pub fn new() -> Simple {
Simple::default()
}

pub fn add_database(&mut self, db: u32) {
self.databases.push(db);
Expand All @@ -43,7 +50,7 @@ impl Filter for Simple {

fn matches_type(&self, enc_type: u8) -> bool {
if self.types.is_empty() {
return true
return true;
}

let typ = Type::from_encoding(enc_type);
Expand All @@ -54,7 +61,7 @@ impl Filter for Simple {
match self.keys.clone() {
None => true,
Some(re) => {
let key = unsafe{str::from_utf8_unchecked(key)};
let key = unsafe { str::from_utf8_unchecked(key) };
re.is_match(key)
}
}
Expand Down
45 changes: 26 additions & 19 deletions src/formatter/json.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
#![allow(unused_must_use)]

use formatter::Formatter;
use super::write_str;
use crate::formatter::Formatter;
use crate::types::EncodingType;
use serialize::json;
use std::io;
use std::io::Write;
use std::str;
use serialize::json;
use types::EncodingType;
use super::write_str;

pub struct JSON {
out: Box<Write+'static>,
out: Box<dyn Write + 'static>,
is_first_db: bool,
has_databases: bool,
is_first_key_in_db: bool,
elements_in_key: u32,
element_index: u32
element_index: u32,
}

impl JSON {
Expand All @@ -26,13 +26,13 @@ impl JSON {
has_databases: false,
is_first_key_in_db: true,
elements_in_key: 0,
element_index: 0
element_index: 0,
}
}
}

fn encode_to_ascii(value: &[u8]) -> String {
let s = unsafe{str::from_utf8_unchecked(value)};
let s = unsafe { str::from_utf8_unchecked(value) };
json::encode(&s).unwrap()
}

Expand All @@ -47,7 +47,7 @@ impl JSON {
self.element_index = 0;
}

fn end_key(&mut self) { }
fn end_key(&mut self) {}

fn write_comma(&mut self) {
if self.element_index > 0 {
Expand Down Expand Up @@ -94,8 +94,7 @@ impl Formatter for JSON {
self.write_value(value);
}

fn start_hash(&mut self, key: &[u8], length: u32,
_expiry: Option<u64>, _info: EncodingType) {
fn start_hash(&mut self, key: &[u8], length: u32, _expiry: Option<u64>, _info: EncodingType) {
self.start_key(length);
self.write_key(key);
write_str(&mut self.out, ":{");
Expand All @@ -116,8 +115,13 @@ impl Formatter for JSON {
self.out.flush();
}

fn start_set(&mut self, key: &[u8], cardinality: u32,
_expiry: Option<u64>, _info: EncodingType) {
fn start_set(
&mut self,
key: &[u8],
cardinality: u32,
_expiry: Option<u64>,
_info: EncodingType,
) {
self.start_key(cardinality);
self.write_key(key);
write_str(&mut self.out, ":[");
Expand All @@ -134,8 +138,7 @@ impl Formatter for JSON {
self.write_value(member);
}

fn start_list(&mut self, key: &[u8], length: u32,
_expiry: Option<u64>, _info: EncodingType) {
fn start_list(&mut self, key: &[u8], length: u32, _expiry: Option<u64>, _info: EncodingType) {
self.start_key(length);
self.write_key(key);
write_str(&mut self.out, ":[");
Expand All @@ -151,8 +154,13 @@ impl Formatter for JSON {
self.write_value(value);
}

fn start_sorted_set(&mut self, key: &[u8], length: u32,
_expiry: Option<u64>, _info: EncodingType) {
fn start_sorted_set(
&mut self,
key: &[u8],
length: u32,
_expiry: Option<u64>,
_info: EncodingType,
) {
self.start_key(length);
self.write_key(key);
write_str(&mut self.out, ":{");
Expand All @@ -163,8 +171,7 @@ impl Formatter for JSON {
write_str(&mut self.out, "}");
}

fn sorted_set_element(&mut self, _key: &[u8],
score: f64, member: &[u8]) {
fn sorted_set_element(&mut self, _key: &[u8], score: f64, member: &[u8]) {
self.write_comma();
self.write_key(member);
write_str(&mut self.out, ":");
Expand Down
18 changes: 12 additions & 6 deletions src/formatter/mod.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
use std::io::Write;

pub use self::json::JSON;
pub use self::nil::Nil;
pub use self::plain::Plain;
pub use self::json::JSON;
pub use self::protocol::Protocol;

use super::types::EncodingType;

pub mod json;
pub mod nil;
pub mod plain;
pub mod json;
pub mod protocol;


pub fn write_str<W: Write>(out: &mut W, data: &str) {
out.write(data.as_bytes()).unwrap();
}
Expand All @@ -35,16 +34,23 @@ pub trait Formatter {
fn end_hash(&mut self, key: &[u8]) {}
fn hash_element(&mut self, key: &[u8], field: &[u8], value: &[u8]) {}


fn start_set(&mut self, key: &[u8], cardinality: u32, expiry: Option<u64>, info: EncodingType) {}
fn start_set(&mut self, key: &[u8], cardinality: u32, expiry: Option<u64>, info: EncodingType) {
}
fn end_set(&mut self, key: &[u8]) {}
fn set_element(&mut self, key: &[u8], member: &[u8]) {}

fn start_list(&mut self, key: &[u8], length: u32, expiry: Option<u64>, info: EncodingType) {}
fn end_list(&mut self, key: &[u8]) {}
fn list_element(&mut self, key: &[u8], value: &[u8]) {}

fn start_sorted_set(&mut self, key: &[u8], length: u32, expiry: Option<u64>, info: EncodingType) {}
fn start_sorted_set(
&mut self,
key: &[u8],
length: u32,
expiry: Option<u64>,
info: EncodingType,
) {
}
fn end_sorted_set(&mut self, key: &[u8]) {}
fn sorted_set_element(&mut self, key: &[u8], score: f64, member: &[u8]) {}
}
2 changes: 1 addition & 1 deletion src/formatter/nil.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use formatter::Formatter;
use crate::formatter::Formatter;

pub struct Nil;

Expand Down
33 changes: 20 additions & 13 deletions src/formatter/plain.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
#![allow(unused_must_use)]
use formatter::Formatter;
use super::write_str;
use crate::formatter::Formatter;
use crate::types::EncodingType;
use serialize::hex::ToHex;
use std::io;
use std::io::Write;
use serialize::hex::ToHex;
use types::EncodingType;
use super::write_str;

pub struct Plain {
out: Box<Write+'static>,
out: Box<dyn Write + 'static>,
dbnum: u32,
index: u32
index: u32,
}

impl Plain {
pub fn new() -> Plain {
let out = Box::new(io::stdout());
Plain { out: out, dbnum: 0, index: 0 }
Plain {
out: out,
dbnum: 0,
index: 0,
}
}

fn write_line_start(&mut self) {
Expand Down Expand Up @@ -76,8 +80,7 @@ impl Formatter for Plain {
self.out.flush();
}

fn start_list(&mut self, _key: &[u8], _length: u32,
_expiry: Option<u64>, _info: EncodingType) {
fn start_list(&mut self, _key: &[u8], _length: u32, _expiry: Option<u64>, _info: EncodingType) {
self.index = 0;
}
fn list_element(&mut self, key: &[u8], value: &[u8]) {
Expand All @@ -92,13 +95,17 @@ impl Formatter for Plain {
self.index += 1;
}

fn start_sorted_set(&mut self, _key: &[u8], _length: u32,
_expiry: Option<u64>, _info: EncodingType) {
fn start_sorted_set(
&mut self,
_key: &[u8],
_length: u32,
_expiry: Option<u64>,
_info: EncodingType,
) {
self.index = 0;
}

fn sorted_set_element(&mut self, key: &[u8],
score: f64, member: &[u8]) {
fn sorted_set_element(&mut self, key: &[u8], score: f64, member: &[u8]) {
self.write_line_start();

self.out.write_all(key);
Expand Down
Loading