indexmap_store 0.1.0

Mutable, persistent key-value store backed by an IndexMap with an append-only log.
Documentation
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -18,6 +18,7 @@
 //!
 //! The store owns the file; concurrent writers are not supported.
 
+use bincode::config::{Configuration, Fixint, LittleEndian, NoLimit};
 use indexmap::IndexMap;
 use serde::{Serialize, de::DeserializeOwned};
 use std::fs::{self, File, OpenOptions};
@@ -25,6 +26,12 @@
 use std::io::{self, BufWriter, ErrorKind, Read, Write};
 use std::path::{Path, PathBuf};
 
+const BINCODE_CONFIG: Configuration<LittleEndian, Fixint, NoLimit> =
+    bincode::config::standard()
+        .with_fixed_int_encoding()
+        .with_little_endian()
+        .with_no_limit();
+
 const LEN_BYTES: usize = 4;
 const TAG_INSERT: u8 = 0;
 const TAG_REMOVE: u8 = 1;
@@ -51,7 +58,7 @@
             compact_ratio: 2.0,
             min_compact_bytes: 1 << 20,
             sync_on_write: false,
-            buf_capacity: 1024 * 1024,
+            buf_capacity: 2 * 1024 * 1024,
         }
     }
 }
@@ -124,15 +131,21 @@
                 let body = &buf[payload_start + 1..payload_end];
                 match tag {
                     TAG_INSERT => {
-                        let (k, v): (K, V) = match bincode::deserialize(body) {
-                            Ok(r) => r,
+                        let (k, v): (K, V) = match bincode::serde::decode_from_slice(
+                            body,
+                            BINCODE_CONFIG,
+                        ) {
+                            Ok((r, _len)) => r,
                             Err(_) => break,
                         };
                         map.insert(k, v);
                     }
                     TAG_REMOVE => {
-                        let k: K = match bincode::deserialize(body) {
-                            Ok(r) => r,
+                        let k: K = match bincode::serde::decode_from_slice(
+                            body,
+                            BINCODE_CONFIG,
+                        ) {
+                            Ok((r, _len)) => r,
                             Err(_) => break,
                         };
                         map.shift_remove(&k);
@@ -160,7 +173,11 @@
             live_records,
             total_records,
             cfg,
-            scratch: Vec::with_capacity(256),
+            scratch: {
+                let mut s = Vec::with_capacity(256);
+                s.extend_from_slice(&[0u8; LEN_BYTES]);
+                s
+            },
         })
     }
 
@@ -224,10 +241,9 @@
     /// [`IndexMap::insert`] semantics).
     #[inline]
     pub fn insert(&mut self, k: K, v: V) -> io::Result<Option<V>> {
-        self.scratch.clear();
-        self.scratch.extend_from_slice(&[0u8; LEN_BYTES]);
+        self.scratch.truncate(LEN_BYTES);
         self.scratch.push(TAG_INSERT);
-        bincode::serialize_into(&mut self.scratch, &(&k, &v))
+        bincode::serde::encode_into_std_write(&(&k, &v), &mut self.scratch, BINCODE_CONFIG)
             .map_err(serialize_err)?;
         self.flush_scratch()?;
         let prev = self.map.insert(k, v);
@@ -246,10 +262,9 @@
         if !self.map.contains_key(k) {
             return Ok(None);
         }
-        self.scratch.clear();
-        self.scratch.extend_from_slice(&[0u8; LEN_BYTES]);
+        self.scratch.truncate(LEN_BYTES);
         self.scratch.push(TAG_REMOVE);
-        bincode::serialize_into(&mut self.scratch, k)
+        bincode::serde::encode_into_std_write(k, &mut self.scratch, BINCODE_CONFIG)
             .map_err(serialize_err)?;
         self.flush_scratch()?;
         let prev = self.map.shift_remove(k);
@@ -276,10 +291,9 @@
         let result = f(v_mut);
         let v_ref: &V = v_mut;
 
-        self.scratch.clear();
-        self.scratch.extend_from_slice(&[0u8; LEN_BYTES]);
+        self.scratch.truncate(LEN_BYTES);
         self.scratch.push(TAG_INSERT);
-        bincode::serialize_into(&mut self.scratch, &(k, v_ref))
+        bincode::serde::encode_into_std_write(&(k, v_ref), &mut self.scratch, BINCODE_CONFIG)
             .map_err(serialize_err)?;
 
         self.flush_scratch()?;
@@ -307,13 +321,15 @@
                 .write(true)
                 .open(&tmp_path)?;
             let mut writer = BufWriter::with_capacity(self.cfg.buf_capacity, tmp);
-            let mut buf = Vec::with_capacity(256);
+            let mut buf: Vec<u8> = Vec::with_capacity(256);
             for (k, v) in &self.map {
                 buf.clear();
+                buf.extend_from_slice(&[0u8; LEN_BYTES]);
                 buf.push(TAG_INSERT);
-                bincode::serialize_into(&mut buf, &(k, v))
+                bincode::serde::encode_into_std_write(&(k, v), &mut buf, BINCODE_CONFIG)
                     .map_err(serialize_err)?;
-                writer.write_all(&(buf.len() as u32).to_le_bytes())?;
+                let payload_len = (buf.len() - LEN_BYTES) as u32;
+                buf[..LEN_BYTES].copy_from_slice(&payload_len.to_le_bytes());
                 writer.write_all(&buf)?;
             }
             writer.flush()?;
@@ -333,7 +349,7 @@
         Ok(())
     }
 
-    #[inline]
+    #[inline(always)]
     fn flush_scratch(&mut self) -> io::Result<()> {
         // Callers reserve LEN_BYTES at the front of `scratch`; fill the length
         // in place so the length-prefix and payload land in a single write.
@@ -371,6 +387,6 @@
 }
 
 #[inline]
-fn serialize_err(e: bincode::Error) -> io::Error {
+fn serialize_err(e: bincode::error::EncodeError) -> io::Error {
     io::Error::new(ErrorKind::InvalidData, e)
 }
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -8,7 +8,7 @@
 [dependencies]
 indexmap = { version = "2", features = ["serde"] }
 serde = { version = "1", features = ["derive"] }
-bincode = "1.3"
+bincode = { version = "2.0.1", features = ["serde"] }
 
 [dev-dependencies]
 tempfile = "3"