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