use super::{
CodecError, DataLimits, ENVELOPE_BYTES, Envelope, PublicationHeader, RecordHeader, data,
};
use crate::layout;
mod shared;
pub use shared::PreparedBlock;
#[derive(Debug)]
pub struct RecordsEncoder<'a> {
source: super::Source,
first_offset: u64,
envelope: Envelope,
metadata: &'a mut Vec<u8>,
payload: &'a mut Vec<u8>,
limits: DataLimits,
records: usize,
parts: usize,
decoded_payload_bytes: usize,
encoding_at: usize,
count_at: usize,
shared_enabled: bool,
shared: Option<shared::Payload>,
}
impl<'a> RecordsEncoder<'a> {
pub fn new(
envelope: Envelope,
header: RecordHeader,
metadata: &'a mut Vec<u8>,
payload: &'a mut Vec<u8>,
limits: DataLimits,
) -> Result<Self, CodecError> {
super::command(envelope, super::Opcode::Records)?;
header.subscription.validate()?;
header.source.validate()?;
let count_at = data::add(45, header.source.size())?;
let header_bytes = data::add(count_at, 4)?;
envelope.validate_frames(header_bytes, 0, limits.envelope)?;
data::capacity(metadata, header_bytes)?;
metadata.clear();
header.subscription.encode(metadata);
Ok(Self::begin(
envelope,
header.source,
header.first_offset,
(metadata, payload),
limits,
))
}
pub fn publication(
envelope: Envelope,
header: PublicationHeader,
metadata: &'a mut Vec<u8>,
payload: &'a mut Vec<u8>,
limits: DataLimits,
) -> Result<Self, CodecError> {
super::command(envelope, super::Opcode::RecordsPub)?;
super::publication_topic(header.source)?;
let count_at = data::add(13, header.source.size())?;
let header_bytes = data::add(count_at, 4)?;
envelope.validate_frames(header_bytes, 0, limits.envelope)?;
data::capacity(metadata, header_bytes)?;
metadata.clear();
Ok(Self::begin(
envelope,
header.source,
header.first_offset,
(metadata, payload),
limits,
))
}
fn begin(
envelope: Envelope,
source: super::Source,
first_offset: u64,
(metadata, payload): (&'a mut Vec<u8>, &'a mut Vec<u8>),
limits: DataLimits,
) -> Self {
payload.clear();
source.encode(metadata);
metadata.extend_from_slice(&first_offset.to_be_bytes());
let encoding_at = metadata.len();
metadata.push(crate::append::PayloadEncoding::Raw as u8);
metadata.extend_from_slice(&[0; 4]);
let count_at = metadata.len();
metadata.extend_from_slice(&[0; 4]);
Self {
source,
first_offset,
envelope,
metadata,
payload,
limits,
records: 0,
parts: 0,
decoded_payload_bytes: 0,
encoding_at,
count_at,
shared_enabled: false,
shared: None,
}
}
pub const fn source(&self) -> super::Source {
self.source
}
pub const fn limits(&self) -> DataLimits {
self.limits
}
pub fn remaining(&self) -> DataLimits {
let encoded = self.has_encoded_payload();
DataLimits {
max_record_bytes: self.limits.max_record_bytes,
max_records: if encoded {
0
} else {
self.limits.max_records.saturating_sub(self.records)
},
max_parts: if encoded {
0
} else {
self.limits.max_parts.saturating_sub(self.parts)
},
envelope: super::EnvelopeLimits {
max_metadata_bytes: self
.limits
.envelope
.max_metadata_bytes
.saturating_sub(self.metadata.len()),
max_payload_bytes: if encoded {
0
} else {
self.limits
.envelope
.max_payload_bytes
.saturating_sub(self.payload_bytes())
},
},
}
}
pub const fn len(&self) -> usize {
self.records
}
pub const fn is_empty(&self) -> bool {
self.records == 0
}
pub const fn decoded_payload_bytes(&self) -> usize {
self.decoded_payload_bytes
}
pub fn push_raw(&mut self, id: crate::MessageId, payload: &[u8]) -> Result<(), CodecError> {
if self.has_encoded_payload() {
return Err(CodecError::Limit);
}
self.materialize()?;
let records = data::add(self.records, 1)?;
data::record_count(records, self.first_offset, self.limits)?;
let parts = data::add(self.parts, 1)?;
if parts > self.limits.max_parts || payload.len() > self.limits.max_record_bytes {
return Err(CodecError::Limit);
}
let length = data::count(payload.len())?;
frame_capacity(
self.metadata,
data::add(self.metadata.len(), 24)?,
self.limits.envelope.max_metadata_bytes,
)?;
frame_capacity(
self.payload,
data::add(self.payload.len(), payload.len())?,
self.limits.envelope.max_payload_bytes,
)?;
let mut descriptor = [0; 24];
descriptor[..16].copy_from_slice(id.as_bytes());
descriptor[19] = 1;
descriptor[20..].copy_from_slice(&length.to_be_bytes());
self.metadata.extend_from_slice(&descriptor);
self.payload.extend_from_slice(payload);
self.records = records;
self.parts = parts;
layout::patch(self.metadata, self.count_at, (records as u32).to_be_bytes())?;
self.set_raw_payload_bytes()?;
Ok(())
}
pub fn extend_packed(
&mut self,
descriptors: &[u8],
payload: &[u8],
count: usize,
) -> Result<(), CodecError> {
if self.has_encoded_payload() {
return Err(CodecError::Limit);
}
self.materialize()?;
let first = self
.first_offset
.checked_add(self.records as u64)
.ok_or(CodecError::Length)?;
let records = data::add(self.records, count)?;
data::count(records)?;
let size = data::add(self.metadata.len(), descriptors.len())?;
let bytes = data::add(self.payload.len(), payload.len())?;
self.envelope
.validate_frames(size, bytes, self.limits.envelope)?;
data::capacity(self.metadata, size)?;
data::capacity(self.payload, bytes)?;
let parts =
data::validate_record_entries(descriptors, payload, count, first, self.remaining())?.0;
let parts = data::add(self.parts, parts)?;
self.metadata.extend_from_slice(descriptors);
self.payload.extend_from_slice(payload);
self.records = records;
self.parts = parts;
layout::patch(self.metadata, self.count_at, (records as u32).to_be_bytes())?;
self.set_raw_payload_bytes()?;
Ok(())
}
pub fn extend<'b, I, P>(&mut self, records: I) -> Result<(), CodecError>
where
I: ExactSizeIterator<Item = (crate::MessageId, data::Encoding, P)>,
P: Iterator<Item = &'b [u8]>,
{
if self.has_encoded_payload() {
return Err(CodecError::Limit);
}
self.materialize()?;
let remaining = self.remaining();
let first = self
.first_offset
.checked_add(self.records as u64)
.ok_or(CodecError::Length)?;
let count = records.len();
data::record_count(count, first, remaining)?;
let total = data::add(self.records, count)?;
data::count(total)?;
let before = (self.metadata.len(), self.payload.len());
let written = self
.write_entries(records)
.and_then(|parts| data::add(self.parts, parts));
let parts = match written {
Ok(parts) => parts,
Err(error) => {
self.metadata.truncate(before.0);
self.payload.truncate(before.1);
return Err(error);
}
};
self.records = total;
self.parts = parts;
layout::patch(self.metadata, self.count_at, (total as u32).to_be_bytes())?;
self.set_raw_payload_bytes()?;
Ok(())
}
fn write_entries<'b, P>(
&mut self,
records: impl Iterator<Item = (crate::MessageId, data::Encoding, P)>,
) -> Result<usize, CodecError>
where
P: Iterator<Item = &'b [u8]>,
{
let mut total_parts = 0;
for (id, encoding, parts) in records {
let count_at = self.begin_entry(id, encoding)?;
let mut record_bytes = 0_usize;
let mut count = 0_u32;
for part in parts {
if encoding
.frame_offset(part, self.limits.max_parts, self.limits.max_record_bytes)
.is_none()
{
return Err(CodecError::Length);
}
total_parts = data::add(total_parts, 1)?;
data::count(total_parts)?;
if data::add(self.parts, total_parts)? > self.limits.max_parts {
return Err(CodecError::Limit);
}
self.push_part(part, &mut record_bytes)?;
count = count.checked_add(1).ok_or(CodecError::Length)?;
}
if !encoding.validate(
count as usize,
self.limits.max_parts,
self.limits.max_record_bytes,
) {
return Err(CodecError::Parts);
}
layout::patch(
self.metadata,
count_at,
(count | encoding.tag()).to_be_bytes(),
)?;
}
Ok(total_parts)
}
fn begin_entry(
&mut self,
id: crate::MessageId,
encoding: data::Encoding,
) -> Result<usize, CodecError> {
let header_end = data::add(self.metadata.len(), encoding.head_bytes())?;
frame_capacity(
self.metadata,
header_end,
self.limits.envelope.max_metadata_bytes,
)?;
self.metadata.extend_from_slice(id.as_bytes());
let count_at = self.metadata.len();
self.metadata.extend_from_slice(&[0; 4]);
if let data::Encoding::Lz4 { decoded_bytes } = encoding {
self.metadata
.extend_from_slice(&decoded_bytes.to_be_bytes());
}
Ok(count_at)
}
fn push_part(&mut self, part: &[u8], record_bytes: &mut usize) -> Result<(), CodecError> {
let part_bytes = data::count(part.len())?;
let metadata_end = data::add(self.metadata.len(), 4)?;
let payload_end = data::add(self.payload.len(), part.len())?;
*record_bytes = data::add(*record_bytes, part.len())?;
if *record_bytes > self.limits.max_record_bytes {
return Err(CodecError::Limit);
}
frame_capacity(
self.metadata,
metadata_end,
self.limits.envelope.max_metadata_bytes,
)?;
frame_capacity(
self.payload,
payload_end,
self.limits.envelope.max_payload_bytes,
)?;
self.metadata.extend_from_slice(&part_bytes.to_be_bytes());
self.payload.extend_from_slice(part);
Ok(())
}
pub fn take_buffer(&mut self, buffer: &mut data::RecordBuffer) -> Result<(), CodecError> {
if !self.is_empty() || buffer.is_empty() {
return Err(CodecError::Limit);
}
data::record_count(buffer.count, self.first_offset, self.limits)?;
if buffer.parts > self.limits.max_parts {
return Err(CodecError::Limit);
}
let payload = buffer.payload.len();
let size = data::add(self.metadata.len(), buffer.metadata.len())?;
self.envelope
.validate_frames(size, payload, self.limits.envelope)?;
data::capacity(self.metadata, size)?;
if buffer.payload.capacity() != self.payload.capacity() {
return Err(CodecError::Capacity);
}
self.metadata.extend_from_slice(&buffer.metadata);
std::mem::swap(self.payload, &mut buffer.payload);
self.records = buffer.count;
self.parts = buffer.parts;
layout::patch(
self.metadata,
self.count_at,
(self.records as u32).to_be_bytes(),
)?;
self.set_raw_payload_bytes()?;
buffer.clear();
Ok(())
}
pub fn finish(self) -> Result<[u8; ENVELOPE_BYTES], CodecError> {
if self.shared.is_some() {
return Err(CodecError::Parts);
}
self.finish_with_payload().map(|(header, _)| header)
}
pub fn finish_with_payload(
self,
) -> Result<([u8; ENVELOPE_BYTES], Option<bytes::Bytes>), CodecError> {
if self.is_empty() {
return Err(CodecError::Limit);
}
let header = self.envelope.encode_header(
self.metadata.len(),
self.payload_bytes(),
self.limits.envelope,
)?;
Ok((header, self.shared.map(shared::Payload::finish)))
}
fn set_raw_payload_bytes(&mut self) -> Result<(), CodecError> {
let payload_bytes = self.payload_bytes();
self.set_payload_encoding(crate::append::PayloadEncoding::Raw, payload_bytes)?;
self.decoded_payload_bytes = payload_bytes;
Ok(())
}
fn set_payload_encoding(
&mut self,
encoding: crate::append::PayloadEncoding,
decoded_bytes: usize,
) -> Result<(), CodecError> {
let [a, b, c, d] = (decoded_bytes as u32).to_be_bytes();
layout::patch(
self.metadata,
self.encoding_at,
[encoding as u8, a, b, c, d],
)?;
Ok(())
}
fn has_encoded_payload(&self) -> bool {
self.metadata
.get(self.encoding_at)
.is_some_and(|&tag| tag != crate::append::PayloadEncoding::Raw as u8)
}
}
fn frame_capacity(buffer: &Vec<u8>, end: usize, limit: usize) -> Result<(), CodecError> {
if end > limit {
return Err(crate::EnvelopeError::Limit.into());
}
u32::try_from(end).map_err(|_| crate::EnvelopeError::Length)?;
data::capacity(buffer, end)
}