use super::{
AppendBatchView, AppendSummary, AppendView, Barrier, OperationBody, OperationCodecError,
OperationKind, OperationLimits, OperationOutput, RecordPosition, decode_append_batches,
decode_append_batches_indexed, decode_append_batches_with_validated_payload,
};
use ozzy_proto::OperationId;
use smallvec::SmallVec;
use std::ops::Range;
mod append;
mod control;
mod primitives;
pub(super) use append::{
AppendTotals, decode_append_batch, encode_append_records, records_are_tiny,
validate_append_descriptors, validate_tiny_descriptors,
};
use append::{decode_append, encode_append};
use control::{
decode_assign, decode_create, decode_open_producer, decode_policy,
decode_producer_result_floor, decode_progress, decode_trim, encode_assign, encode_create,
encode_open_producer, encode_policy, encode_producer_result_floor, encode_progress,
encode_trim,
};
pub(super) use primitives::{
Decoder, Encoder, enforce_limit, validate_limits, validate_position_count,
};
pub fn encode_operation_body(
body: &OperationBody<'_>,
limits: OperationLimits,
) -> Result<Vec<u8>, OperationCodecError> {
let mut output = Vec::new();
append_operation_body(&mut output, body, limits)?;
Ok(output)
}
pub fn append_operation_body(
output: &mut Vec<u8>,
body: &OperationBody<'_>,
limits: OperationLimits,
) -> Result<Range<usize>, OperationCodecError> {
append_operation_body_to(output, body, limits)
}
pub fn append_operation_body_to(
output: &mut impl OperationOutput,
body: &OperationBody<'_>,
limits: OperationLimits,
) -> Result<Range<usize>, OperationCodecError> {
validate_limits(limits)?;
let start = output.len();
let mut encoder = Encoder::new(output, start, limits.max_body_bytes);
let result = (|| {
match body {
OperationBody::CreatePartition(value) => encode_create(value, limits, &mut encoder)?,
OperationBody::OpenProducer(value) => encode_open_producer(*value, &mut encoder)?,
OperationBody::Append(value) => encode_append(value, limits, &mut encoder)?,
OperationBody::Progress(value) => encode_progress(*value, &mut encoder)?,
OperationBody::Assign(value) => encode_assign(*value, &mut encoder)?,
OperationBody::Trim(value) => encode_trim(*value, &mut encoder)?,
OperationBody::PartitionPolicy(value) => encode_policy(*value, &mut encoder)?,
OperationBody::Barrier(value) => encoder.id(value.operation_id.as_bytes())?,
OperationBody::ProducerResultFloor(value) => {
encode_producer_result_floor(*value, &mut encoder)?;
}
}
Ok(())
})();
if let Err(error) = result {
encoder.restore_start();
return Err(error);
}
Ok(start..encoder.finish())
}
pub fn validate_operation_body(
kind: OperationKind,
input: &[u8],
limits: OperationLimits,
) -> Result<(), OperationCodecError> {
decode_body::<false, true>(kind, input, limits).map(|_| ())
}
pub fn validate_operation_body_with_validated_payload(
kind: OperationKind,
input: &[u8],
limits: OperationLimits,
) -> Result<(), OperationCodecError> {
decode_body::<false, false>(kind, input, limits).map(|_| ())
}
pub fn decode_operation_body(
kind: OperationKind,
input: &[u8],
limits: OperationLimits,
) -> Result<OperationBody<'_>, OperationCodecError> {
decode_body::<true, true>(kind, input, limits)
}
pub fn decode_append_summary(
input: &[u8],
limits: OperationLimits,
) -> Result<AppendSummary, OperationCodecError> {
decode_append_summary_and_view(input, limits).map(|(summary, _)| summary)
}
pub fn decode_append_summary_and_view(
input: &[u8],
limits: OperationLimits,
) -> Result<(AppendSummary, AppendView<'_>), OperationCodecError> {
validate_limits(limits)?;
enforce_limit("operation body bytes", input.len(), limits.max_body_bytes)?;
let mut decoder = Decoder::new(input);
let count = decoder.count("append batch count", limits.max_append_batches)?;
if count == 0 {
return Err(OperationCodecError::EmptyAppend);
}
let mut batches = SmallVec::with_capacity(count);
let mut totals = AppendTotals::default();
for _ in 0..count {
let (_, view) =
decode_append_batch::<false, true>(&mut decoder, limits, &mut totals, None)?;
batches.push(view.summary);
}
decoder.finish()?;
Ok((
AppendSummary { batches },
AppendView::validated(input, limits),
))
}
pub fn decode_append_summary_and_batches(
input: &[u8],
limits: OperationLimits,
) -> Result<(AppendSummary, SmallVec<[AppendBatchView<'_>; 4]>), OperationCodecError> {
let views = decode_append_batches(input, limits)?;
let summary = AppendSummary {
batches: views.iter().map(|view| view.summary).collect(),
};
Ok((summary, views))
}
pub fn decode_append_summary_and_batches_indexed<'a>(
input: &'a [u8],
limits: OperationLimits,
positions: &mut Vec<RecordPosition>,
) -> Result<(AppendSummary, SmallVec<[AppendBatchView<'a>; 4]>), OperationCodecError> {
let views = decode_append_batches_indexed(input, limits, positions)?;
let summary = AppendSummary {
batches: views.iter().map(|view| view.summary).collect(),
};
Ok((summary, views))
}
pub fn decode_append_summary_and_batches_with_validated_payload(
input: &[u8],
limits: OperationLimits,
) -> Result<(AppendSummary, SmallVec<[AppendBatchView<'_>; 4]>), OperationCodecError> {
let views = decode_append_batches_with_validated_payload(input, limits)?;
let summary = AppendSummary {
batches: views.iter().map(|view| view.summary).collect(),
};
Ok((summary, views))
}
fn decode_body<const MATERIALIZE: bool, const VALIDATE_PREPARED: bool>(
kind: OperationKind,
input: &[u8],
limits: OperationLimits,
) -> Result<OperationBody<'_>, OperationCodecError> {
validate_limits(limits)?;
enforce_limit("operation body bytes", input.len(), limits.max_body_bytes)?;
let mut decoder = Decoder::new(input);
let body = match kind {
OperationKind::CreatePartition => {
OperationBody::CreatePartition(decode_create(&mut decoder, limits)?)
}
OperationKind::OpenProducer => {
OperationBody::OpenProducer(decode_open_producer(&mut decoder)?)
}
OperationKind::Append => OperationBody::Append(decode_append::<
MATERIALIZE,
VALIDATE_PREPARED,
>(&mut decoder, limits)?),
OperationKind::Progress => OperationBody::Progress(decode_progress(&mut decoder)?),
OperationKind::Assign => OperationBody::Assign(decode_assign(&mut decoder)?),
OperationKind::Trim => OperationBody::Trim(decode_trim(&mut decoder)?),
OperationKind::PartitionPolicy => {
OperationBody::PartitionPolicy(decode_policy(&mut decoder)?)
}
OperationKind::Barrier => OperationBody::Barrier(Barrier {
operation_id: OperationId::from_bytes(decoder.id()?),
}),
OperationKind::ProducerResultFloor => {
OperationBody::ProducerResultFloor(decode_producer_result_floor(&mut decoder)?)
}
};
decoder.finish()?;
Ok(body)
}