eth-valkyoth-codec 0.16.0

Bounded no_std Ethereum wire codec policy.
Documentation
use crate::{DecodeError, DecodeLimits, checked_len_add, require_range_in_bounds};

use super::{
    LONG_LIST_OFFSET, RlpItem, RlpList, RlpListForm, RlpScalar, SHORT_LIST_OFFSET,
    SHORT_STRING_LIMIT, count_immediate_items,
};
use crate::rlp::{
    RlpScalarForm, decode_long_string, decode_short_string, parse_payload_len, payload,
};

pub(super) struct ParsedItem {
    pub(super) kind: ParsedItemKind,
    header_len: usize,
    pub(super) payload_start: usize,
    pub(super) payload_end: usize,
    pub(super) item_end: usize,
}

impl ParsedItem {
    pub(super) fn into_rlp_item<'a>(
        self,
        input: &'a [u8],
        offset: usize,
        limits: DecodeLimits,
        depth_remaining: usize,
    ) -> Result<(RlpItem<'a>, usize), DecodeError> {
        let payload = input
            .get(self.payload_start..self.payload_end)
            .ok_or(DecodeError::OffsetOutOfBounds)?;
        let encoded_len = self
            .item_end
            .checked_sub(offset)
            .ok_or(DecodeError::LengthOverflow)?;
        let item = match self.kind {
            ParsedItemKind::Scalar(form) => RlpItem::Scalar(RlpScalar {
                payload,
                encoded_len,
                header_len: self.header_len,
                form,
            }),
            ParsedItemKind::List(form) => {
                if depth_remaining == 0 {
                    return Err(DecodeError::NestingTooDeep);
                }
                RlpItem::List(RlpList {
                    payload,
                    encoded_len,
                    header_len: self.header_len,
                    item_count: count_immediate_items(payload, limits)?,
                    form,
                    limits,
                    iteration_depth_remaining: depth_remaining.saturating_sub(1),
                })
            }
        };
        Ok((item, self.item_end))
    }
}

#[derive(Clone, Copy)]
pub(super) enum ParsedItemKind {
    Scalar(RlpScalarForm),
    List(RlpListForm),
}

pub(super) fn parse_item(
    input: &[u8],
    offset: usize,
    container_end: usize,
) -> Result<ParsedItem, DecodeError> {
    let local = input
        .get(offset..container_end)
        .ok_or(DecodeError::OffsetOutOfBounds)?;
    let prefix = *local.first().ok_or(DecodeError::Malformed)?;
    let (kind, header_len, payload_len) = match prefix {
        0x00..=0x7f => (ParsedItemKind::Scalar(RlpScalarForm::SingleByte), 0, 1),
        crate::rlp::SHORT_STRING_OFFSET..=crate::rlp::LONG_STRING_OFFSET => {
            let scalar = decode_short_string(local, prefix)?;
            (
                ParsedItemKind::Scalar(scalar.form()),
                scalar.header_len(),
                scalar.payload().len(),
            )
        }
        0xb8..=0xbf => {
            let scalar = decode_long_string(local, prefix)?;
            (
                ParsedItemKind::Scalar(scalar.form()),
                scalar.header_len(),
                scalar.payload().len(),
            )
        }
        SHORT_LIST_OFFSET..=LONG_LIST_OFFSET => {
            let payload_len = usize::from(prefix.saturating_sub(SHORT_LIST_OFFSET));
            payload(local, 1, payload_len)?;
            (ParsedItemKind::List(RlpListForm::ShortList), 1, payload_len)
        }
        0xf8..=0xff => {
            let len_of_len = usize::from(prefix.saturating_sub(LONG_LIST_OFFSET));
            let payload_len = parse_payload_len(local, 1, len_of_len)?;
            if payload_len <= SHORT_STRING_LIMIT {
                return Err(DecodeError::Malformed);
            }
            let header_len = checked_len_add(1, len_of_len)?;
            payload(local, header_len, payload_len)?;
            (
                ParsedItemKind::List(RlpListForm::LongList),
                header_len,
                payload_len,
            )
        }
    };
    let payload_start = checked_len_add(offset, header_len)?;
    let payload_end = checked_len_add(payload_start, payload_len)?;
    let item_end = if header_len == 0 {
        checked_len_add(offset, 1)?
    } else {
        payload_end
    };
    require_range_in_bounds(offset, item_end.saturating_sub(offset), container_end)?;
    Ok(ParsedItem {
        kind,
        header_len,
        payload_start,
        payload_end,
        item_end,
    })
}