1use crate::{
2 DecodeAccumulator, DecodeError, DecodeLimits, checked_len_add, require_exact_consumption,
3 require_range_in_bounds,
4};
5
6use super::{
7 LONG_LIST_OFFSET, RlpScalar, RlpScalarForm, SHORT_LIST_OFFSET, SHORT_STRING_LIMIT,
8 decode_long_string, decode_short_string, parse_payload_len, payload,
9};
10
11pub const MAX_RLP_LIST_TRAVERSAL_DEPTH: usize = 128;
16
17#[derive(Clone, Copy, Debug, Eq, PartialEq)]
19pub enum RlpListForm {
20 ShortList,
22 LongList,
24}
25
26#[derive(Clone, Copy, Debug)]
31pub struct RlpList<'a> {
32 payload: &'a [u8],
33 encoded_len: usize,
34 header_len: usize,
35 item_count: usize,
36 form: RlpListForm,
37 limits: DecodeLimits,
38}
39
40impl<'a> RlpList<'a> {
41 #[must_use]
43 pub const fn payload(self) -> &'a [u8] {
44 self.payload
45 }
46
47 #[must_use]
49 pub const fn encoded_len(self) -> usize {
50 self.encoded_len
51 }
52
53 #[must_use]
55 pub const fn header_len(self) -> usize {
56 self.header_len
57 }
58
59 #[must_use]
61 pub const fn item_count(self) -> usize {
62 self.item_count
63 }
64
65 #[must_use]
67 pub const fn form(self) -> RlpListForm {
68 self.form
69 }
70
71 #[must_use]
73 pub const fn is_empty(self) -> bool {
74 self.item_count == 0
75 }
76
77 #[must_use]
79 pub const fn items(self) -> RlpListItems<'a> {
80 RlpListItems {
81 input: self.payload,
82 cursor: 0,
83 remaining: self.item_count,
84 limits: self.limits,
85 }
86 }
87}
88
89impl PartialEq for RlpList<'_> {
90 fn eq(&self, other: &Self) -> bool {
91 self.payload == other.payload
92 && self.encoded_len == other.encoded_len
93 && self.header_len == other.header_len
94 && self.item_count == other.item_count
95 && self.form == other.form
96 }
97}
98
99impl Eq for RlpList<'_> {}
100
101#[derive(Clone, Copy, Debug, Eq, PartialEq)]
103pub enum RlpItem<'a> {
104 Scalar(RlpScalar<'a>),
106 List(RlpList<'a>),
108}
109
110impl<'a> RlpItem<'a> {
111 #[must_use]
113 pub const fn encoded_len(self) -> usize {
114 match self {
115 Self::Scalar(scalar) => scalar.encoded_len(),
116 Self::List(list) => list.encoded_len(),
117 }
118 }
119
120 #[must_use]
122 pub const fn header_len(self) -> usize {
123 match self {
124 Self::Scalar(scalar) => scalar.header_len(),
125 Self::List(list) => list.header_len(),
126 }
127 }
128
129 #[must_use]
131 pub const fn is_scalar(self) -> bool {
132 matches!(self, Self::Scalar(_))
133 }
134
135 #[must_use]
137 pub const fn is_list(self) -> bool {
138 matches!(self, Self::List(_))
139 }
140
141 #[must_use]
143 pub const fn as_scalar(&self) -> Option<RlpScalar<'a>> {
144 match self {
145 Self::Scalar(scalar) => Some(*scalar),
146 Self::List(_) => None,
147 }
148 }
149
150 #[must_use]
152 pub const fn as_list(&self) -> Option<RlpList<'a>> {
153 match self {
154 Self::Scalar(_) => None,
155 Self::List(list) => Some(*list),
156 }
157 }
158}
159
160#[derive(Clone, Debug)]
162pub struct RlpListItems<'a> {
163 input: &'a [u8],
164 cursor: usize,
165 remaining: usize,
166 limits: DecodeLimits,
167}
168
169impl<'a> RlpListItems<'a> {
170 #[must_use]
172 pub const fn remaining(&self) -> usize {
173 self.remaining
174 }
175
176 #[cfg(test)]
178 pub(super) const fn for_test(input: &'a [u8], remaining: usize, limits: DecodeLimits) -> Self {
179 Self {
180 input,
181 cursor: 0,
182 remaining,
183 limits,
184 }
185 }
186}
187
188impl<'a> Iterator for RlpListItems<'a> {
189 type Item = Result<RlpItem<'a>, DecodeError>;
190
191 fn next(&mut self) -> Option<Self::Item> {
192 if self.remaining == 0 {
193 return None;
194 }
195
196 let result = parse_item(self.input, self.cursor, self.input.len())
197 .and_then(|item| item.into_rlp_item(self.input, self.cursor, self.limits))
198 .map(|(item, next_cursor)| {
199 self.cursor = next_cursor;
200 item
201 });
202 self.remaining = self.remaining.saturating_sub(1);
203
204 match result {
205 Ok(item) => Some(Ok(item)),
206 Err(error) => {
207 self.remaining = 0;
208 Some(Err(error))
209 }
210 }
211 }
212
213 fn size_hint(&self) -> (usize, Option<usize>) {
214 (self.remaining, Some(self.remaining))
215 }
216}
217
218impl ExactSizeIterator for RlpListItems<'_> {}
219
220impl core::iter::FusedIterator for RlpListItems<'_> {}
221
222pub fn decode_rlp_list<'a>(
224 input: &'a [u8],
225 limits: DecodeLimits,
226) -> Result<RlpList<'a>, DecodeError> {
227 let mut accumulator = limits.accumulator();
228 let list = decode_rlp_list_partial(input, &mut accumulator)?;
229 require_exact_consumption(list.encoded_len, input.len())?;
230 Ok(list)
231}
232
233pub fn decode_rlp_list_partial<'a>(
242 input: &'a [u8],
243 accumulator: &mut DecodeAccumulator,
244) -> Result<RlpList<'a>, DecodeError> {
245 accumulator.check_input_len(input.len())?;
246 accumulator.account_items(1)?;
247 accumulator.check_nesting_depth(1)?;
248
249 let prefix = *input.first().ok_or(DecodeError::Malformed)?;
250 let list = match prefix {
251 SHORT_LIST_OFFSET..=LONG_LIST_OFFSET => {
252 decode_short_list(input, prefix, accumulator.limits())?
253 }
254 0xf8..=0xff => decode_long_list(input, prefix, accumulator.limits())?,
255 0x00..=0xbf => return Err(DecodeError::UnexpectedScalar),
256 };
257 let item_count = validate_list_payload(list.payload, accumulator)?;
258 accumulator.check_list_count(item_count)?;
259 Ok(RlpList { item_count, ..list })
260}
261
262fn decode_short_list(
263 input: &[u8],
264 prefix: u8,
265 limits: DecodeLimits,
266) -> Result<RlpList<'_>, DecodeError> {
267 let payload_len = usize::from(prefix.saturating_sub(SHORT_LIST_OFFSET));
268 let payload = payload(input, 1, payload_len)?;
269 Ok(RlpList {
270 payload,
271 encoded_len: checked_len_add(1, payload_len)?,
272 header_len: 1,
273 item_count: 0,
274 form: RlpListForm::ShortList,
275 limits,
276 })
277}
278
279fn decode_long_list(
280 input: &[u8],
281 prefix: u8,
282 limits: DecodeLimits,
283) -> Result<RlpList<'_>, DecodeError> {
284 let len_of_len = usize::from(prefix.saturating_sub(LONG_LIST_OFFSET));
285 let payload_len = parse_payload_len(input, 1, len_of_len)?;
286 if payload_len <= SHORT_STRING_LIMIT {
287 return Err(DecodeError::Malformed);
288 }
289 let header_len = checked_len_add(1, len_of_len)?;
290 let payload = payload(input, header_len, payload_len)?;
291 Ok(RlpList {
292 payload,
293 encoded_len: checked_len_add(header_len, payload_len)?,
294 header_len,
295 item_count: 0,
296 form: RlpListForm::LongList,
297 limits,
298 })
299}
300
301#[derive(Clone, Copy)]
302struct ListFrame {
303 end: usize,
304 count: usize,
305}
306
307pub(super) fn validate_list_payload(
308 input: &[u8],
309 accumulator: &mut DecodeAccumulator,
310) -> Result<usize, DecodeError> {
311 let mut stack = [ListFrame { end: 0, count: 0 }; MAX_RLP_LIST_TRAVERSAL_DEPTH];
312 let root = stack.get_mut(0).ok_or(DecodeError::NestingTooDeep)?;
313 *root = ListFrame {
314 end: input.len(),
315 count: 0,
316 };
317 let mut depth = 1usize;
318 let mut cursor = 0usize;
319
320 loop {
321 let frame_index = depth.checked_sub(1).ok_or(DecodeError::LengthOverflow)?;
322 let frame = stack
323 .get_mut(frame_index)
324 .ok_or(DecodeError::NestingTooDeep)?;
325
326 if cursor == frame.end {
327 let finished_count = frame.count;
328 accumulator.check_list_count(finished_count)?;
329 depth = depth.checked_sub(1).ok_or(DecodeError::LengthOverflow)?;
330 if depth == 0 {
331 return Ok(finished_count);
332 }
333 continue;
334 }
335 if cursor > frame.end {
336 return Err(DecodeError::OffsetOutOfBounds);
337 }
338
339 frame.count = checked_len_add(frame.count, 1)?;
340 accumulator.check_list_count(frame.count)?;
341
342 let item = parse_item(input, cursor, frame.end)?;
343 accumulator.account_items(1)?;
344 if matches!(item.kind, ParsedItemKind::List(_)) {
345 let next_depth = checked_len_add(depth, 1)?;
346 accumulator.check_nesting_depth(next_depth)?;
347 if next_depth > MAX_RLP_LIST_TRAVERSAL_DEPTH {
348 return Err(DecodeError::NestingTooDeep);
349 }
350 let child_index = next_depth
351 .checked_sub(1)
352 .ok_or(DecodeError::LengthOverflow)?;
353 let child = stack
354 .get_mut(child_index)
355 .ok_or(DecodeError::NestingTooDeep)?;
356 *child = ListFrame {
357 end: item.payload_end,
358 count: 0,
359 };
360 depth = next_depth;
361 cursor = item.payload_start;
362 } else {
363 cursor = item.item_end;
364 }
365 }
366}
367
368struct ParsedItem {
369 kind: ParsedItemKind,
370 header_len: usize,
371 payload_start: usize,
372 payload_end: usize,
373 item_end: usize,
374}
375
376impl ParsedItem {
377 fn into_rlp_item<'a>(
378 self,
379 input: &'a [u8],
380 offset: usize,
381 limits: DecodeLimits,
382 ) -> Result<(RlpItem<'a>, usize), DecodeError> {
383 let payload = input
384 .get(self.payload_start..self.payload_end)
385 .ok_or(DecodeError::OffsetOutOfBounds)?;
386 let encoded_len = self
387 .item_end
388 .checked_sub(offset)
389 .ok_or(DecodeError::LengthOverflow)?;
390 let item = match self.kind {
391 ParsedItemKind::Scalar(form) => RlpItem::Scalar(RlpScalar {
392 payload,
393 encoded_len,
394 header_len: self.header_len,
395 form,
396 }),
397 ParsedItemKind::List(form) => RlpItem::List(RlpList {
398 payload,
399 encoded_len,
400 header_len: self.header_len,
401 item_count: count_immediate_items(payload, limits)?,
402 form,
403 limits,
404 }),
405 };
406 Ok((item, self.item_end))
407 }
408}
409
410#[derive(Clone, Copy)]
411enum ParsedItemKind {
412 Scalar(RlpScalarForm),
413 List(RlpListForm),
414}
415
416fn parse_item(
417 input: &[u8],
418 offset: usize,
419 container_end: usize,
420) -> Result<ParsedItem, DecodeError> {
421 let local = input
422 .get(offset..container_end)
423 .ok_or(DecodeError::OffsetOutOfBounds)?;
424 let prefix = *local.first().ok_or(DecodeError::Malformed)?;
425 let (kind, header_len, payload_len) = match prefix {
426 0x00..=0x7f => (ParsedItemKind::Scalar(RlpScalarForm::SingleByte), 0, 1),
427 super::SHORT_STRING_OFFSET..=super::LONG_STRING_OFFSET => {
428 let scalar = decode_short_string(local, prefix)?;
429 (
430 ParsedItemKind::Scalar(scalar.form()),
431 scalar.header_len(),
432 scalar.payload().len(),
433 )
434 }
435 0xb8..=0xbf => {
436 let scalar = decode_long_string(local, prefix)?;
437 (
438 ParsedItemKind::Scalar(scalar.form()),
439 scalar.header_len(),
440 scalar.payload().len(),
441 )
442 }
443 SHORT_LIST_OFFSET..=LONG_LIST_OFFSET => {
444 let payload_len = usize::from(prefix.saturating_sub(SHORT_LIST_OFFSET));
445 payload(local, 1, payload_len)?;
446 (ParsedItemKind::List(RlpListForm::ShortList), 1, payload_len)
447 }
448 0xf8..=0xff => {
449 let len_of_len = usize::from(prefix.saturating_sub(LONG_LIST_OFFSET));
450 let payload_len = parse_payload_len(local, 1, len_of_len)?;
451 if payload_len <= SHORT_STRING_LIMIT {
452 return Err(DecodeError::Malformed);
453 }
454 let header_len = checked_len_add(1, len_of_len)?;
455 payload(local, header_len, payload_len)?;
456 (
457 ParsedItemKind::List(RlpListForm::LongList),
458 header_len,
459 payload_len,
460 )
461 }
462 };
463 let payload_start = checked_len_add(offset, header_len)?;
464 let payload_end = checked_len_add(payload_start, payload_len)?;
465 let item_end = if header_len == 0 {
466 checked_len_add(offset, 1)?
467 } else {
468 payload_end
469 };
470 require_range_in_bounds(offset, item_end.saturating_sub(offset), container_end)?;
471 Ok(ParsedItem {
472 kind,
473 header_len,
474 payload_start,
475 payload_end,
476 item_end,
477 })
478}
479
480fn count_immediate_items(input: &[u8], limits: DecodeLimits) -> Result<usize, DecodeError> {
481 let mut accumulator = limits.accumulator();
484 accumulator.check_input_len(input.len())?;
485 let mut count = 0usize;
486 let mut cursor = 0usize;
487 while cursor < input.len() {
488 let item = parse_item(input, cursor, input.len())?;
489 count = checked_len_add(count, 1)?;
490 accumulator.account_items(1)?;
491 accumulator.check_list_count(count)?;
492 cursor = item.item_end;
493 }
494 if cursor == input.len() {
495 Ok(count)
496 } else {
497 Err(DecodeError::OffsetOutOfBounds)
498 }
499}