use core::convert::TryFrom;
use crate::{DecodeAccumulator, DecodeError, DecodeLimits, checked_len_add};
use super::{
LENGTH_RADIX, LONG_LIST_OFFSET, LONG_STRING_OFFSET, RlpInteger, RlpItem, RlpList, RlpScalar,
SHORT_LIST_OFFSET, SHORT_STRING_LIMIT, SHORT_STRING_OFFSET,
integer::validate_rlp_integer_payload, list::validate_list_payload,
};
pub fn encoded_rlp_scalar_len(payload: &[u8]) -> Result<usize, DecodeError> {
encoded_payload_len(payload, ScalarSingleByte::Enabled)
}
pub fn encoded_rlp_integer_len(payload: &[u8]) -> Result<usize, DecodeError> {
validate_rlp_integer_payload(payload)?;
encoded_rlp_scalar_len(payload)
}
pub fn encoded_rlp_list_len(payload: &[u8], limits: DecodeLimits) -> Result<usize, DecodeError> {
validate_encoded_list_payload(payload, limits)?;
encoded_payload_len(payload, ScalarSingleByte::Disabled)
}
pub fn encode_rlp_scalar(payload: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
encode_payload(
payload,
output,
SHORT_STRING_OFFSET,
LONG_STRING_OFFSET,
ScalarSingleByte::Enabled,
)
}
pub fn encode_rlp_integer(payload: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
validate_rlp_integer_payload(payload)?;
encode_rlp_scalar(payload, output)
}
pub fn encode_rlp_list_payload(
payload: &[u8],
limits: DecodeLimits,
output: &mut [u8],
) -> Result<usize, DecodeError> {
validate_encoded_list_payload(payload, limits)?;
encode_rlp_list_payload_unchecked(payload, output)
}
fn encode_rlp_list_payload_unchecked(
payload: &[u8],
output: &mut [u8],
) -> Result<usize, DecodeError> {
encode_payload(
payload,
output,
SHORT_LIST_OFFSET,
LONG_LIST_OFFSET,
ScalarSingleByte::Disabled,
)
}
pub fn encode_decoded_scalar(
scalar: RlpScalar<'_>,
output: &mut [u8],
) -> Result<usize, DecodeError> {
encode_rlp_scalar(scalar.payload(), output)
}
pub fn encode_decoded_integer(
integer: RlpInteger<'_>,
output: &mut [u8],
) -> Result<usize, DecodeError> {
encode_rlp_integer(integer.payload(), output)
}
pub fn encode_decoded_list(list: RlpList<'_>, output: &mut [u8]) -> Result<usize, DecodeError> {
encode_rlp_list_payload_unchecked(list.payload(), output)
}
pub fn encode_decoded_item(item: RlpItem<'_>, output: &mut [u8]) -> Result<usize, DecodeError> {
match item {
RlpItem::Scalar(scalar) => encode_decoded_scalar(scalar, output),
RlpItem::List(list) => encode_decoded_list(list, output),
}
}
#[derive(Clone, Copy)]
enum ScalarSingleByte {
Enabled,
Disabled,
}
fn encoded_payload_len(
payload: &[u8],
scalar_single_byte: ScalarSingleByte,
) -> Result<usize, DecodeError> {
let payload_len = payload.len();
if matches!(scalar_single_byte, ScalarSingleByte::Enabled)
&& payload_len == 1
&& payload.first().is_some_and(|byte| *byte <= 0x7f)
{
return Ok(1);
}
if payload_len <= SHORT_STRING_LIMIT {
return checked_len_add(1, payload_len);
}
let len_of_len = length_of_length(payload_len)?;
checked_len_add(checked_len_add(1, len_of_len)?, payload_len)
}
fn encode_payload(
payload: &[u8],
output: &mut [u8],
short_offset: u8,
long_offset: u8,
scalar_single_byte: ScalarSingleByte,
) -> Result<usize, DecodeError> {
let required_len = encoded_payload_len(payload, scalar_single_byte)?;
if output.len() < required_len {
return Err(DecodeError::OffsetOutOfBounds);
}
if matches!(scalar_single_byte, ScalarSingleByte::Enabled)
&& payload.len() == 1
&& payload.first().is_some_and(|byte| *byte <= 0x7f)
{
let byte = *payload.first().ok_or(DecodeError::OffsetOutOfBounds)?;
write_byte(output, byte)?;
return Ok(1);
}
let header_len = if payload.len() <= SHORT_STRING_LIMIT {
write_short_header(output, short_offset, payload.len())?
} else {
write_long_header(output, long_offset, payload.len())?
};
write_payload(output, header_len, payload)?;
checked_len_add(header_len, payload.len())
}
fn write_short_header(
output: &mut [u8],
short_offset: u8,
payload_len: usize,
) -> Result<usize, DecodeError> {
let payload_len = u8::try_from(payload_len).map_err(|_| DecodeError::LengthOverflow)?;
let prefix = short_offset
.checked_add(payload_len)
.ok_or(DecodeError::LengthOverflow)?;
write_byte(output, prefix)?;
Ok(1)
}
fn write_long_header(
output: &mut [u8],
long_offset: u8,
payload_len: usize,
) -> Result<usize, DecodeError> {
let len_of_len = length_of_length(payload_len)?;
let len_of_len_byte = u8::try_from(len_of_len).map_err(|_| DecodeError::LengthOverflow)?;
let prefix = long_offset
.checked_add(len_of_len_byte)
.ok_or(DecodeError::LengthOverflow)?;
let header_len = checked_len_add(1, len_of_len)?;
write_byte(output, prefix)?;
let len_bytes = payload_len.to_be_bytes();
let start = len_bytes
.len()
.checked_sub(len_of_len)
.ok_or(DecodeError::LengthOverflow)?;
let source = len_bytes
.get(start..)
.ok_or(DecodeError::OffsetOutOfBounds)?;
let target = output
.get_mut(1..header_len)
.ok_or(DecodeError::OffsetOutOfBounds)?;
target.copy_from_slice(source);
Ok(header_len)
}
fn write_byte(output: &mut [u8], byte: u8) -> Result<(), DecodeError> {
let target = output.first_mut().ok_or(DecodeError::OffsetOutOfBounds)?;
*target = byte;
Ok(())
}
fn write_payload(output: &mut [u8], header_len: usize, payload: &[u8]) -> Result<(), DecodeError> {
let end = checked_len_add(header_len, payload.len())?;
let target = output
.get_mut(header_len..end)
.ok_or(DecodeError::OffsetOutOfBounds)?;
target.copy_from_slice(payload);
Ok(())
}
fn length_of_length(payload_len: usize) -> Result<usize, DecodeError> {
if payload_len <= SHORT_STRING_LIMIT {
return Err(DecodeError::Malformed);
}
let mut value = payload_len;
let mut len = 0usize;
while value > 0 {
len = checked_len_add(len, 1)?;
value = value
.checked_div(LENGTH_RADIX)
.ok_or(DecodeError::LengthOverflow)?;
}
Ok(len)
}
fn validate_encoded_list_payload(
payload: &[u8],
limits: DecodeLimits,
) -> Result<usize, DecodeError> {
let mut accumulator: DecodeAccumulator = limits.accumulator();
accumulator.check_input_len(payload.len())?;
validate_list_payload(payload, &mut accumulator)
}