use harmos_domain::__private::decoding_add as add;
use harmos_domain::MessageError;
const DEPTH: usize = 32;
#[derive(Clone, Copy)]
pub struct DecodeInput<'a> {
source: Source<'a>,
depth: usize,
}
#[derive(Clone, Copy)]
pub struct DecodeField<'a> {
pub tag: u32,
pub value: DecodeValue<'a>,
}
#[derive(Clone, Copy)]
enum Source<'a> {
Bytes(&'a [u8]),
Fields { parent: &'a DecodeInput<'a>, tag: u32, after: usize },
}
#[derive(Clone, Copy)]
pub enum DecodeValue<'a> {
Varint(u64),
Bits32(u32),
Bits64(u64),
Bytes(&'a [u8]),
Group,
}
#[derive(Clone, Copy)]
pub enum DecodeScalar {
Varint,
Bits32,
Bits64,
}
impl<'a> DecodeInput<'a> {
pub const fn new(bytes: &'a [u8]) -> Self {
Self { source: Source::Bytes(bytes), depth: 0 }
}
pub fn child(&self, bytes: &'a [u8]) -> Result<Self, MessageError> {
self.at(Source::Bytes(bytes))
}
pub fn field(&self, tag: u32) -> Result<DecodeInput<'_>, MessageError> {
self.at(Source::Fields { parent: self, tag, after: 0 })
}
pub fn variant(&self, tags: &[u32]) -> Result<Option<(u32, DecodeInput<'_>)>, MessageError> {
let mut selected = None;
let mut after = 0;
let mut ordinal = 0usize;
self.fields(&mut |field| {
if tags.contains(&field.tag) {
self.child(field.bytes()?)?.fields(&mut |_| Ok(()))?;
if selected != Some(field.tag) {
selected = Some(field.tag);
after = ordinal;
}
} else if matches!(field.value, DecodeValue::Bytes(_)) {
return Err(MessageError::borrowed("unknown protobuf enum variant"));
}
ordinal = add(ordinal, 1)?;
Ok(())
})?;
let Some(tag) = selected else {
return Ok(None);
};
let input = self.at(Source::Fields { parent: self, tag, after })?;
Ok(Some((tag, input)))
}
pub fn encoded_len(&self) -> Result<usize, MessageError> {
let mut total = 0usize;
self.parts(&mut |bytes| {
total = add(total, bytes.len())?;
Ok(())
})?;
Ok(total)
}
pub fn contiguous(&self) -> Result<&'a [u8], MessageError> {
let mut part = None;
self.parts(&mut |bytes| {
if part.is_some() {
return Err(MessageError::borrowed("merged decoding memory bound is unsupported"));
}
part = Some(bytes);
Ok(())
})?;
Ok(part.unwrap_or(&[]))
}
pub fn fields(&self, visit: &mut dyn FnMut(DecodeField<'a>) -> Result<(), MessageError>) -> Result<(), MessageError> {
self.parts(&mut |mut bytes| {
while !bytes.is_empty() {
visit(read(&mut bytes, self.depth)?)?;
}
Ok(())
})
}
fn at<'b>(&self, source: Source<'b>) -> Result<DecodeInput<'b>, MessageError> {
let depth = add(self.depth, 1)?;
if depth > DEPTH {
return Err(MessageError::borrowed("protobuf decode exceeds 32 nested levels"));
}
Ok(DecodeInput { source, depth })
}
fn parts(&self, visit: &mut dyn FnMut(&'a [u8]) -> Result<(), MessageError>) -> Result<(), MessageError> {
match self.source {
Source::Bytes(bytes) => visit(bytes),
Source::Fields { parent, tag, after } => {
let mut ordinal = 0usize;
parent.fields(&mut |field| {
if ordinal >= after && field.tag == tag {
visit(field.bytes()?)?;
}
ordinal = add(ordinal, 1)?;
Ok(())
})
}
}
}
}
impl<'a> DecodeField<'a> {
pub fn bytes(self) -> Result<&'a [u8], MessageError> {
match self.value {
DecodeValue::Bytes(bytes) => Ok(bytes),
_ => Err(MessageError::borrowed("expected length-delimited protobuf field")),
}
}
pub fn text(self) -> Result<&'a str, MessageError> {
std::str::from_utf8(self.bytes()?).map_err(|_| MessageError::borrowed("invalid protobuf string UTF-8"))
}
pub fn scalar(self, kind: DecodeScalar) -> Result<DecodeValue<'a>, MessageError> {
match (kind, self.value) {
(DecodeScalar::Varint, DecodeValue::Varint(_))
| (DecodeScalar::Bits32, DecodeValue::Bits32(_))
| (DecodeScalar::Bits64, DecodeValue::Bits64(_)) => Ok(self.value),
_ => Err(MessageError::borrowed("protobuf scalar wire type mismatch")),
}
}
pub fn scalars(
self, kind: DecodeScalar, visit: &mut dyn FnMut(DecodeValue<'a>) -> Result<(), MessageError>,
) -> Result<(), MessageError> {
let DecodeValue::Bytes(mut bytes) = self.value else {
return visit(self.scalar(kind)?);
};
while !bytes.is_empty() {
let value = match kind {
DecodeScalar::Varint => DecodeValue::Varint(varint(&mut bytes)?),
DecodeScalar::Bits32 => DecodeValue::Bits32(u32::from_le_bytes(take(&mut bytes, 4)?.try_into().expect("four bytes"))),
DecodeScalar::Bits64 => DecodeValue::Bits64(u64::from_le_bytes(take(&mut bytes, 8)?.try_into().expect("eight bytes"))),
};
visit(value)?;
}
Ok(())
}
}
fn varint(bytes: &mut &[u8]) -> Result<u64, MessageError> {
let mut value = 0u64;
for shift in (0..70).step_by(7) {
let byte = *take(bytes, 1)?.first().expect("one byte");
if shift == 63 && byte > 1 {
return Err(MessageError::borrowed("invalid protobuf varint"));
}
value |= u64::from(byte & 127) << shift;
if byte < 128 {
return Ok(value);
}
}
Err(MessageError::borrowed("invalid protobuf varint"))
}
fn take<'a>(bytes: &mut &'a [u8], length: usize) -> Result<&'a [u8], MessageError> {
if length > bytes.len() {
return Err(MessageError::borrowed("truncated protobuf field"));
}
let (value, rest) = bytes.split_at(length);
*bytes = rest;
Ok(value)
}
fn key(bytes: &mut &[u8]) -> Result<(u32, u8), MessageError> {
let key = varint(bytes)?;
let tag = u32::try_from(key >> 3).map_err(|_| MessageError::borrowed("invalid protobuf tag"))?;
if tag == 0 || tag > 536_870_911 {
return Err(MessageError::borrowed("invalid protobuf tag"));
}
Ok((tag, (key & 7) as u8))
}
fn read<'a>(bytes: &mut &'a [u8], depth: usize) -> Result<DecodeField<'a>, MessageError> {
let (tag, wire) = key(bytes)?;
let value = value(tag, wire, bytes, depth)?;
Ok(DecodeField { tag, value })
}
fn value<'a>(tag: u32, wire: u8, bytes: &mut &'a [u8], depth: usize) -> Result<DecodeValue<'a>, MessageError> {
Ok(match wire {
0 => DecodeValue::Varint(varint(bytes)?),
1 => DecodeValue::Bits64(u64::from_le_bytes(take(bytes, 8)?.try_into().expect("eight bytes"))),
2 => {
let length = usize::try_from(varint(bytes)?).map_err(|_| MessageError::borrowed("protobuf length exceeds address space"))?;
DecodeValue::Bytes(take(bytes, length)?)
}
5 => DecodeValue::Bits32(u32::from_le_bytes(take(bytes, 4)?.try_into().expect("four bytes"))),
3 => {
if depth >= DEPTH {
return Err(MessageError::borrowed("protobuf decode exceeds 32 nested levels"));
}
loop {
let (nested, wire) = key(bytes)?;
if wire == 4 {
if nested != tag {
return Err(MessageError::borrowed("mismatched protobuf group"));
}
break;
}
value(nested, wire, bytes, depth + 1)?;
}
DecodeValue::Group
}
_ => return Err(MessageError::borrowed("invalid protobuf wire type")),
})
}