use anyhow::Context as _;
use prost::Message as _;
use prost_reflect::ReflectMessage;
use std::collections::BTreeMap;
#[allow(non_upper_case_globals)]
pub const kB: usize = 1 << 10;
pub const MB: usize = 1 << 20;
#[derive(Clone, Copy, PartialEq, Eq)]
pub(super) enum Wire {
Varint,
I64,
Len,
I32,
}
const VARINT: u32 = 0;
const I64: u32 = 1;
const LEN: u32 = 2;
const I32: u32 = 5;
impl Wire {
pub(crate) const fn from_tag(tag: u32) -> Option<Self> {
match tag & 7 {
VARINT => Some(Self::Varint),
I64 => Some(Self::I64),
LEN => Some(Self::Len),
I32 => Some(Self::I32),
_ => None,
}
}
pub(crate) const fn raw(self) -> u32 {
match self {
Self::Varint => VARINT,
Self::I64 => I64,
Self::Len => LEN,
Self::I32 => I32,
}
}
}
impl From<prost_reflect::Kind> for Wire {
fn from(kind: prost_reflect::Kind) -> Self {
use prost_reflect::Kind;
match kind {
Kind::Int32
| Kind::Int64
| Kind::Uint32
| Kind::Uint64
| Kind::Sint32
| Kind::Sint64
| Kind::Bool
| Kind::Enum(_) => Self::Varint,
Kind::Fixed64 | Kind::Sfixed64 | Kind::Double => Self::I64,
Kind::Fixed32 | Kind::Sfixed32 | Kind::Float => Self::I32,
Kind::String | Kind::Bytes | Kind::Message(_) => Self::Len,
}
}
}
pub(super) struct Reader<'a>(quick_protobuf::BytesReader, &'a [u8]);
impl<'a> Reader<'a> {
fn new(bytes: &'a [u8]) -> Self {
Self(quick_protobuf::BytesReader::from_bytes(bytes), bytes)
}
fn read(&mut self, wire: Wire) -> anyhow::Result<Vec<u8>> {
let mut v = vec![];
let mut w = quick_protobuf::Writer::new(&mut v);
match wire {
Wire::Varint => w.write_varint(self.0.read_varint64(self.1)?).unwrap(),
Wire::I64 => w.write_fixed64(self.0.read_fixed64(self.1)?).unwrap(),
Wire::Len => return Ok(self.0.read_bytes(self.1)?.into()),
Wire::I32 => w.write_fixed32(self.0.read_fixed32(self.1)?).unwrap(),
}
Ok(v)
}
fn read_field(
&mut self,
out: &mut Vec<Vec<u8>>,
field_wire: Wire,
got_wire: Wire,
) -> anyhow::Result<()> {
if got_wire == field_wire {
out.push(self.read(field_wire)?);
return Ok(());
}
if got_wire != Wire::Len {
anyhow::bail!("unexpected wire type");
}
let mut r = Self::new(self.0.read_bytes(self.1)?);
while !r.0.is_eof() {
out.push(r.read(field_wire)?);
}
Ok(())
}
}
pub(super) fn read_fields(
buf: &[u8],
desc: &prost_reflect::MessageDescriptor,
) -> anyhow::Result<BTreeMap<u32, Vec<Vec<u8>>>> {
if desc.parent_file().syntax() != prost_reflect::Syntax::Proto3 {
anyhow::bail!("only proto3 syntax is supported");
}
let mut r = Reader::new(buf);
let mut fields = BTreeMap::new();
while !r.0.is_eof() {
let tag = r.0.next_tag(r.1)?;
let wire = Wire::from_tag(tag).context("invalid wire type")?;
let field = desc.get_field(tag >> 3).context("unknown field")?;
if field.is_map() {
anyhow::bail!("maps unsupported");
}
if !field.is_list() && !field.supports_presence() {
anyhow::bail!(
"{}::{} : fields with implicit presence are not supported",
field.parent_message().name(),
field.name()
);
}
r.read_field(
fields.entry(field.number()).or_default(),
field.kind().into(),
wire,
)?;
}
Ok(fields)
}
pub fn canonical_raw(
buf: &[u8],
desc: &prost_reflect::MessageDescriptor,
) -> anyhow::Result<Vec<u8>> {
let mut v = vec![];
let mut w = quick_protobuf::Writer::new(&mut v);
for (num, mut values) in read_fields(buf, desc)? {
let fd = desc.get_field(num).unwrap();
if values.len() > 1 && !fd.is_list() {
anyhow::bail!("non-repeated field with multiple values");
}
if let prost_reflect::Kind::Message(desc) = &fd.kind() {
for v in &mut values {
*v = canonical_raw(v, desc)?;
}
}
let wire = Wire::from(fd.kind());
match wire {
Wire::Varint | Wire::I64 | Wire::I32 => {
if values.len() > 1 {
w.write_tag(num << 3 | LEN).unwrap();
w.write_bytes(&values.into_iter().flatten().collect::<Vec<_>>())
.unwrap();
} else {
w.write_tag(num << 3 | wire.raw()).unwrap();
for b in &values[0] {
w.write_u8(*b).unwrap();
}
}
}
Wire::Len => {
for v in &values {
w.write_tag(num << 3 | LEN).unwrap();
w.write_bytes(v).unwrap();
}
}
}
}
Ok(v)
}
pub fn canonical<T: ProtoFmt>(x: &T) -> Vec<u8> {
let msg = x.build();
canonical_raw(&msg.encode_to_vec(), &msg.descriptor()).unwrap()
}
pub fn encode<T: ProtoFmt>(x: &T) -> Vec<u8> {
canonical(x)
}
pub fn decode<T: ProtoFmt>(bytes: &[u8]) -> anyhow::Result<T> {
T::read(&<T as ProtoFmt>::Proto::decode(bytes)?)
}
pub trait ProtoFmt: Sized {
type Proto: ReflectMessage + Default;
fn read(r: &Self::Proto) -> anyhow::Result<Self>;
fn build(&self) -> Self::Proto;
}
pub fn read_required<T: ProtoFmt>(field: &Option<T::Proto>) -> anyhow::Result<T> {
ProtoFmt::read(field.as_ref().context("missing field")?)
}
pub fn read_optional<T: ProtoFmt>(field: &Option<T::Proto>) -> anyhow::Result<Option<T>> {
field.as_ref().map(ProtoFmt::read).transpose()
}
pub fn read_map<T, K, V>(
items: &[T],
k: impl Fn(&T) -> &Option<K::Proto>,
v: impl Fn(&T) -> &Option<V::Proto>,
) -> anyhow::Result<BTreeMap<K, V>>
where
K: ProtoFmt + Ord,
V: ProtoFmt,
{
let items: Vec<(K, V)> = items
.iter()
.map(|item| {
let k = read_required(k(item)).context("key")?;
let v = read_required(v(item)).context("value")?;
Ok((k, v))
})
.collect::<anyhow::Result<Vec<_>>>()?;
Ok(items.into_iter().collect())
}
pub fn required<T>(field: &Option<T>) -> anyhow::Result<&T> {
field.as_ref().context("missing")
}