--- /dev/null
+# 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"
--- /dev/null
+[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
--- /dev/null
+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),
+ }
+}
--- /dev/null
+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