alloy_sol_types/
utils.rs

1// Copyright 2015-2020 Parity Technologies
2// Copyright 2023-2023 Alloy Contributors
3//
4// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7// option. This file may not be copied, modified, or distributed
8// except according to those terms.
9
10//! Utilities used by different modules.
11
12use crate::{Error, Result, Word};
13
14const USIZE_BYTES: usize = usize::BITS as usize / 8;
15
16/// Calculates the padded length of a slice by rounding its length to the next
17/// word.
18#[inline(always)]
19pub const fn words_for(data: &[u8]) -> usize {
20    words_for_len(data.len())
21}
22
23/// Calculates the padded length of a slice of a specific length by rounding its
24/// length to the next word.
25#[inline(always)]
26#[allow(clippy::manual_div_ceil)] // `.div_ceil` has worse codegen: https://godbolt.org/z/MenKWfPh9
27pub const fn words_for_len(len: usize) -> usize {
28    (len + 31) / 32
29}
30
31/// `padded_len` rounds a slice length up to the next multiple of 32
32#[inline(always)]
33pub(crate) const fn padded_len(data: &[u8]) -> usize {
34    next_multiple_of_32(data.len())
35}
36
37/// See [`usize::next_multiple_of`].
38#[inline(always)]
39pub const fn next_multiple_of_32(n: usize) -> usize {
40    match n % 32 {
41        0 => n,
42        r => n + (32 - r),
43    }
44}
45
46/// Left-pads a `usize` to 32 bytes.
47#[inline]
48pub(crate) fn pad_usize(value: usize) -> Word {
49    let mut padded = Word::ZERO;
50    padded[32 - USIZE_BYTES..32].copy_from_slice(&value.to_be_bytes());
51    padded
52}
53
54/// Returns `Ok(())`. Exists for the [`define_udt!`](crate::define_udt!)'s
55/// typecheck.
56#[doc(hidden)]
57#[inline]
58pub const fn just_ok<T>(_: &T) -> crate::Result<()> {
59    Ok(())
60}
61
62#[inline]
63pub(crate) fn check_zeroes(data: &[u8]) -> bool {
64    data.iter().all(|b| *b == 0)
65}
66
67#[inline]
68pub(crate) fn as_offset(word: &Word, validate: bool) -> Result<usize> {
69    let (before, data) = word.split_at(32 - USIZE_BYTES);
70    if validate && !check_zeroes(before) {
71        return Err(Error::type_check_fail(&word[..], "offset (usize)"));
72    }
73    Ok(usize::from_be_bytes(<[u8; USIZE_BYTES]>::try_from(data).unwrap()))
74}
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79    use alloy_primitives::b256;
80
81    #[test]
82    fn test_words_for() {
83        assert_eq!(words_for(&[]), 0);
84        assert_eq!(words_for(&[0; 31]), 1);
85        assert_eq!(words_for(&[0; 32]), 1);
86        assert_eq!(words_for(&[0; 33]), 2);
87    }
88
89    #[test]
90    fn test_pad_u32() {
91        // this will fail if endianness is not supported
92        assert_eq!(
93            pad_usize(0),
94            b256!("0x0000000000000000000000000000000000000000000000000000000000000000")
95        );
96        assert_eq!(
97            pad_usize(1),
98            b256!("0x0000000000000000000000000000000000000000000000000000000000000001")
99        );
100        assert_eq!(
101            pad_usize(0x100),
102            b256!("0x0000000000000000000000000000000000000000000000000000000000000100")
103        );
104        assert_eq!(
105            pad_usize(0xffffffff),
106            b256!("0x00000000000000000000000000000000000000000000000000000000ffffffff")
107        );
108    }
109}