Second part of chapter 7: ActionKV (WIP)
authorGreg Burri <greg.burri@gmail.com>
Sat, 17 Sep 2022 21:17:56 +0000 (23:17 +0200)
committerGreg Burri <greg.burri@gmail.com>
Sat, 17 Sep 2022 21:17:56 +0000 (23:17 +0200)
ch7-file-and-storage/actionkv/Cargo.lock [new file with mode: 0644]
ch7-file-and-storage/actionkv/Cargo.toml [new file with mode: 0644]
ch7-file-and-storage/actionkv/src/akv_mem.rs [new file with mode: 0644]
ch7-file-and-storage/actionkv/src/lib.rs [new file with mode: 0644]

diff --git a/ch7-file-and-storage/actionkv/Cargo.lock b/ch7-file-and-storage/actionkv/Cargo.lock
new file mode 100644 (file)
index 0000000..e9c7fc1
--- /dev/null
@@ -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 (file)
index 0000000..23e6703
--- /dev/null
@@ -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 (file)
index 0000000..87d3c0c
--- /dev/null
@@ -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<String> = 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 (file)
index 0000000..c626a82
--- /dev/null
@@ -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<u8>;
+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<ByteString, u64>,
+}
+
+impl ActionKV {
+    fn process_record<R: Read>(f: &mut R) -> io::Result<KeyValuePair> {
+        let saved_checksum = f.read_u32::<LittleEndian>()?;
+        let key_len = f.read_u32::<LittleEndian>()?;
+        let val_len = f.read_u32::<LittleEndian>()?;
+        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::<u32>::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<Self> {
+        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