Skip to content

Commit

Permalink
feat: borsh serde
Browse files Browse the repository at this point in the history
  • Loading branch information
dzmitry-lahoda committed May 15, 2024
1 parent 8359365 commit 3c39e2d
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 0 deletions.
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ allocator-api2 = { version = "0.2.9", optional = true, default-features = false,
# Equivalent trait which can be shared with other hash table implementations.
equivalent = { version = "1.0", optional = true, default-features = false }

# borsh serde
borsh = { version = "1.5.0", default-features = false, optional = true, features = ["derive"]}

[dev-dependencies]
lazy_static = "1.4"
rand = { version = "0.8.3", features = ["small_rng"] }
Expand Down Expand Up @@ -66,6 +69,8 @@ raw = []
# time cost.
inline-more = []

borsh = ["dep:borsh"]

[package.metadata.docs.rs]
features = ["nightly", "rayon", "serde", "raw"]
rustdoc-args = ["--generate-link-to-definition"]
78 changes: 78 additions & 0 deletions src/external_trait_impls/borsh/hash_map.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
use crate::HashMap;

use borsh::{
io::{Read, Result, Write},
BorshDeserialize, BorshSerialize,
};

impl<K: BorshSerialize, V: BorshSerialize, S: BorshSerialize> BorshSerialize for HashMap<K, V, S> {
fn serialize<W: Write>(&self, writer: &mut W) -> Result<()> {
// assuming hash may have some seed,
// as borsh is supposed by default to be deterministic, need to write it down
// if allocator is compile time, than one can just impl wrapper with zero bytes serde of it
self.hash_builder.serialize(writer)?;
// considering A stateless
self.len().serialize(writer)?;
for kv in self.iter() {
kv.serialize(writer)?;
}
Ok(())
}
}

impl<
K: BorshDeserialize + core::hash::Hash + Eq,
V: BorshDeserialize,
S: BorshDeserialize + core::hash::BuildHasher,
> BorshDeserialize for HashMap<K, V, S>
{
fn deserialize_reader<R: Read>(reader: &mut R) -> Result<Self> {
let hash_builder = S::deserialize_reader(reader)?;
let len = usize::deserialize_reader(reader)?;
let mut map = HashMap::with_capacity_and_hasher(len, hash_builder);
for _ in 0..len {
let (k, v) = <(K, V)>::deserialize_reader(reader)?;
// can use raw api here to init from memory, so can do it other time
map.insert(k, v);
}
Ok(map)
}
}

#[cfg(test)]
mod tests {
use borsh::{BorshDeserialize, BorshSerialize};
use std::vec::Vec;

#[derive(Default, BorshDeserialize, BorshSerialize, Clone)]
struct NoHash;

impl core::hash::BuildHasher for NoHash {
type Hasher = NoHash;
fn build_hasher(&self) -> NoHash {
Self
}
}

impl core::hash::Hasher for NoHash {
fn finish(&self) -> u64 {
42
}

fn write(&mut self, _bytes: &[u8]) {}
}

#[test]
fn encdec() {
let mut map = crate::HashMap::<_, _, NoHash>::default();
map.insert(1, 2);
map.insert(3, 4);
let mut buf = Vec::new();
map.serialize(&mut buf).unwrap();
let original = map.clone();
map = crate::HashMap::<_, _, NoHash>::deserialize_reader(&mut &buf[..]).unwrap();
assert_eq!(original[&1], map[&1]);
assert_eq!(original[&3], map[&3]);
assert_eq!(original.len(), map.len());
}
}
1 change: 1 addition & 0 deletions src/external_trait_impls/borsh/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
mod hash_map;
2 changes: 2 additions & 0 deletions src/external_trait_impls/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ pub(crate) mod rayon;
mod rkyv;
#[cfg(feature = "serde")]
mod serde;
#[cfg(feature = "borsh")]
mod borsh;

0 comments on commit 3c39e2d

Please sign in to comment.