Skip to content

Commit 2310a95

Browse files
committed
Auto merge of #525 - dzmitry-lahoda-forks:dz/1, r=Amanieu
feat: borsh serde
2 parents f540cb7 + 734191c commit 2310a95

File tree

4 files changed

+86
-0
lines changed

4 files changed

+86
-0
lines changed

Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ allocator-api2 = { version = "0.2.9", optional = true, default-features = false,
3636
# Equivalent trait which can be shared with other hash table implementations.
3737
equivalent = { version = "1.0", optional = true, default-features = false }
3838

39+
# borsh serde
40+
borsh = { version = "1.5.0", default-features = false, optional = true, features = ["derive"]}
41+
3942
[dev-dependencies]
4043
lazy_static = "1.4"
4144
rand = { version = "0.8.3", features = ["small_rng"] }
@@ -66,6 +69,8 @@ raw = []
6669
# time cost.
6770
inline-more = []
6871

72+
borsh = ["dep:borsh"]
73+
6974
[package.metadata.docs.rs]
7075
features = ["nightly", "rayon", "serde", "raw"]
7176
rustdoc-args = ["--generate-link-to-definition"]
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
use crate::HashMap;
2+
3+
use borsh::{
4+
io::{Read, Result, Write},
5+
BorshDeserialize, BorshSerialize,
6+
};
7+
8+
impl<K: BorshSerialize, V: BorshSerialize, S: BorshSerialize> BorshSerialize for HashMap<K, V, S> {
9+
fn serialize<W: Write>(&self, writer: &mut W) -> Result<()> {
10+
// assuming hash may have some seed,
11+
// as borsh is supposed by default to be deterministic, need to write it down
12+
// if allocator is compile time, than one can just impl wrapper with zero bytes serde of it
13+
self.hash_builder.serialize(writer)?;
14+
// considering A stateless
15+
self.len().serialize(writer)?;
16+
for kv in self.iter() {
17+
kv.serialize(writer)?;
18+
}
19+
Ok(())
20+
}
21+
}
22+
23+
impl<
24+
K: BorshDeserialize + core::hash::Hash + Eq,
25+
V: BorshDeserialize,
26+
S: BorshDeserialize + core::hash::BuildHasher,
27+
> BorshDeserialize for HashMap<K, V, S>
28+
{
29+
fn deserialize_reader<R: Read>(reader: &mut R) -> Result<Self> {
30+
let hash_builder = S::deserialize_reader(reader)?;
31+
let len = usize::deserialize_reader(reader)?;
32+
let mut map = HashMap::with_capacity_and_hasher(len, hash_builder);
33+
for _ in 0..len {
34+
let (k, v) = <(K, V)>::deserialize_reader(reader)?;
35+
// can use raw api here to init from memory, so can do it other time
36+
map.insert(k, v);
37+
}
38+
Ok(map)
39+
}
40+
}
41+
42+
#[cfg(test)]
43+
mod tests {
44+
use borsh::{BorshDeserialize, BorshSerialize};
45+
use std::vec::Vec;
46+
47+
#[derive(Default, BorshDeserialize, BorshSerialize, Clone)]
48+
struct NoHash;
49+
50+
impl core::hash::BuildHasher for NoHash {
51+
type Hasher = NoHash;
52+
fn build_hasher(&self) -> NoHash {
53+
Self
54+
}
55+
}
56+
57+
impl core::hash::Hasher for NoHash {
58+
fn finish(&self) -> u64 {
59+
42
60+
}
61+
62+
fn write(&mut self, _bytes: &[u8]) {}
63+
}
64+
65+
#[test]
66+
fn encdec() {
67+
let mut map = crate::HashMap::<_, _, NoHash>::default();
68+
map.insert(1, 2);
69+
map.insert(3, 4);
70+
let mut buf = Vec::new();
71+
map.serialize(&mut buf).unwrap();
72+
let original = map.clone();
73+
map = crate::HashMap::<_, _, NoHash>::deserialize_reader(&mut &buf[..]).unwrap();
74+
assert_eq!(original[&1], map[&1]);
75+
assert_eq!(original[&3], map[&3]);
76+
assert_eq!(original.len(), map.len());
77+
}
78+
}

src/external_trait_impls/borsh/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
mod hash_map;

src/external_trait_impls/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#[cfg(feature = "borsh")]
2+
mod borsh;
13
#[cfg(feature = "rayon")]
24
pub(crate) mod rayon;
35
#[cfg(feature = "rkyv")]

0 commit comments

Comments
 (0)