pbrs 0.2.0

Pure-Rust Protocol Buffers kernel with the Google protobuf v4 application API
Documentation
#![allow(
    clippy::unimplemented,
    clippy::unwrap_used,
    clippy::expect_used,
    reason = "null MiniTable/MessagePtr is a rust_out kernel invariant, not a user Result"
)]

use crate::error::ParseError;
use crate::internal::{Private, SealedInternal};
use crate::message::{Clear, ClearAndParse, MergeFrom, Serialize};
use crate::proxied::{AsView, View};
use crate::wire::{
    WIRE_LEN, WIRE_VARINT, decode_tag, decode_varint, decode_zigzag32, decode_zigzag64,
    read_fixed32, read_fixed64, read_len_bytes, skip_field,
};
use std::marker::PhantomData;

use super::{
    Arena, AssociatedMiniTable, FieldKind, FieldType, MessagePtr, MiniField, MiniTablePtr, MsgData,
    StringView, UpbGetArena, UpbGetMessagePtr, UpbGetMessagePtrMut,
};

fn parse_into(
    data: *mut MsgData,
    buf: &[u8],
    arena: &Arena,
    enforce_required: bool,
) -> Result<(), ParseError> {
    let mt = unsafe { (*data).mt };
    if mt.0.is_null() {
        return Ok(());
    }
    let table = unsafe { &*mt.0 };
    let mut pos = 0usize;
    while pos < buf.len() {
        let (num, wire) = decode_tag(buf, &mut pos)?;
        let Some((idx, f)) = table.field_by_number(num) else {
            skip_field(buf, &mut pos, wire)?;
            continue;
        };
        decode_field(data, idx, f, buf, &mut pos, wire, arena)?;
    }
    if enforce_required {
        unsafe {
            let has = &(*data).has;
            for (i, f) in table.fields.iter().enumerate() {
                if f.required && !has.get(i).copied().unwrap_or(false) {
                    return Err(ParseError::new("required"));
                }
            }
        }
    }
    Ok(())
}

fn decode_field(
    data: *mut MsgData,
    idx: usize,
    f: MiniField,
    buf: &[u8],
    pos: &mut usize,
    wire: u32,
    arena: &Arena,
) -> Result<(), ParseError> {
    let ptr = MessagePtr::<()> {
        raw: data,
        _phantom: PhantomData,
    };
    if f.is_map {
        if wire != WIRE_LEN {
            skip_field(buf, pos, wire)?;
            return Ok(());
        }
        let payload = read_len_bytes(buf, pos)?;
        let map = unsafe { ptr.get_or_create_mutable_map_at_index(idx as u32, arena) }
            .ok_or_else(|| ParseError::new("map alloc"))?;
        let (k, v) = decode_map_entry(f.sub, payload, arena)?;
        unsafe {
            (*map).entries.borrow_mut().push((k, v));
        }
        return Ok(());
    }
    if f.repeated && !f.is_map {
        let arr = unsafe { ptr.get_or_create_mutable_array_at_index(idx as u32, arena) }
            .ok_or_else(|| ParseError::new("array alloc"))?;
        if f.packed && wire == WIRE_LEN {
            let payload = read_len_bytes(buf, pos)?;
            let mut p = 0usize;
            while p < payload.len() {
                let v = decode_packed_item(f.ty, payload, &mut p)?;
                unsafe {
                    (*arr).items.borrow_mut().push(v);
                }
            }
            return Ok(());
        }
        let v = decode_one(f, buf, pos, wire, arena)?;
        unsafe {
            (*arr).items.borrow_mut().push(v);
        }
        ptr.set_slot(idx as u32, FieldKind::Repeated(arr), true);
        return Ok(());
    }
    if f.ty == FieldType::Message && !f.repeated {
        let payload = read_len_bytes(buf, pos)?;
        let child = arena.alloc_msg(f.sub);
        parse_into(child, payload, arena, true)?;
        ptr.set_slot(idx as u32, FieldKind::Msg(child), true);
        return Ok(());
    }
    if matches!(f.ty, FieldType::String | FieldType::Bytes) {
        let payload = read_len_bytes(buf, pos)?;
        if f.ty == FieldType::String {
            let enforce = unsafe {
                (*data)
                    .mt
                    .0
                    .as_ref()
                    .map(|t| t.enforce_utf8)
                    .unwrap_or(false)
            };
            if enforce && std::str::from_utf8(payload).is_err() {
                return Err(ParseError::new("utf8"));
            }
        }
        unsafe {
            ptr.set_base_field_string_at_index(idx as u32, StringView::from(payload));
        }
        return Ok(());
    }
    let v = decode_one(f, buf, pos, wire, arena)?;
    ptr.set_slot(idx as u32, v, true);
    Ok(())
}

fn decode_map_entry(
    sub: MiniTablePtr,
    buf: &[u8],
    arena: &Arena,
) -> Result<(Vec<u8>, FieldKind), ParseError> {
    let table = unsafe { sub.0.as_ref() };
    let mut key = Vec::new();
    let mut val = FieldKind::Empty;
    let mut pos = 0usize;
    while pos < buf.len() {
        let (num, wire) = decode_tag(buf, &mut pos)?;
        let field = table.and_then(|t| t.fields.iter().copied().find(|f| f.number == num));
        match num {
            1 => match field.map(|f| f.ty) {
                Some(FieldType::String) | Some(FieldType::Bytes) | None => {
                    if wire == WIRE_LEN {
                        key = read_len_bytes(buf, &mut pos)?.to_vec();
                    } else {
                        skip_field(buf, &mut pos, wire)?;
                    }
                }
                Some(FieldType::Bool) => {
                    if wire == WIRE_VARINT {
                        key = vec![if decode_varint(buf, &mut pos)? != 0 {
                            1
                        } else {
                            0
                        }];
                    } else {
                        skip_field(buf, &mut pos, wire)?;
                    }
                }
                Some(FieldType::Fixed32 | FieldType::SFixed32) => {
                    key = read_fixed32(buf, &mut pos)?.to_le_bytes().to_vec();
                }
                Some(FieldType::Fixed64 | FieldType::SFixed64) => {
                    key = read_fixed64(buf, &mut pos)?.to_le_bytes().to_vec();
                }
                Some(FieldType::SInt32) => {
                    let v = decode_zigzag32(decode_varint(buf, &mut pos)?);
                    key = v.to_le_bytes().to_vec();
                }
                Some(FieldType::SInt64) => {
                    let v = decode_zigzag64(decode_varint(buf, &mut pos)?);
                    key = v.to_le_bytes().to_vec();
                }
                Some(FieldType::Int64 | FieldType::UInt64) => {
                    key = decode_varint(buf, &mut pos)?.to_le_bytes().to_vec();
                }
                Some(_) => {
                    if wire == WIRE_VARINT {
                        key = (decode_varint(buf, &mut pos)? as i32)
                            .to_le_bytes()
                            .to_vec();
                    } else {
                        skip_field(buf, &mut pos, wire)?;
                    }
                }
            },
            2 => {
                if let Some(f) = field {
                    val = decode_one(f, buf, &mut pos, wire, arena)?;
                } else if wire == WIRE_VARINT {
                    val = FieldKind::I32(decode_varint(buf, &mut pos)? as i32);
                } else {
                    skip_field(buf, &mut pos, wire)?;
                }
            }
            _ => skip_field(buf, &mut pos, wire)?,
        }
    }
    Ok((key, val))
}

fn decode_packed_item(ty: FieldType, buf: &[u8], pos: &mut usize) -> Result<FieldKind, ParseError> {
    match ty {
        FieldType::Int32 | FieldType::Enum => Ok(FieldKind::I32(decode_varint(buf, pos)? as i32)),
        FieldType::Int64 => Ok(FieldKind::I64(decode_varint(buf, pos)? as i64)),
        FieldType::UInt32 => Ok(FieldKind::U32(decode_varint(buf, pos)? as u32)),
        FieldType::UInt64 => Ok(FieldKind::U64(decode_varint(buf, pos)?)),
        FieldType::SInt32 => Ok(FieldKind::I32(decode_zigzag32(decode_varint(buf, pos)?))),
        FieldType::SInt64 => Ok(FieldKind::I64(decode_zigzag64(decode_varint(buf, pos)?))),
        FieldType::Bool => Ok(FieldKind::Bool(decode_varint(buf, pos)? != 0)),
        FieldType::Fixed32 | FieldType::SFixed32 | FieldType::Float => {
            Ok(FieldKind::U32(read_fixed32(buf, pos)?))
        }
        FieldType::Fixed64 | FieldType::SFixed64 | FieldType::Double => {
            Ok(FieldKind::U64(read_fixed64(buf, pos)?))
        }
        _ => Err(ParseError::new("bad packed type")),
    }
}

fn decode_one(
    f: MiniField,
    buf: &[u8],
    pos: &mut usize,
    wire: u32,
    arena: &Arena,
) -> Result<FieldKind, ParseError> {
    match f.ty {
        FieldType::Int32 | FieldType::Enum => {
            if wire != WIRE_VARINT {
                skip_field(buf, pos, wire)?;
                return Ok(FieldKind::Empty);
            }
            Ok(FieldKind::I32(decode_varint(buf, pos)? as i32))
        }
        FieldType::Int64 => Ok(FieldKind::I64(decode_varint(buf, pos)? as i64)),
        FieldType::UInt32 => Ok(FieldKind::U32(decode_varint(buf, pos)? as u32)),
        FieldType::UInt64 => Ok(FieldKind::U64(decode_varint(buf, pos)?)),
        FieldType::SInt32 => Ok(FieldKind::I32(decode_zigzag32(decode_varint(buf, pos)?))),
        FieldType::SInt64 => Ok(FieldKind::I64(decode_zigzag64(decode_varint(buf, pos)?))),
        FieldType::Bool => Ok(FieldKind::Bool(decode_varint(buf, pos)? != 0)),
        FieldType::Fixed32 | FieldType::SFixed32 => Ok(FieldKind::U32(read_fixed32(buf, pos)?)),
        FieldType::Fixed64 | FieldType::SFixed64 => Ok(FieldKind::U64(read_fixed64(buf, pos)?)),
        FieldType::Float => Ok(FieldKind::F32(f32::from_bits(read_fixed32(buf, pos)?))),
        FieldType::Double => Ok(FieldKind::F64(f64::from_bits(read_fixed64(buf, pos)?))),
        FieldType::String | FieldType::Bytes => {
            let p = read_len_bytes(buf, pos)?;
            Ok(FieldKind::Bytes(arena.alloc_bytes(p.to_vec())))
        }
        FieldType::Message => {
            if wire != WIRE_LEN {
                skip_field(buf, pos, wire)?;
                return Ok(FieldKind::Empty);
            }
            let payload = read_len_bytes(buf, pos)?;
            let child = arena.alloc_msg(f.sub);
            parse_into(child, payload, arena, false)?;
            Ok(FieldKind::Msg(child))
        }
        _ => {
            skip_field(buf, pos, wire)?;
            Ok(FieldKind::Empty)
        }
    }
}

impl<T> ClearAndParse for T
where
    Self: SealedInternal + UpbGetMessagePtrMut + UpbGetArena,
{
    fn clear_and_parse(&mut self, data: &[u8]) -> Result<(), ParseError> {
        Clear::clear(self);
        parse_into(
            self.get_ptr_mut(Private).raw,
            data,
            self.get_arena(Private),
            true,
        )
    }
    fn clear_and_parse_dont_enforce_required(&mut self, data: &[u8]) -> Result<(), ParseError> {
        Clear::clear(self);
        parse_into(
            self.get_ptr_mut(Private).raw,
            data,
            self.get_arena(Private),
            false,
        )
    }
    fn merge_from_bytes(&mut self, data: &[u8]) -> Result<(), ParseError> {
        parse_into(
            self.get_ptr_mut(Private).raw,
            data,
            self.get_arena(Private),
            true,
        )
    }
    fn merge_from_bytes_dont_enforce_required(&mut self, data: &[u8]) -> Result<(), ParseError> {
        parse_into(
            self.get_ptr_mut(Private).raw,
            data,
            self.get_arena(Private),
            false,
        )
    }
}

impl<T> MergeFrom for T
where
    Self: SealedInternal + AsView + UpbGetArena + UpbGetMessagePtr,
    Self::Proxied: AssociatedMiniTable,
    for<'a> View<'a, Self::Proxied>: UpbGetMessagePtr,
{
    fn merge_from(&mut self, src: impl AsView<Proxied = Self::Proxied>) {
        if let Ok(bytes) = Serialize::serialize(&src.as_view()) {
            let _ = parse_into(
                self.get_ptr(Private).raw,
                &bytes,
                self.get_arena(Private),
                false,
            );
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::runtime::encode::encode_slot;
    use crate::runtime::{Arena, FieldType, MiniField, MiniTablePtr};
    use crate::wire::WIRE_LEN;

    fn string_field(repeated: bool) -> MiniField {
        MiniField {
            number: 1,
            ty: FieldType::String,
            repeated,
            packed: false,
            proto3_singular: false,
            required: false,
            is_map: false,
            sub: MiniTablePtr::dangling(),
            oneof_group: 0,
        }
    }

    #[test]
    fn parsed_string_storage_is_owned_by_the_arena() {
        let arena = Arena::new();
        let mut pos = 0;
        let field = string_field(false);
        let kind = decode_one(field, &[1, b'a'], &mut pos, WIRE_LEN, &arena).expect("decode");
        let mut encoded = Vec::new();
        encode_slot(&field, kind, &mut encoded);
        assert_eq!(encoded, [0x0a, 1, b'a']);
    }
}