use crate::{
authorisation::PriorAuthedEntryEntryPair, defaults::default_authorised_entry, prelude::*,
};
use super::DropSliceMetadata;
use compact_u64::{cu64_encode, cu64_encode_standalone, write_tag};
use core::marker::PhantomData;
use ufotofu::codec_prelude::*;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum EncodeDropError<ProducerError, ConsumerError> {
ProducerError(ProducerError),
ConsumerError(ConsumerError),
ConsumedBytesMismatch,
ArchitectureTooSmall,
}
pub struct DropEncoder<C, P>
where
C: BulkConsumer<Item = u8>,
P: BulkProducer<Item = u8>,
{
drop_consumer: C,
entry_state: AuthorisedEntry,
chunk_offset: u64,
phantom: PhantomData<P>,
}
impl<C, P> DropEncoder<C, P>
where
C: BulkConsumer<Item = u8>,
P: BulkProducer<Item = u8>,
{
pub fn new(drop_consumer: C) -> Self {
let entry_state = default_authorised_entry();
DropEncoder {
drop_consumer,
entry_state,
chunk_offset: 0,
phantom: PhantomData,
}
}
}
impl<C, P> From<C> for DropEncoder<C, P>
where
C: BulkConsumer<Item = u8>,
P: BulkProducer<Item = u8>,
{
fn from(value: C) -> Self {
DropEncoder::new(value)
}
}
impl<C, P> Consumer for DropEncoder<C, P>
where
C: BulkConsumer<Item = u8>,
P: BulkProducer<Item = u8>,
{
type Item = (DropSliceMetadata, P);
type Final = ();
type Error = EncodeDropError<P::Error, C::Error>;
async fn consume(&mut self, val: Either<Self::Item, Self::Final>) -> Result<(), Self::Error> {
let (metadata, mut payload) = match val {
Right(()) => {
return self
.drop_consumer
.consume_item(0u8)
.await
.map_err(EncodeDropError::ConsumerError);
}
Left((metadata, payload)) => (metadata, payload),
};
let expected_bytes = metadata.expected_bytes();
if metadata.entry() != &self.entry_state {
let entry = metadata.entry();
self.chunk_offset = 0;
let mut needs_namespace = false;
let mut needs_subspace = false;
let mut header = 0b0100_0000;
if entry.namespace_id() != self.entry_state.namespace_id() {
needs_namespace = true;
header |= 0b0010_0000;
}
if entry.subspace_id() != self.entry_state.subspace_id() {
needs_subspace = true;
header |= 0b0001_0000;
}
write_tag(&mut header, 2, 4, entry.timestamp().into());
if metadata.is_complete() {
header |= 0b0000_0001;
}
if !metadata.is_complete() && metadata.chunk_count() > 0 && metadata.first_chunk() == 0
{
header |= 0b0000_0010;
}
if metadata.is_multislice() {
header |= 0b0000_0011;
}
self.drop_consumer
.consume_item(header)
.await
.map_err(EncodeDropError::ConsumerError)?;
if needs_namespace {
entry
.namespace_id()
.encode(&mut self.drop_consumer)
.await
.map_err(EncodeDropError::ConsumerError)?;
}
if needs_subspace {
entry
.subspace_id()
.encode(&mut self.drop_consumer)
.await
.map_err(EncodeDropError::ConsumerError)?;
}
entry
.path()
.relative_encode(self.entry_state.path(), &mut self.drop_consumer)
.await
.map_err(EncodeDropError::ConsumerError)?;
cu64_encode(entry.timestamp().into(), 2, &mut self.drop_consumer)
.await
.map_err(EncodeDropError::ConsumerError)?;
cu64_encode_standalone(entry.payload_length(), &mut self.drop_consumer)
.await
.map_err(EncodeDropError::ConsumerError)?;
entry
.payload_digest()
.encode(&mut self.drop_consumer)
.await
.map_err(EncodeDropError::ConsumerError)?;
let pair =
PriorAuthedEntryEntryPair::new(self.entry_state.clone(), entry.entry().clone());
entry
.authorisation_token()
.relative_encode(&pair, &mut self.drop_consumer)
.await
.map_err(EncodeDropError::ConsumerError)?;
self.entry_state = entry.clone();
match header & 0b0000_0011 {
0b0000_0000 => {
return Ok(());
}
0b0000_0011 => (), _ => {
let mut consumed_bytes: usize = 0;
loop {
match payload
.expose_items(async |items| {
match self.drop_consumer.bulk_consume(items).await {
Ok(consumed) => (consumed, Ok(consumed)),
Err(err) => (0, Err(EncodeDropError::ConsumerError(err))),
}
})
.await
.map_err(EncodeDropError::ProducerError)?
{
Left(progress) => {
consumed_bytes = consumed_bytes
.checked_add(progress?)
.ok_or(EncodeDropError::ArchitectureTooSmall)?;
if consumed_bytes as u64 > expected_bytes {
return Err(EncodeDropError::ConsumedBytesMismatch);
}
}
Right(_) => {
if consumed_bytes as u64 != expected_bytes {
return Err(EncodeDropError::ConsumedBytesMismatch);
}
return Ok(());
}
}
}
}
}
}
let mut slice_header = 0b1000_0000;
let chunk_interval = metadata.first_chunk() - self.chunk_offset.saturating_sub(1);
write_tag(&mut slice_header, 3, 1, chunk_interval);
write_tag(&mut slice_header, 4, 3, metadata.chunk_count());
cu64_encode(chunk_interval, 3, &mut self.drop_consumer)
.await
.map_err(EncodeDropError::ConsumerError)?;
cu64_encode(metadata.chunk_count(), 4, &mut self.drop_consumer)
.await
.map_err(EncodeDropError::ConsumerError)?;
let mut consumed_bytes: usize = 0;
loop {
match payload
.expose_items(
async |items| match self.drop_consumer.bulk_consume(items).await {
Ok(consumed) => (consumed, Ok(consumed)),
Err(err) => (0, Err(EncodeDropError::ConsumerError(err))),
},
)
.await
.map_err(EncodeDropError::ProducerError)?
{
Left(progress) => {
consumed_bytes = consumed_bytes
.checked_add(progress?)
.ok_or(EncodeDropError::ArchitectureTooSmall)?;
if (consumed_bytes as u64) > expected_bytes {
return Err(EncodeDropError::ConsumedBytesMismatch);
}
}
Right(_) => {
if consumed_bytes as u64 != expected_bytes {
return Err(EncodeDropError::ConsumedBytesMismatch);
}
self.chunk_offset += metadata.chunk_count();
return Ok(());
}
}
}
}
async fn flush(&mut self) -> Result<(), Self::Error> {
self.drop_consumer
.flush()
.await
.map_err(EncodeDropError::ConsumerError)
}
}