use super::{
AppendKey, Authority, CodecError, DataLimits, ENVELOPE_BYTES, Envelope, MessageId, Opcode,
Packet, PartitionIncarnation, Policy, Reader, add, capacity, command, count, decode_common,
encode_common, identities, record_count,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct OpPosition {
pub op: u64,
pub digest: [u8; 32],
}
#[derive(Debug, Clone, Copy)]
pub struct Appended<'a> {
pub authority: Authority,
pub partition: PartitionIncarnation,
pub owner_epoch: u64,
pub key: AppendKey,
pub first_offset: u64,
pub policy: Policy,
pub position: OpPosition,
pub message_ids: &'a [MessageId],
}
#[derive(Debug, Clone)]
pub struct DecodedAppended<'a> {
pub authority: Authority,
pub partition: PartitionIncarnation,
pub owner_epoch: u64,
pub key: AppendKey,
pub first_offset: u64,
pub policy: Policy,
pub position: OpPosition,
pub message_ids: MessageIds<'a>,
}
#[derive(Debug, Clone)]
pub struct MessageIds<'a>(std::slice::Iter<'a, [u8; 16]>);
impl Iterator for MessageIds<'_> {
type Item = MessageId;
fn next(&mut self) -> Option<Self::Item> {
Some(MessageId::from_bytes(*self.0.next()?))
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
}
impl ExactSizeIterator for MessageIds<'_> {}
impl std::iter::FusedIterator for MessageIds<'_> {}
pub fn encode_appended(
envelope: Envelope,
receipt: Appended<'_>,
metadata: &mut Vec<u8>,
limits: DataLimits,
) -> Result<[u8; ENVELOPE_BYTES], CodecError> {
command(envelope, Opcode::Appended, true)?;
identities(
receipt.authority,
receipt.partition,
receipt.owner_epoch,
receipt.key,
)?;
record_count(
receipt.message_ids.len(),
receipt.key.first_sequence,
limits,
)?;
record_count(receipt.message_ids.len(), receipt.first_offset, limits)?;
position(receipt.position)?;
let size = add(
146,
receipt
.message_ids
.len()
.checked_mul(16)
.ok_or(CodecError::Length)?,
)?;
let header = envelope.encode_header(size, 0, limits.envelope)?;
capacity(metadata, size)?;
metadata.clear();
encode_common(
metadata,
receipt.authority,
receipt.partition,
receipt.owner_epoch,
receipt.key,
);
metadata.extend_from_slice(&receipt.first_offset.to_be_bytes());
let records = count(receipt.message_ids.len())?.to_be_bytes();
metadata.extend_from_slice(&records);
metadata.push(receipt.policy as u8);
metadata.extend_from_slice(&receipt.position.op.to_be_bytes());
metadata.extend_from_slice(&receipt.position.digest);
metadata.extend_from_slice(&records);
for id in receipt.message_ids {
metadata.extend_from_slice(id.as_bytes());
}
Ok(header)
}
pub fn decode_appended(
packet: Packet<'_>,
limits: DataLimits,
) -> Result<DecodedAppended<'_>, CodecError> {
command(packet.envelope, Opcode::Appended, true)?;
packet.envelope.validate_frames(
packet.metadata.len(),
packet.payload.len(),
limits.envelope,
)?;
if !packet.payload.is_empty() {
return Err(CodecError::Length);
}
let mut cursor = Reader::new(packet.metadata);
let (authority, partition, owner_epoch, key) = decode_common(&mut cursor)?;
let first_offset = cursor.u64()?;
let records = cursor.u32()? as usize;
record_count(records, key.first_sequence, limits)?;
record_count(records, first_offset, limits)?;
let policy = Policy::try_from(cursor.u8()?)?;
let through = OpPosition {
op: cursor.u64()?,
digest: cursor.array()?,
};
position(through)?;
if cursor.u32()? as usize != records {
return Err(CodecError::Length);
}
let ids = cursor.take(records.checked_mul(16).ok_or(CodecError::Length)?)?;
if !cursor.rest().is_empty() {
return Err(CodecError::Length);
}
Ok(DecodedAppended {
authority,
partition,
owner_epoch,
key,
first_offset,
policy,
position: through,
message_ids: MessageIds(ids.as_chunks::<16>().0.iter()),
})
}
fn position(position: OpPosition) -> Result<(), CodecError> {
if position.op == 0 || position.op == u64::MAX || position.digest == [0; 32] {
return Err(CodecError::Position);
}
Ok(())
}