use alloc::vec::Vec;
use crate::allocation::{
AllocationContext, AllocationError, RequestedCapacity, try_push, try_reserve_total_exact,
};
use crate::error::{ParseError, ParseErrorKind, PayloadKind};
use crate::source::SourceLineNumber;
use super::compact::CompactByte;
use super::count::PayloadByteCount;
use super::program::ProgramByte;
use super::runtime::RuntimeByte;
pub(crate) fn push_bytes(
output: &mut Vec<u8>,
source: impl IntoIterator<Item = u8>,
context: AllocationContext,
) -> Result<(), AllocationError> {
for byte in source {
try_push(output, byte, context)?;
}
Ok(())
}
#[derive(Debug, PartialEq, Eq)]
pub(crate) struct Payload {
bytes: Vec<ProgramByte>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct PayloadSyntax<'code> {
bytes: &'code [CompactByte],
line_number: SourceLineNumber,
payload_kind: PayloadKind,
}
#[derive(Debug, PartialEq, Eq)]
pub(crate) struct ValidatedPayloadSyntax {
bytes: Vec<ProgramByte>,
}
impl<'code> PayloadSyntax<'code> {
pub(crate) const fn new(
bytes: &'code [CompactByte],
line_number: SourceLineNumber,
payload_kind: PayloadKind,
) -> Self {
Self {
bytes,
line_number,
payload_kind,
}
}
pub(crate) const fn byte_count(self) -> PayloadByteCount {
PayloadByteCount::new(self.bytes.len())
}
pub(crate) fn validate(self) -> Result<ValidatedPayloadSyntax, ParseError> {
for byte in self.bytes.iter().copied() {
ProgramByte::parse(byte, self.line_number, self.payload_kind)?;
}
let mut bytes = Vec::new();
try_reserve_total_exact(
&mut bytes,
RequestedCapacity::from_payload_count(self.byte_count()),
AllocationContext::ProgramPayload,
)
.map_err(|error| {
ParseError::at_line(self.line_number, ParseErrorKind::Allocation(error))
})?;
for byte in self.bytes.iter().copied() {
let parsed = ProgramByte::from_validated_payload_byte(byte).map_err(|error| {
ParseError::at_line(self.line_number, ParseErrorKind::InternalInvariant(error))
})?;
try_push(&mut bytes, parsed, AllocationContext::ProgramPayload).map_err(|error| {
ParseError::at_line(self.line_number, ParseErrorKind::Allocation(error))
})?;
}
Ok(ValidatedPayloadSyntax { bytes })
}
}
impl ValidatedPayloadSyntax {
pub(crate) fn into_payload(self) -> Payload {
Payload { bytes: self.bytes }
}
}
impl Payload {
pub(crate) fn byte_count(&self) -> PayloadByteCount {
PayloadByteCount::new(self.bytes.len())
}
pub(crate) fn program_bytes(&self) -> &[ProgramByte] {
&self.bytes
}
pub(crate) fn needle(&self) -> PayloadNeedle<'_> {
match self.bytes.split_first() {
Some((&first, _)) => PayloadNeedle::NonEmpty(NonEmptyPayloadNeedle {
payload: self,
first,
}),
None => PayloadNeedle::Empty(EmptyPayloadNeedle { payload: self }),
}
}
pub(crate) fn bytes(&self) -> impl Iterator<Item = u8> + '_ {
self.bytes.iter().copied().map(ProgramByte::get)
}
pub(crate) fn push_bytes_to(
&self,
output: &mut Vec<u8>,
context: AllocationContext,
) -> Result<(), AllocationError> {
push_bytes(output, self.bytes(), context)
}
pub(crate) fn runtime_bytes(&self) -> impl Iterator<Item = RuntimeByte> + '_ {
self.bytes.iter().copied().map(RuntimeByte::from_program)
}
pub(crate) fn to_vec_with_context(
&self,
context: AllocationContext,
) -> Result<Vec<u8>, AllocationError> {
let mut output = Vec::new();
try_reserve_total_exact(
&mut output,
RequestedCapacity::from_payload_count(self.byte_count()),
context,
)?;
self.push_bytes_to(&mut output, context)?;
Ok(output)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum PayloadNeedle<'payload> {
Empty(EmptyPayloadNeedle<'payload>),
NonEmpty(NonEmptyPayloadNeedle<'payload>),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct EmptyPayloadNeedle<'payload> {
payload: &'payload Payload,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct NonEmptyPayloadNeedle<'payload> {
payload: &'payload Payload,
first: ProgramByte,
}
impl EmptyPayloadNeedle<'_> {
pub(crate) fn byte_count(self) -> PayloadByteCount {
self.payload.byte_count()
}
}
impl<'payload> NonEmptyPayloadNeedle<'payload> {
pub(crate) fn byte_count(self) -> PayloadByteCount {
self.payload.byte_count()
}
pub(crate) const fn first_byte(self) -> ProgramByte {
self.first
}
pub(crate) fn program_bytes(self) -> &'payload [ProgramByte] {
self.payload.program_bytes()
}
}