eth_valkyoth_codec/
lib.rs1#![no_std]
2#![forbid(unsafe_code)]
3use core::cmp::Ordering;
6
7#[derive(Clone, Copy, Debug, Eq, PartialEq)]
9pub struct DecodeLimits {
10 pub max_input_bytes: usize,
12 pub max_list_items: usize,
14 pub max_nesting_depth: usize,
16 pub max_total_allocation: usize,
18}
19
20impl DecodeLimits {
21 pub const STRICT: Self = Self {
23 max_input_bytes: 1 << 20,
24 max_list_items: 4096,
25 max_nesting_depth: 64,
26 max_total_allocation: 1 << 20,
27 };
28
29 pub fn check_input_len(self, len: usize) -> Result<(), DecodeError> {
31 if len > self.max_input_bytes {
32 return Err(DecodeError::InputTooLarge);
33 }
34 Ok(())
35 }
36
37 pub fn check_list_count(self, count: usize) -> Result<(), DecodeError> {
39 if count > self.max_list_items {
40 return Err(DecodeError::ListTooLong);
41 }
42 Ok(())
43 }
44
45 pub fn check_nesting_depth(self, depth: usize) -> Result<(), DecodeError> {
47 if depth > self.max_nesting_depth {
48 return Err(DecodeError::NestingTooDeep);
49 }
50 Ok(())
51 }
52
53 pub fn check_allocation(self, size: usize) -> Result<(), DecodeError> {
55 if size > self.max_total_allocation {
56 return Err(DecodeError::AllocationExceeded);
57 }
58 Ok(())
59 }
60}
61
62#[derive(Clone, Copy, Debug, Eq, PartialEq)]
64pub enum DecodeError {
65 InputTooLarge,
67 TrailingBytes,
69 DecoderOverread,
71 Malformed,
73 ListTooLong,
75 NestingTooDeep,
77 AllocationExceeded,
79}
80
81pub fn require_exact_consumption(consumed: usize, input_len: usize) -> Result<(), DecodeError> {
83 match consumed.cmp(&input_len) {
84 Ordering::Equal => Ok(()),
85 Ordering::Less => Err(DecodeError::TrailingBytes),
86 Ordering::Greater => Err(DecodeError::DecoderOverread),
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93
94 #[test]
95 fn rejects_oversized_input() {
96 let limits = DecodeLimits {
97 max_input_bytes: 2,
98 ..DecodeLimits::STRICT
99 };
100 assert_eq!(limits.check_input_len(3), Err(DecodeError::InputTooLarge));
101 }
102
103 #[test]
104 fn rejects_oversized_list() {
105 let limits = DecodeLimits {
106 max_list_items: 2,
107 ..DecodeLimits::STRICT
108 };
109 assert_eq!(limits.check_list_count(3), Err(DecodeError::ListTooLong));
110 }
111
112 #[test]
113 fn rejects_excessive_nesting_depth() {
114 let limits = DecodeLimits {
115 max_nesting_depth: 2,
116 ..DecodeLimits::STRICT
117 };
118 assert_eq!(
119 limits.check_nesting_depth(3),
120 Err(DecodeError::NestingTooDeep)
121 );
122 }
123
124 #[test]
125 fn rejects_excessive_allocation() {
126 let limits = DecodeLimits {
127 max_total_allocation: 2,
128 ..DecodeLimits::STRICT
129 };
130 assert_eq!(
131 limits.check_allocation(3),
132 Err(DecodeError::AllocationExceeded)
133 );
134 }
135
136 #[test]
137 fn detects_trailing_bytes() {
138 assert_eq!(
139 require_exact_consumption(1, 2),
140 Err(DecodeError::TrailingBytes)
141 );
142 }
143
144 #[test]
145 fn detects_decoder_overread() {
146 assert_eq!(
147 require_exact_consumption(3, 2),
148 Err(DecodeError::DecoderOverread)
149 );
150 }
151}