Skip to main content

revm_precompile/
utilities.rs

1//! Utility function that precompiles use, padding and converting between types.
2use primitives::{b256, Bytes, B256};
3use std::borrow::Cow;
4
5/// Right-pads the given slice at `offset` with zeroes until `LEN`.
6///
7/// Returns the first `LEN` bytes if it does not need padding.
8#[inline]
9pub fn right_pad_with_offset<const LEN: usize>(data: &[u8], offset: usize) -> Cow<'_, [u8; LEN]> {
10    right_pad(data.get(offset..).unwrap_or_default())
11}
12
13/// Right-pads the given slice at `offset` with zeroes until `len`.
14///
15/// Returns the first `len` bytes if it does not need padding.
16#[inline]
17pub fn right_pad_with_offset_vec(data: &[u8], offset: usize, len: usize) -> Cow<'_, [u8]> {
18    right_pad_vec(data.get(offset..).unwrap_or_default(), len)
19}
20
21/// Right-pads the given slice with zeroes until `LEN`.
22///
23/// Returns the first `LEN` bytes if it does not need padding.
24#[inline]
25pub fn right_pad<const LEN: usize>(data: &[u8]) -> Cow<'_, [u8; LEN]> {
26    if let Some(data) = data.get(..LEN) {
27        Cow::Borrowed(data.try_into().unwrap())
28    } else {
29        let mut padded = [0; LEN];
30        padded[..data.len()].copy_from_slice(data);
31        Cow::Owned(padded)
32    }
33}
34
35/// Right-pads the given slice with zeroes until `len`.
36///
37/// Returns the first `len` bytes if it does not need padding.
38#[inline]
39pub fn right_pad_vec(data: &[u8], len: usize) -> Cow<'_, [u8]> {
40    if let Some(data) = data.get(..len) {
41        Cow::Borrowed(data)
42    } else {
43        let mut padded = vec![0; len];
44        padded[..data.len()].copy_from_slice(data);
45        Cow::Owned(padded)
46    }
47}
48
49/// Left-pads the given slice with zeroes until `LEN`.
50///
51/// Returns the first `LEN` bytes if it does not need padding.
52#[inline]
53pub fn left_pad<const LEN: usize>(data: &[u8]) -> Cow<'_, [u8; LEN]> {
54    if let Some(data) = data.get(..LEN) {
55        Cow::Borrowed(data.try_into().unwrap())
56    } else {
57        let mut padded = [0; LEN];
58        padded[LEN - data.len()..].copy_from_slice(data);
59        Cow::Owned(padded)
60    }
61}
62
63/// Left-pads the given slice with zeroes until `len`.
64///
65/// Returns the first `len` bytes if it does not need padding.
66#[inline]
67pub fn left_pad_vec(data: &[u8], len: usize) -> Cow<'_, [u8]> {
68    if let Some(data) = data.get(..len) {
69        Cow::Borrowed(data)
70    } else {
71        let mut padded = vec![0; len];
72        padded[len - data.len()..].copy_from_slice(data);
73        Cow::Owned(padded)
74    }
75}
76
77/// Left-pads the given big-endian slice with zeroes until `len`.
78///
79/// Unlike [`left_pad_vec`], when `data` is longer than `len` this correctly
80/// truncates leading (most-significant) bytes instead of trailing ones.
81#[inline]
82pub fn left_pad_vec_be(data: &[u8], len: usize) -> Cow<'_, [u8]> {
83    if data.len() < len {
84        let mut padded = vec![0; len];
85        padded[len - data.len()..].copy_from_slice(data);
86        Cow::Owned(padded)
87    } else {
88        // Truncate leading bytes (data is big-endian).
89        Cow::Borrowed(&data[data.len() - len..])
90    }
91}
92
93/// Converts a boolean to a left-padded 32-byte [`Bytes`] value.
94///
95/// This is optimized to not allocate at runtime by using 2 static arrays.
96#[inline]
97pub const fn bool_to_bytes32(value: bool) -> Bytes {
98    Bytes::from_static(&bool_to_b256(value).0)
99}
100
101/// Converts a boolean to a left-padded [`B256`] value.
102///
103/// This is optimized to not allocate at runtime by using 2 static arrays.
104#[inline]
105pub const fn bool_to_b256(value: bool) -> &'static B256 {
106    const TRUE: &B256 =
107        &b256!("0x0000000000000000000000000000000000000000000000000000000000000001");
108    const FALSE: &B256 =
109        &b256!("0x0000000000000000000000000000000000000000000000000000000000000000");
110    if value {
111        TRUE
112    } else {
113        FALSE
114    }
115}
116
117#[cfg(test)]
118mod tests {
119    use super::*;
120
121    #[test]
122    fn get_with_right_padding() {
123        let data = [1, 2, 3, 4];
124        let padded = right_pad_with_offset::<8>(&data, 4);
125        assert!(matches!(padded, Cow::Owned(_)));
126        assert_eq!(padded[..], [0, 0, 0, 0, 0, 0, 0, 0]);
127        let padded = right_pad_with_offset_vec(&data, 4, 8);
128        assert!(matches!(padded, Cow::Owned(_)));
129        assert_eq!(padded[..], [0, 0, 0, 0, 0, 0, 0, 0]);
130
131        let data = [1, 2, 3, 4, 5, 6, 7, 8];
132        let padded = right_pad_with_offset::<8>(&data, 0);
133        assert!(matches!(padded, Cow::Borrowed(_)));
134        assert_eq!(padded[..], [1, 2, 3, 4, 5, 6, 7, 8]);
135        let padded = right_pad_with_offset_vec(&data, 0, 8);
136        assert!(matches!(padded, Cow::Borrowed(_)));
137        assert_eq!(padded[..], [1, 2, 3, 4, 5, 6, 7, 8]);
138
139        let data = [1, 2, 3, 4, 5, 6, 7, 8];
140        let padded = right_pad_with_offset::<8>(&data, 4);
141        assert!(matches!(padded, Cow::Owned(_)));
142        assert_eq!(padded[..], [5, 6, 7, 8, 0, 0, 0, 0]);
143        let padded = right_pad_with_offset_vec(&data, 4, 8);
144        assert!(matches!(padded, Cow::Owned(_)));
145        assert_eq!(padded[..], [5, 6, 7, 8, 0, 0, 0, 0]);
146    }
147
148    #[test]
149    fn right_padding() {
150        let data = [1, 2, 3, 4];
151        let padded = right_pad::<8>(&data);
152        assert!(matches!(padded, Cow::Owned(_)));
153        assert_eq!(padded[..], [1, 2, 3, 4, 0, 0, 0, 0]);
154        let padded = right_pad_vec(&data, 8);
155        assert!(matches!(padded, Cow::Owned(_)));
156        assert_eq!(padded[..], [1, 2, 3, 4, 0, 0, 0, 0]);
157
158        let data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
159        let padded = right_pad::<8>(&data);
160        assert!(matches!(padded, Cow::Borrowed(_)));
161        assert_eq!(padded[..], [1, 2, 3, 4, 5, 6, 7, 8]);
162        let padded = right_pad_vec(&data, 8);
163        assert!(matches!(padded, Cow::Borrowed(_)));
164        assert_eq!(padded[..], [1, 2, 3, 4, 5, 6, 7, 8]);
165    }
166
167    #[test]
168    fn left_padding() {
169        let data = [1, 2, 3, 4];
170        let padded = left_pad::<8>(&data);
171        assert!(matches!(padded, Cow::Owned(_)));
172        assert_eq!(padded[..], [0, 0, 0, 0, 1, 2, 3, 4]);
173        let padded = left_pad_vec(&data, 8);
174        assert!(matches!(padded, Cow::Owned(_)));
175        assert_eq!(padded[..], [0, 0, 0, 0, 1, 2, 3, 4]);
176
177        let data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
178        let padded = left_pad::<8>(&data);
179        assert!(matches!(padded, Cow::Borrowed(_)));
180        assert_eq!(padded[..], [1, 2, 3, 4, 5, 6, 7, 8]);
181        let padded = left_pad_vec(&data, 8);
182        assert!(matches!(padded, Cow::Borrowed(_)));
183        assert_eq!(padded[..], [1, 2, 3, 4, 5, 6, 7, 8]);
184    }
185
186    #[test]
187    fn left_padding_be() {
188        let data = [1, 2, 3, 4];
189        let padded = left_pad_vec_be(&data, 8);
190        assert!(matches!(padded, Cow::Owned(_)));
191        assert_eq!(padded[..], [0, 0, 0, 0, 1, 2, 3, 4]);
192
193        let data = [0, 0, 1, 2, 3, 4];
194        let padded = left_pad_vec_be(&data, 4);
195        assert!(matches!(padded, Cow::Borrowed(_)));
196        assert_eq!(padded[..], [1, 2, 3, 4]);
197
198        let data = [1, 2, 3, 4];
199        let padded = left_pad_vec_be(&data, 4);
200        assert!(matches!(padded, Cow::Borrowed(_)));
201        assert_eq!(padded[..], [1, 2, 3, 4]);
202    }
203
204    #[test]
205    fn bool2bytes() {
206        let f = bool_to_bytes32(false);
207        assert_eq!(f[..], [0; 32]);
208        let t = bool_to_bytes32(true);
209        assert_eq!(t.len(), 32);
210        assert_eq!(t[..31], [0; 31]);
211        assert_eq!(t[31], 1);
212    }
213}