use core::any::{Any, TypeId};
use core::hash::{BuildHasher, Hash};
use hashbrown::HashMap;
use smallbox::SmallBox;
use smallbox::space::S8;
pub type DefaultDedupeHasher = ahash::RandomState;
#[cfg(not(feature = "std"))]
use alloc::{boxed::Box, sync::Arc};
#[cfg(feature = "std")]
use std::{boxed::Box, sync::Arc};
use crate::prelude::*;
const DEFAULT_INITIAL_CAPACITY: usize = 128;
const DEFAULT_NUM_TYPES: usize = 4;
pub struct FrozenEncoderState {
type_stores: Vec<(TypeId, SmallBox<dyn Any + Send + Sync, S8>)>,
total_primed: usize,
}
pub struct FrozenDecoderState {
typed_vec: Option<(TypeId, Box<dyn Any + Send + Sync>)>,
boxed_values: Vec<Box<dyn Any + Send + Sync>>,
total_primed: usize,
}
impl FrozenEncoderState {
#[inline(always)]
pub const fn len(&self) -> usize {
self.total_primed
}
#[inline(always)]
pub const fn is_empty(&self) -> bool {
self.total_primed == 0
}
}
impl FrozenDecoderState {
#[inline(always)]
pub const fn len(&self) -> usize {
self.total_primed
}
#[inline(always)]
pub const fn is_empty(&self) -> bool {
self.total_primed == 0
}
}
pub trait DedupeEncodeable: Hash + Eq + Pack + Clone + Send + Sync + 'static {
type Hasher: BuildHasher + Default + Send + Sync + 'static;
}
impl<T: DedupeEncodeable> Encode for T {
#[inline(always)]
fn encode_ext(
&self,
writer: &mut impl Write,
ctx: Option<&mut crate::context::EncoderContext>,
) -> Result<usize> {
if let Some(ctx) = ctx
&& let Some(encoder) = ctx.dedupe.as_mut()
{
return encoder.encode::<T, T::Hasher>(self, writer);
}
self.pack(writer)
}
#[inline(always)]
fn encode_slice(items: &[Self], writer: &mut impl Write) -> Result<usize> {
T::pack_slice(items, writer)
}
}
pub trait DedupeDecodeable: Pack + Clone + Hash + Eq + Send + Sync + 'static {
type Hasher: BuildHasher + Default + Send + Sync + 'static;
}
impl<T: DedupeDecodeable> Decode for T {
#[inline(always)]
fn decode_ext(
reader: &mut impl Read,
ctx: Option<&mut crate::context::DecoderContext>,
) -> Result<Self> {
if let Some(ctx) = ctx
&& let Some(decoder) = ctx.dedupe.as_mut()
{
return decoder.decode::<T>(reader);
}
T::unpack(reader)
}
#[inline(always)]
fn decode_vec(reader: &mut impl Read, count: usize) -> Result<Vec<Self>> {
T::unpack_vec(reader, count)
}
}
pub struct DedupeEncoder {
frozen: Option<Arc<FrozenEncoderState>>,
frozen_total_primed: usize,
type_stores: Vec<(TypeId, SmallBox<dyn Any + Send + Sync, S8>)>,
next_id: usize,
initial_capacity: usize,
}
impl Default for DedupeEncoder {
#[inline(always)]
fn default() -> Self {
Self::new()
}
}
impl DedupeEncoder {
#[inline(always)]
pub fn new() -> Self {
Self {
frozen: None,
frozen_total_primed: 0,
type_stores: Vec::with_capacity(DEFAULT_NUM_TYPES),
next_id: 1, initial_capacity: DEFAULT_INITIAL_CAPACITY,
}
}
#[inline(always)]
pub fn with_capacity(initial_capacity: usize, num_types: usize) -> Self {
Self {
frozen: None,
frozen_total_primed: 0,
type_stores: Vec::with_capacity(num_types),
next_id: 1,
initial_capacity,
}
}
#[inline(always)]
pub fn with_frozen(frozen: Arc<FrozenEncoderState>) -> Self {
let total_primed = frozen.total_primed;
Self {
frozen: Some(frozen),
frozen_total_primed: total_primed,
type_stores: Vec::with_capacity(DEFAULT_NUM_TYPES),
next_id: total_primed + 1,
initial_capacity: DEFAULT_INITIAL_CAPACITY,
}
}
pub fn freeze(self) -> FrozenEncoderState {
assert!(
self.frozen.is_none(),
"cannot freeze an encoder that already has a frozen state"
);
FrozenEncoderState {
type_stores: self.type_stores,
total_primed: self.next_id - 1,
}
}
#[inline(always)]
pub fn clear(&mut self) {
self.type_stores.clear();
self.next_id = self.frozen_total_primed + 1;
}
#[inline(always)]
pub const fn len(&self) -> usize {
self.next_id - 1
}
#[inline(always)]
pub const fn is_empty(&self) -> bool {
self.next_id == 1
}
#[inline(always)]
pub fn num_types(&self) -> usize {
self.type_stores.len()
}
#[inline(always)]
pub fn type_ids(&self) -> impl Iterator<Item = TypeId> + '_ {
self.type_stores.iter().map(|(id, _)| *id)
}
#[inline]
pub fn contains_type<T: 'static>(&self) -> bool {
let type_id = TypeId::of::<T>();
self.type_stores.iter().any(|(id, _)| *id == type_id)
}
#[inline]
pub fn len_for_type<T, S>(&self) -> usize
where
T: Hash + Eq + Send + Sync + 'static,
S: BuildHasher + Send + Sync + 'static,
{
let type_id = TypeId::of::<T>();
self.type_stores
.iter()
.find(|(id, _)| *id == type_id)
.and_then(|(_, store)| store.downcast_ref::<HashMap<T, usize, S>>())
.map_or(0, |m| m.len())
}
#[inline]
pub fn values_for_type<T, S>(&self) -> impl Iterator<Item = &T>
where
T: Hash + Eq + Send + Sync + 'static,
S: BuildHasher + Send + Sync + 'static,
{
let type_id = TypeId::of::<T>();
self.type_stores
.iter()
.find(|(id, _)| *id == type_id)
.and_then(|(_, store)| store.downcast_ref::<HashMap<T, usize, S>>())
.into_iter()
.flat_map(|m| m.keys())
}
#[inline]
pub fn clear_type<T: Hash + Eq + Send + Sync + 'static>(&mut self) {
let type_id = TypeId::of::<T>();
if let Some(pos) = self.type_stores.iter().position(|(id, _)| *id == type_id) {
self.type_stores.swap_remove(pos);
}
}
#[inline]
pub fn memory_usage(&self) -> usize {
use core::mem::size_of;
let mut total = self.type_stores.capacity()
* (size_of::<TypeId>() + size_of::<SmallBox<dyn Any + Send + Sync, S8>>());
let entry_count = self.len();
total += entry_count * size_of::<usize>() * 3;
total
}
#[inline]
pub fn encode<T, S>(&mut self, val: &T, writer: &mut impl Write) -> Result<usize>
where
T: Hash + Eq + Pack + Clone + Send + Sync + 'static,
S: BuildHasher + Default + Send + Sync + 'static,
{
let type_id = TypeId::of::<T>();
if let Some(frozen) = &self.frozen
&& let Some((_, store)) = frozen.type_stores.iter().find(|(id, _)| *id == type_id)
{
let typed_store: &HashMap<T, usize, S> = unsafe {
&*(&**store as *const (dyn Any + Send + Sync) as *const HashMap<T, usize, S>)
};
if let Some(&existing_id) = typed_store.get(val) {
return Lencode::encode_varint(existing_id, writer);
}
}
let store = match self.type_stores.iter_mut().find(|(id, _)| *id == type_id) {
Some((_, store)) => store,
None => {
self.type_stores.push((
type_id,
smallbox::smallbox!(HashMap::<T, usize, S>::with_capacity_and_hasher(
self.initial_capacity,
S::default(),
)),
));
&mut self.type_stores.last_mut().unwrap().1
}
};
let typed_store: &mut HashMap<T, usize, S> = unsafe {
&mut *(&mut **store as *mut (dyn Any + Send + Sync) as *mut HashMap<T, usize, S>)
};
if let Some(&existing_id) = typed_store.get(val) {
return Lencode::encode_varint(existing_id, writer);
}
let new_id = self.next_id;
self.next_id += 1;
typed_store.insert(val.clone(), new_id);
let mut total_bytes = 0;
total_bytes += Lencode::encode_varint(0usize, writer)?; total_bytes += val.pack(writer)?;
Ok(total_bytes)
}
#[inline]
pub fn prime<T, S>(&mut self, val: &T) -> usize
where
T: Hash + Eq + Pack + Clone + Send + Sync + 'static,
S: BuildHasher + Default + Send + Sync + 'static,
{
assert!(
self.frozen.is_none(),
"cannot prime an encoder that already has a frozen state"
);
let type_id = TypeId::of::<T>();
let store = match self.type_stores.iter_mut().find(|(id, _)| *id == type_id) {
Some((_, store)) => store,
None => {
self.type_stores.push((
type_id,
smallbox::smallbox!(HashMap::<T, usize, S>::with_capacity_and_hasher(
self.initial_capacity,
S::default(),
)),
));
&mut self.type_stores.last_mut().unwrap().1
}
};
let typed_store: &mut HashMap<T, usize, S> = unsafe {
&mut *(&mut **store as *mut (dyn Any + Send + Sync) as *mut HashMap<T, usize, S>)
};
if let Some(&existing_id) = typed_store.get(val) {
return existing_id;
}
let new_id = self.next_id;
self.next_id += 1;
typed_store.insert(val.clone(), new_id);
new_id
}
}
pub struct DedupeDecoder {
frozen: Option<Arc<FrozenDecoderState>>,
frozen_total_primed: usize,
typed_vec: Option<(TypeId, Box<dyn Any + Send + Sync>)>,
boxed_values: Vec<Box<dyn Any + Send + Sync>>,
scratch_count: usize,
}
impl Default for DedupeDecoder {
fn default() -> Self {
Self::new()
}
}
impl DedupeDecoder {
#[inline(always)]
pub fn new() -> Self {
Self {
frozen: None,
frozen_total_primed: 0,
typed_vec: None,
boxed_values: Vec::new(),
scratch_count: 0,
}
}
#[inline(always)]
pub fn with_capacity(capacity: usize) -> Self {
let _ = capacity;
Self {
frozen: None,
frozen_total_primed: 0,
typed_vec: None,
boxed_values: Vec::new(),
scratch_count: 0,
}
}
#[inline(always)]
pub fn with_frozen(frozen: Arc<FrozenDecoderState>) -> Self {
let frozen_total_primed = frozen.total_primed;
Self {
frozen: Some(frozen),
frozen_total_primed,
typed_vec: None,
boxed_values: Vec::new(),
scratch_count: 0,
}
}
pub fn freeze(self) -> FrozenDecoderState {
assert!(
self.frozen.is_none(),
"cannot freeze a decoder that already has a frozen state"
);
FrozenDecoderState {
typed_vec: self.typed_vec,
boxed_values: self.boxed_values,
total_primed: self.scratch_count,
}
}
#[inline(always)]
pub fn clear(&mut self) {
self.typed_vec = None;
self.boxed_values.clear();
self.scratch_count = 0;
}
#[inline(always)]
pub const fn len(&self) -> usize {
self.frozen_total_primed + self.scratch_count
}
#[inline(always)]
pub const fn is_empty(&self) -> bool {
self.len() == 0
}
#[inline]
pub fn memory_usage(&self) -> usize {
use core::mem::size_of;
self.boxed_values.capacity() * size_of::<Box<dyn Any + Send + Sync>>()
+ self
.typed_vec
.as_ref()
.map_or(0, |_| self.scratch_count * 64)
}
#[inline]
pub fn decode<T: Pack + Clone + Hash + Eq + Send + Sync + 'static>(
&mut self,
reader: &mut impl Read,
) -> Result<T> {
let id = Lencode::decode_varint::<usize>(reader)?;
let type_id = TypeId::of::<T>();
let total_primed = self.frozen_total_primed;
if id != 0 && id <= total_primed {
let frozen = self.frozen.as_ref().unwrap();
return lookup_frozen::<T>(frozen, type_id, id - 1);
}
let scratch_id_base = total_primed;
if let Some((ref cached_type, ref mut store)) = self.typed_vec
&& *cached_type == type_id
{
let vec: &mut Vec<T> =
unsafe { &mut *(store.as_mut() as *mut (dyn Any + Send + Sync) as *mut Vec<T>) };
if id == 0 {
let value = T::unpack(reader)?;
vec.push(value.clone());
self.scratch_count += 1;
return Ok(value);
} else {
let index = id - scratch_id_base - 1;
if let Some(v) = vec.get(index) {
return Ok(v.clone());
}
return Err(crate::io::Error::InvalidData);
}
}
if self.typed_vec.is_none() && self.boxed_values.is_empty() {
let mut vec: Vec<T> = Vec::with_capacity(DEFAULT_INITIAL_CAPACITY);
if id == 0 {
let value = T::unpack(reader)?;
vec.push(value.clone());
self.typed_vec = Some((type_id, Box::new(vec)));
self.scratch_count += 1;
return Ok(value);
} else {
return Err(crate::io::Error::InvalidData);
}
}
if id == 0 {
let value = T::unpack(reader)?;
self.boxed_values.push(Box::new(value.clone()));
self.scratch_count += 1;
Ok(value)
} else {
let index = id - scratch_id_base - 1;
if let Some(boxed_value) = self.boxed_values.get(index) {
let typed_value: &T =
unsafe { &*(&**boxed_value as *const (dyn Any + Send + Sync) as *const T) };
return Ok(typed_value.clone());
}
Err(crate::io::Error::InvalidData)
}
}
#[inline]
pub fn prime<T: Pack + Clone + Hash + Eq + Send + Sync + 'static>(&mut self, val: T) {
assert!(
self.frozen.is_none(),
"cannot prime a decoder that already has a frozen state"
);
let type_id = TypeId::of::<T>();
if let Some((ref cached_type, ref mut store)) = self.typed_vec
&& *cached_type == type_id
{
let vec: &mut Vec<T> =
unsafe { &mut *(store.as_mut() as *mut (dyn Any + Send + Sync) as *mut Vec<T>) };
vec.push(val);
self.scratch_count += 1;
return;
}
if self.typed_vec.is_none() && self.boxed_values.is_empty() {
let mut vec: Vec<T> = Vec::with_capacity(DEFAULT_INITIAL_CAPACITY);
vec.push(val);
self.typed_vec = Some((type_id, Box::new(vec)));
self.scratch_count += 1;
return;
}
self.boxed_values.push(Box::new(val));
self.scratch_count += 1;
}
}
#[inline]
fn lookup_frozen<T: Clone + 'static>(
frozen: &FrozenDecoderState,
type_id: TypeId,
vec_index: usize,
) -> Result<T> {
if let Some((ref cached_type, ref store)) = frozen.typed_vec
&& *cached_type == type_id
{
let vec: &Vec<T> =
unsafe { &*(&**store as *const (dyn Any + Send + Sync) as *const Vec<T>) };
if let Some(v) = vec.get(vec_index) {
return Ok(v.clone());
}
return Err(crate::io::Error::InvalidData);
}
if let Some(boxed_value) = frozen.boxed_values.get(vec_index) {
let typed_value: &T =
unsafe { &*(&**boxed_value as *const (dyn Any + Send + Sync) as *const T) };
return Ok(typed_value.clone());
}
Err(crate::io::Error::InvalidData)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::io::Cursor;
type H = DefaultDedupeHasher;
#[test]
fn test_dedupe_encode_decode_roundtrip() {
let mut encoder = DedupeEncoder::new();
let mut decoder = DedupeDecoder::new();
let mut buffer = Vec::new();
let values = [42u32, 123u32, 42u32, 456u32, 123u32, 789u32, 42u32];
for &value in &values {
encoder.encode::<u32, H>(&value, &mut buffer).unwrap();
}
let mut cursor = Cursor::new(&buffer);
let mut decoded_values = Vec::new();
for _ in &values {
let decoded: u32 = decoder.decode(&mut cursor).unwrap();
decoded_values.push(decoded);
}
assert_eq!(values.to_vec(), decoded_values);
}
#[test]
fn test_dedupe_clear() {
let mut encoder = DedupeEncoder::new();
let mut decoder = DedupeDecoder::new();
let mut buffer = Vec::new();
encoder.encode::<u32, H>(&42u32, &mut buffer).unwrap();
encoder.encode::<u32, H>(&123u32, &mut buffer).unwrap();
encoder.clear();
decoder.clear();
buffer.clear();
encoder.encode::<u32, H>(&42u32, &mut buffer).unwrap(); encoder.encode::<u32, H>(&42u32, &mut buffer).unwrap();
let mut cursor = Cursor::new(&buffer);
let decoded1: u32 = decoder.decode(&mut cursor).unwrap();
let decoded2: u32 = decoder.decode(&mut cursor).unwrap();
assert_eq!(decoded1, 42u32);
assert_eq!(decoded2, 42u32);
}
#[test]
fn test_dedupe_len_for_type() {
let mut encoder = DedupeEncoder::new();
let mut buffer = Vec::new();
assert_eq!(encoder.len_for_type::<u32, H>(), 0);
assert_eq!(encoder.num_types(), 0);
encoder.encode::<u32, H>(&42u32, &mut buffer).unwrap();
encoder.encode::<u32, H>(&42u32, &mut buffer).unwrap(); encoder.encode::<u32, H>(&99u32, &mut buffer).unwrap();
encoder.encode::<u64, H>(&7u64, &mut buffer).unwrap();
assert_eq!(encoder.len_for_type::<u32, H>(), 2);
assert_eq!(encoder.len_for_type::<u64, H>(), 1);
assert_eq!(encoder.len_for_type::<u16, H>(), 0);
assert_eq!(encoder.num_types(), 2);
assert_eq!(encoder.len(), 3);
}
#[test]
fn test_dedupe_clear_type() {
let mut encoder = DedupeEncoder::new();
let mut buffer = Vec::new();
encoder.encode::<u32, H>(&42u32, &mut buffer).unwrap();
encoder.encode::<u64, H>(&7u64, &mut buffer).unwrap();
assert_eq!(encoder.num_types(), 2);
encoder.clear_type::<u32>();
assert_eq!(encoder.len_for_type::<u32, H>(), 0);
assert_eq!(encoder.len_for_type::<u64, H>(), 1);
assert_eq!(encoder.num_types(), 1);
}
#[test]
fn test_dedupe_memory_usage() {
let mut encoder = DedupeEncoder::new();
let mut buffer = Vec::new();
let initial = encoder.memory_usage();
encoder.encode::<u32, H>(&42u32, &mut buffer).unwrap();
encoder.encode::<u32, H>(&99u32, &mut buffer).unwrap();
let after = encoder.memory_usage();
assert!(
after > initial,
"memory usage should increase after storing entries"
);
}
#[test]
fn test_dedupe_decoder_memory_usage() {
let decoder = DedupeDecoder::new();
let _usage = decoder.memory_usage();
}
#[test]
fn test_dedupe_invalid_id() {
let mut decoder = DedupeDecoder::new();
let mut buffer = Vec::new();
Lencode::encode_varint(5usize, &mut buffer).unwrap();
let mut cursor = Cursor::new(&buffer);
let result: Result<u32> = decoder.decode(&mut cursor);
assert!(result.is_err());
matches!(result, Err(crate::io::Error::InvalidData));
}
#[test]
fn test_prime_assigns_sequential_ids() {
let mut encoder = DedupeEncoder::new();
assert_eq!(encoder.prime::<u32, H>(&10), 1);
assert_eq!(encoder.prime::<u32, H>(&20), 2);
assert_eq!(encoder.prime::<u32, H>(&30), 3);
assert_eq!(encoder.len(), 3);
}
#[test]
fn test_prime_is_idempotent() {
let mut encoder = DedupeEncoder::new();
let id1 = encoder.prime::<u32, H>(&42);
let id2 = encoder.prime::<u32, H>(&42);
assert_eq!(id1, id2);
assert_eq!(encoder.len(), 1);
}
#[test]
fn test_prime_writes_nothing() {
let mut encoder = DedupeEncoder::new();
encoder.prime::<u32, H>(&42);
encoder.prime::<u32, H>(&99);
let mut buffer = Vec::new();
encoder.encode::<u32, H>(&42, &mut buffer).unwrap();
assert_eq!(buffer, vec![1]);
buffer.clear();
encoder.encode::<u32, H>(&99, &mut buffer).unwrap();
assert_eq!(buffer, vec![2]);
}
#[test]
fn test_prime_roundtrip_with_encode_decode() {
let mut encoder = DedupeEncoder::new();
let mut decoder = DedupeDecoder::new();
let mut buffer = Vec::new();
let primed = [100u32, 200, 300, 400];
for v in &primed {
encoder.prime::<u32, H>(v);
decoder.prime::<u32>(*v);
}
let stream = [200u32, 500, 100, 500, 400, 300, 600];
for v in &stream {
encoder.encode::<u32, H>(v, &mut buffer).unwrap();
}
let mut cursor = Cursor::new(&buffer);
for &expected in &stream {
let decoded: u32 = decoder.decode(&mut cursor).unwrap();
assert_eq!(decoded, expected);
}
}
#[test]
fn test_prime_mixed_with_encode() {
let mut encoder = DedupeEncoder::new();
encoder.prime::<u32, H>(&7);
let mut buffer = Vec::new();
let bytes = encoder.encode::<u32, H>(&7, &mut buffer).unwrap();
assert_eq!(bytes, 1);
assert_eq!(buffer, vec![1]);
}
#[test]
fn test_frozen_state_basic_roundtrip() {
let mut primer_enc = DedupeEncoder::new();
let mut primer_dec = DedupeDecoder::new();
let primed = [100u32, 200, 300, 400];
for v in &primed {
primer_enc.prime::<u32, H>(v);
primer_dec.prime::<u32>(*v);
}
let frozen_enc = Arc::new(primer_enc.freeze());
let frozen_dec = Arc::new(primer_dec.freeze());
assert_eq!(frozen_enc.len(), 4);
assert_eq!(frozen_dec.len(), 4);
let mut enc = DedupeEncoder::with_frozen(Arc::clone(&frozen_enc));
let mut dec = DedupeDecoder::with_frozen(Arc::clone(&frozen_dec));
let stream = [200u32, 500, 100, 500, 400, 300, 600];
let mut buffer = Vec::new();
for v in &stream {
enc.encode::<u32, H>(v, &mut buffer).unwrap();
}
let mut cursor = Cursor::new(&buffer);
for &expected in &stream {
let decoded: u32 = dec.decode(&mut cursor).unwrap();
assert_eq!(decoded, expected);
}
}
#[test]
fn test_frozen_clear_preserves_frozen() {
let mut primer = DedupeEncoder::new();
primer.prime::<u32, H>(&10);
primer.prime::<u32, H>(&20);
let frozen = Arc::new(primer.freeze());
let mut enc = DedupeEncoder::with_frozen(Arc::clone(&frozen));
let mut buffer = Vec::new();
enc.encode::<u32, H>(&99, &mut buffer).unwrap();
assert_eq!(enc.len(), 3);
enc.clear();
assert_eq!(enc.len(), 2); buffer.clear();
let bytes = enc.encode::<u32, H>(&10, &mut buffer).unwrap();
assert_eq!(bytes, 1);
assert_eq!(buffer, vec![1]);
buffer.clear();
enc.encode::<u32, H>(&77, &mut buffer).unwrap();
assert_eq!(buffer[0], 0);
}
#[test]
fn test_frozen_shared_across_encoders() {
let mut primer = DedupeEncoder::new();
primer.prime::<u32, H>(&1);
primer.prime::<u32, H>(&2);
primer.prime::<u32, H>(&3);
let frozen = Arc::new(primer.freeze());
let mut enc_a = DedupeEncoder::with_frozen(Arc::clone(&frozen));
let mut enc_b = DedupeEncoder::with_frozen(Arc::clone(&frozen));
let mut buf_a = Vec::new();
let mut buf_b = Vec::new();
enc_a.encode::<u32, H>(&1, &mut buf_a).unwrap(); enc_b.encode::<u32, H>(&3, &mut buf_b).unwrap(); assert_eq!(buf_a, vec![1]);
assert_eq!(buf_b, vec![3]);
buf_a.clear();
buf_b.clear();
enc_a.encode::<u32, H>(&100, &mut buf_a).unwrap(); enc_b.encode::<u32, H>(&200, &mut buf_b).unwrap();
assert_eq!(enc_a.len(), 4);
assert_eq!(enc_b.len(), 4);
}
#[test]
#[should_panic(expected = "cannot freeze an encoder that already has a frozen state")]
fn test_frozen_refreeze_panics() {
let mut primer = DedupeEncoder::new();
primer.prime::<u32, H>(&1);
let frozen = Arc::new(primer.freeze());
let enc = DedupeEncoder::with_frozen(frozen);
let _ = enc.freeze(); }
#[test]
#[should_panic(expected = "cannot prime an encoder that already has a frozen state")]
fn test_frozen_encoder_prime_panics() {
let mut primer = DedupeEncoder::new();
primer.prime::<u32, H>(&1);
let frozen = Arc::new(primer.freeze());
let mut enc = DedupeEncoder::with_frozen(frozen);
enc.prime::<u32, H>(&99); }
#[test]
#[should_panic(expected = "cannot prime a decoder that already has a frozen state")]
fn test_frozen_decoder_prime_panics() {
let mut primer = DedupeDecoder::new();
primer.prime::<u32>(1);
let frozen = Arc::new(primer.freeze());
let mut dec = DedupeDecoder::with_frozen(frozen);
dec.prime::<u32>(99); }
#[test]
fn test_frozen_novel_to_scratch_then_reference() {
let mut primer_enc = DedupeEncoder::new();
let mut primer_dec = DedupeDecoder::new();
primer_enc.prime::<u32, H>(&10);
primer_dec.prime::<u32>(10);
let frozen_enc = Arc::new(primer_enc.freeze());
let frozen_dec = Arc::new(primer_dec.freeze());
let mut enc = DedupeEncoder::with_frozen(frozen_enc);
let mut dec = DedupeDecoder::with_frozen(frozen_dec);
let mut buffer = Vec::new();
enc.encode::<u32, H>(&50, &mut buffer).unwrap();
enc.encode::<u32, H>(&10, &mut buffer).unwrap();
enc.encode::<u32, H>(&50, &mut buffer).unwrap();
let mut cursor = Cursor::new(&buffer);
assert_eq!(dec.decode::<u32>(&mut cursor).unwrap(), 50);
assert_eq!(dec.decode::<u32>(&mut cursor).unwrap(), 10);
assert_eq!(dec.decode::<u32>(&mut cursor).unwrap(), 50);
}
#[test]
fn test_frozen_empty_encoder() {
let enc = DedupeEncoder::new();
let frozen = Arc::new(enc.freeze());
assert_eq!(frozen.len(), 0);
assert!(frozen.is_empty());
let mut worker = DedupeEncoder::with_frozen(frozen);
assert_eq!(worker.len(), 0);
let mut buffer = Vec::new();
worker.encode::<u32, H>(&42, &mut buffer).unwrap();
assert_eq!(worker.len(), 1);
}
#[test]
fn test_frozen_multi_type_encoder() {
let mut primer = DedupeEncoder::new();
primer.prime::<u32, H>(&1);
primer.prime::<u64, H>(&7);
primer.prime::<u32, H>(&2);
let frozen = Arc::new(primer.freeze());
assert_eq!(frozen.len(), 3);
let mut enc = DedupeEncoder::with_frozen(frozen);
let mut buffer = Vec::new();
enc.encode::<u32, H>(&1, &mut buffer).unwrap();
assert_eq!(buffer, vec![1]);
buffer.clear();
enc.encode::<u64, H>(&7, &mut buffer).unwrap();
assert_eq!(buffer, vec![2]);
buffer.clear();
enc.encode::<u32, H>(&2, &mut buffer).unwrap();
assert_eq!(buffer, vec![3]);
}
#[test]
fn test_frozen_encoder_memory_usage_stable_after_clear() {
let mut primer = DedupeEncoder::new();
for i in 0..100u32 {
primer.prime::<u32, H>(&i);
}
let frozen = Arc::new(primer.freeze());
let mut enc = DedupeEncoder::with_frozen(frozen);
let mut buffer = Vec::new();
for i in 1000..1100u32 {
enc.encode::<u32, H>(&i, &mut buffer).unwrap();
}
let post_encode = enc.len();
assert_eq!(post_encode, 200);
enc.clear();
assert_eq!(enc.len(), 100);
}
const _: fn() = || {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<FrozenEncoderState>();
assert_send_sync::<FrozenDecoderState>();
};
#[cfg(feature = "std")]
#[test]
fn test_frozen_shared_across_threads() {
use std::thread;
let mut primer_enc = DedupeEncoder::new();
let mut primer_dec = DedupeDecoder::new();
for i in 0..50u32 {
primer_enc.prime::<u32, H>(&i);
primer_dec.prime::<u32>(i);
}
let frozen_enc = Arc::new(primer_enc.freeze());
let frozen_dec = Arc::new(primer_dec.freeze());
let handles: Vec<_> = (0..4)
.map(|worker_id| {
let frozen_enc = Arc::clone(&frozen_enc);
let frozen_dec = Arc::clone(&frozen_dec);
thread::spawn(move || {
let mut enc = DedupeEncoder::with_frozen(frozen_enc);
let mut dec = DedupeDecoder::with_frozen(frozen_dec);
let mut buffer = Vec::new();
let stream: Vec<u32> = (0..25u32)
.chain(core::iter::once(1000 + worker_id))
.collect();
for v in &stream {
enc.encode::<u32, H>(v, &mut buffer).unwrap();
}
let mut cursor = Cursor::new(&buffer);
for &expected in &stream {
let decoded: u32 = dec.decode(&mut cursor).unwrap();
assert_eq!(decoded, expected, "worker {worker_id}");
}
})
})
.collect();
for h in handles {
h.join().unwrap();
}
}
#[test]
fn test_clear_preserves_scratch_across_multiple_cycles() {
let mut primer_enc = DedupeEncoder::new();
let mut primer_dec = DedupeDecoder::new();
primer_enc.prime::<u32, H>(&10);
primer_enc.prime::<u32, H>(&20);
primer_dec.prime::<u32>(10);
primer_dec.prime::<u32>(20);
let frozen_enc = Arc::new(primer_enc.freeze());
let frozen_dec = Arc::new(primer_dec.freeze());
let mut enc = DedupeEncoder::with_frozen(frozen_enc);
let mut dec = DedupeDecoder::with_frozen(frozen_dec);
for cycle in 0..5u32 {
enc.clear();
dec.clear();
let novel_a = 100 + cycle;
let novel_b = 200 + cycle;
let mut buffer = Vec::new();
enc.encode::<u32, H>(&10, &mut buffer).unwrap(); enc.encode::<u32, H>(&novel_a, &mut buffer).unwrap(); enc.encode::<u32, H>(&20, &mut buffer).unwrap(); enc.encode::<u32, H>(&novel_b, &mut buffer).unwrap(); enc.encode::<u32, H>(&novel_a, &mut buffer).unwrap();
assert_eq!(enc.len(), 4);
let mut cursor = Cursor::new(&buffer);
assert_eq!(dec.decode::<u32>(&mut cursor).unwrap(), 10);
assert_eq!(dec.decode::<u32>(&mut cursor).unwrap(), novel_a);
assert_eq!(dec.decode::<u32>(&mut cursor).unwrap(), 20);
assert_eq!(dec.decode::<u32>(&mut cursor).unwrap(), novel_b);
assert_eq!(dec.decode::<u32>(&mut cursor).unwrap(), novel_a);
}
}
#[test]
fn test_frozen_large_primed_set() {
let mut primer_enc = DedupeEncoder::new();
let mut primer_dec = DedupeDecoder::new();
for i in 0..1000u32 {
primer_enc.prime::<u32, H>(&i);
primer_dec.prime::<u32>(i);
}
let frozen_enc = Arc::new(primer_enc.freeze());
let frozen_dec = Arc::new(primer_dec.freeze());
assert_eq!(frozen_enc.len(), 1000);
let mut enc = DedupeEncoder::with_frozen(frozen_enc);
let mut dec = DedupeDecoder::with_frozen(frozen_dec);
let mut buffer = Vec::new();
let stream: Vec<u32> = (0..500).chain(10_000..10_500).collect();
for v in &stream {
enc.encode::<u32, H>(v, &mut buffer).unwrap();
}
let mut cursor = Cursor::new(&buffer);
for &expected in &stream {
let decoded: u32 = dec.decode(&mut cursor).unwrap();
assert_eq!(decoded, expected);
}
}
#[test]
fn test_prime_survives_novel_values() {
let mut encoder = DedupeEncoder::new();
let mut decoder = DedupeDecoder::new();
let mut buffer = Vec::new();
encoder.prime::<u32, H>(&10);
encoder.prime::<u32, H>(&20);
decoder.prime::<u32>(10);
decoder.prime::<u32>(20);
encoder.encode::<u32, H>(&30, &mut buffer).unwrap();
encoder.encode::<u32, H>(&10, &mut buffer).unwrap();
encoder.encode::<u32, H>(&30, &mut buffer).unwrap();
let mut cursor = Cursor::new(&buffer);
assert_eq!(decoder.decode::<u32>(&mut cursor).unwrap(), 30);
assert_eq!(decoder.decode::<u32>(&mut cursor).unwrap(), 10);
assert_eq!(decoder.decode::<u32>(&mut cursor).unwrap(), 30);
}
}