use std::{collections::HashMap, io::Write};
use crate::{
codec::{
consts::{FLAG_DEVICE, FLAG_SERVER},
error::CodecError,
op::Op,
writer::EntryBufferWriter,
},
hlc::Timestamp,
uuid::Uuid,
};
pub struct Encoder<E, W> {
sink: W,
last_timestamp: u64,
uuids: HashMap<Uuid, u32>,
server_mode: bool,
entry_index: usize,
size: usize,
_phantom: std::marker::PhantomData<E>,
}
impl<E: Op, W: Write> Encoder<E, W> {
pub fn new(mut sink: W, magic: &[u8], server_mode: bool) -> Result<Self, CodecError> {
if magic.is_empty() {
return Err(CodecError::BadMagic);
}
sink.write_all(magic)?;
if server_mode {
sink.write_all(&[FLAG_SERVER])?;
} else {
sink.write_all(&[FLAG_DEVICE])?;
}
Ok(Self {
sink,
last_timestamp: 0,
uuids: HashMap::default(),
server_mode,
entry_index: 0,
size: 0,
_phantom: std::marker::PhantomData,
})
}
pub fn sink_mut(&mut self) -> &mut W {
&mut self.sink
}
pub fn entry_index(&self) -> usize {
self.entry_index
}
pub fn encode_entry(
&mut self,
op: &E,
timestamp: Timestamp,
server_user_id: Option<Uuid>,
) -> Result<usize, CodecError> {
let dict_len_before = self.uuids.len();
let result = self.try_encode_entry(op, timestamp, server_user_id);
if result.is_err() {
self.uuids.retain(|_, id| (*id as usize) <= dict_len_before);
}
result
}
fn try_encode_entry(
&mut self,
op: &E,
timestamp: Timestamp,
server_user_id: Option<Uuid>,
) -> Result<usize, CodecError> {
let mut writer = EntryBufferWriter::new(&mut self.uuids);
op.encode(&mut writer)?;
let raw_timestamp = timestamp.raw();
writer.write_delta(raw_timestamp, self.last_timestamp)?;
if self.server_mode {
match server_user_id {
Some(server_user_id) => writer.write_uuid(&server_user_id),
None => return Err(CodecError::MissingUserId),
}
}
let (bytes, _) = writer.finalize();
self.sink.write_all(&bytes)?;
self.last_timestamp = raw_timestamp;
self.entry_index += 1;
self.size += bytes.len();
Ok(self.entry_index)
}
pub fn size(&self) -> usize {
self.size
}
}