use crate::error::{ParcodeError, Result};
use crate::format::ChildRef;
use crate::graph::SerializationJob;
use crate::visitor::ParcodeVisitor;
use serde::Serialize;
use std::hash::{Hash, Hasher};
use twox_hash::XxHash64;
pub(crate) fn hash_key<K>(key: &K) -> u64
where
K: Hash,
{
let mut hasher = XxHash64::with_seed(0);
key.hash(&mut hasher);
hasher.finish()
}
#[derive(Debug)]
pub struct MapShardJob<'a, K, V> {
pub items: Vec<(&'a K, &'a V)>,
}
impl<'a, K, V> SerializationJob<'a> for MapShardJob<'a, K, V>
where
K: Serialize + Hash + Sync,
V: Serialize + Sync + ParcodeVisitor,
{
fn execute(&self, _: &[ChildRef]) -> Result<Vec<u8>> {
let count = self.items.len();
if count == 0 {
return Ok(Vec::new());
}
let mut data_blob = Vec::new();
let mut offsets = Vec::with_capacity(count * 4);
let mut hashes = Vec::with_capacity(count * 8);
let mut cursor = std::io::Cursor::new(&mut data_blob);
for (k, v) in &self.items {
hashes.extend_from_slice(&hash_key(k).to_le_bytes());
let pos = u32::try_from(cursor.position())
.map_err(|_| ParcodeError::Serialization("Data blob exceeds 4GB".into()))?;
offsets.extend_from_slice(&pos.to_le_bytes());
bincode::serde::encode_into_std_write(k, &mut cursor, bincode::config::standard())
.map_err(|e| ParcodeError::Serialization(e.to_string()))?;
v.serialize_shallow(&mut cursor)?;
}
let hashes_size = count * 8;
let offsets_size = count * 4;
let total_size = 8 + hashes_size + offsets_size + data_blob.len();
let mut final_buf = Vec::with_capacity(total_size);
final_buf.extend_from_slice(
&u32::try_from(count)
.map_err(|_| ParcodeError::Serialization("Shard count exceeds u32".into()))?
.to_le_bytes(),
);
final_buf.extend_from_slice(&[0u8; 4]);
final_buf.extend_from_slice(&hashes);
final_buf.extend_from_slice(&offsets);
final_buf.extend_from_slice(&data_blob);
Ok(final_buf)
}
fn estimated_size(&self) -> usize {
self.items.len() * (std::mem::size_of::<K>() + std::mem::size_of::<V>() + 12)
}
}