From: Greg Burri Date: Sat, 17 Sep 2022 21:17:56 +0000 (+0200) Subject: Second part of chapter 7: ActionKV (WIP) X-Git-Url: https://git.euphorik.ch/?a=commitdiff_plain;h=73371d4a520ba19727df92d1cf122c04ba583257;p=rust_in_action.git Second part of chapter 7: ActionKV (WIP) --- diff --git a/ch7-file-and-storage/actionkv/Cargo.lock b/ch7-file-and-storage/actionkv/Cargo.lock new file mode 100644 index 0000000..e9c7fc1 --- /dev/null +++ b/ch7-file-and-storage/actionkv/Cargo.lock @@ -0,0 +1,113 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "actionkv" +version = "0.1.0" +dependencies = [ + "bincode", + "byteorder", + "crc", + "serde", + "serde_cbor", + "serde_derive", +] + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "crc" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53757d12b596c16c78b83458d732a5d1a17ab3f53f2f7412f6fb57cc8a140ab3" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d0165d2900ae6778e36e80bbc4da3b5eefccee9ba939761f9c2882a5d9af3ff" + +[[package]] +name = "half" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" + +[[package]] +name = "proc-macro2" +version = "1.0.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "serde" +version = "1.0.144" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860" + +[[package]] +name = "serde_cbor" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" +dependencies = [ + "half", + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.144" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "syn" +version = "1.0.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd" diff --git a/ch7-file-and-storage/actionkv/Cargo.toml b/ch7-file-and-storage/actionkv/Cargo.toml new file mode 100644 index 0000000..23e6703 --- /dev/null +++ b/ch7-file-and-storage/actionkv/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "actionkv" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +byteorder = "1.4" +crc ="3" +bincode = "1" +serde = "1" +serde_cbor = "0.11" +serde_derive = "1" + +[lib] +name = "libactionkv" +path = "src/lib.rs" + +[[bin]] +name = "akv_mem" +path = "src/akv_mem.rs" \ No newline at end of file diff --git a/ch7-file-and-storage/actionkv/src/akv_mem.rs b/ch7-file-and-storage/actionkv/src/akv_mem.rs new file mode 100644 index 0000000..87d3c0c --- /dev/null +++ b/ch7-file-and-storage/actionkv/src/akv_mem.rs @@ -0,0 +1,57 @@ +use libactionkv::ActionKV; + +#[cfg(target_os = "windows")] +const USAGE: &str = " +Usage: + akv_mem.exe FILE get KEY + akv_mem.exe FILE delete KEY + akv_mem.exe FILE insert KEY VALUE + akv_mem.exe FILE update KEY VALUE +"; + +// TODO: Is there a more elegant way to do that? +#[cfg(not(target_os = "windows"))] +const USAGE: &str = " +Usage: + akv_mem FILE get KEY + akv_mem FILE delete KEY + akv_mem FILE insert KEY VALUE + akv_mem FILE update KEY VALUE +"; + +fn main() { + let args: Vec = std::env::args().collect(); + let fname = args.get(1).expect(&USAGE); + + let action: &str = args.get(2).expect(&USAGE).as_ref(); + let key: &str = args.get(3).expect(&USAGE).as_ref(); + let maybe_value = args.get(4); + + let path = std::path::Path::new(&fname); + let mut store = ActionKV::open(path).expect("unable to open file"); + + store.load().expect("unable to load data"); + + match action { + /* + "get" => match store.get(key).unwrap() { + None => eprintln!("{} not found", key), + Some(value) => println!("{}", value), + }, + + "delete" => store.delete(key).unwrap(), + + "insert" => { + let value = maybe_value.expect(&USAGE).as_ref(); + store.insert(key, value).unwrap(); + }, + + "update" => { + let value = maybe_value.expect(&USAGE).as_ref(); + store.update(key, value).unwrap(); + }, + */ + + _ => eprintln!("{}", &USAGE), + } +} diff --git a/ch7-file-and-storage/actionkv/src/lib.rs b/ch7-file-and-storage/actionkv/src/lib.rs new file mode 100644 index 0000000..c626a82 --- /dev/null +++ b/ch7-file-and-storage/actionkv/src/lib.rs @@ -0,0 +1,80 @@ +use std::io::{self, BufReader, Seek, SeekFrom, Read}; +use byteorder::{LittleEndian, ReadBytesExt}; +use crc::Crc; +use std::fs::{File, OpenOptions}; +use std::path::Path; +use std::collections::HashMap; +use serde_derive::{Serialize, Deserialize}; + +type ByteString = Vec; +type ByteStr = [u8]; + +#[derive(Debug, Serialize, Deserialize)] +pub struct KeyValuePair { + pub key: ByteString, + pub value: ByteString, +} + +#[derive(Debug)] +pub struct ActionKV { + f: File, + pub index: HashMap, +} + +impl ActionKV { + fn process_record(f: &mut R) -> io::Result { + let saved_checksum = f.read_u32::()?; + let key_len = f.read_u32::()?; + let val_len = f.read_u32::()?; + let data_len = key_len + val_len; + + let mut data = ByteString::with_capacity(data_len as usize); + + { + f.by_ref().take(data_len as u64).read_to_end(&mut data)?; + } + + debug_assert_eq!(data.len(), data_len as usize); + + let crc = Crc::::new(&crc::CRC_32_CKSUM); + let checksum = crc.checksum(&data); + if checksum != saved_checksum { + panic!("data corruption encountered ({:08x} != {:08x})", checksum, saved_checksum); + } + + let value = data.split_off(key_len as usize); + let key = data; + + Ok(KeyValuePair { key, value }) + } + + pub fn open(path: &Path) -> io::Result { + let f = OpenOptions::new().read(true).write(true).create(true).append(true).open(path)?; + let index = HashMap::new(); + Ok(ActionKV { f, index }) + } + + pub fn load(&mut self) -> io::Result<()> { + let mut f = BufReader::new(&mut self.f); + loop { + let position = f.seek(SeekFrom::Current(0))?; + let maybe_kv = ActionKV::process_record(&mut f); + + let kv = match maybe_kv { + Ok(kv) => kv, + Err(err) => { + match err.kind() { + io::ErrorKind::UnexpectedEof => { + break; + }, + _ => return Err(err), + } + } + }; + + self.index.insert(kv.key, position); + } + + Ok(()) + } +} \ No newline at end of file