ink_primitives/sol/
encoder.rs

1// Copyright (C) ink! contributors.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15/// A Solidity ABI encoder.
16//
17// # Design Notes
18//
19// In contrast to `alloy_sol_types::abi::Encoder`, this implementation is non-allocating.
20//
21// Note though, that this non-allocating claim is about the encoder itself, not the
22// representations of the types it encodes (i.e. types with allocating representations
23// like `Vec<T>` inherently allocate).
24pub struct Encoder<'enc> {
25    /// The head buffer.
26    head: &'enc mut [u8],
27    /// The (segmented) tail buffer.
28    tail: Option<&'enc mut [u8]>,
29    /// The head offset.
30    head_offset: usize,
31    /// The tail offset.
32    tail_offset: usize,
33}
34
35impl<'enc> Encoder<'enc> {
36    /// Creates an encoder from a mutable byte slice.
37    pub fn new(buffer: &'enc mut [u8]) -> Self {
38        Self {
39            head: buffer,
40            tail: None,
41            head_offset: 0,
42            tail_offset: 0,
43        }
44    }
45
46    /// Appends a word.
47    pub fn append_word(&mut self, word: [u8; 32]) {
48        debug_assert_eq!(self.head_offset % 32, 0);
49        let next_offset = self.head_offset.checked_add(32).unwrap();
50        self.head[self.head_offset..next_offset].copy_from_slice(word.as_slice());
51        debug_assert_eq!(next_offset % 32, 0);
52        self.head_offset = next_offset;
53    }
54
55    /// Appends bytes.
56    pub fn append_bytes(&mut self, bytes: &[u8]) {
57        debug_assert_eq!(self.head_offset % 32, 0);
58        if bytes.is_empty() {
59            return;
60        }
61        let end_offset = self.head_offset.checked_add(bytes.len()).unwrap();
62        self.head[self.head_offset..end_offset].copy_from_slice(bytes);
63        let next_offset = match end_offset % 32 {
64            0 => end_offset,
65            r => {
66                let pad_len = 32 - r;
67                let next_offset = end_offset.checked_add(pad_len).unwrap();
68                self.head[end_offset..next_offset].fill(0u8);
69                next_offset
70            }
71        };
72        debug_assert_eq!(next_offset % 32, 0);
73        self.head_offset = next_offset;
74    }
75
76    /// Appends offset.
77    ///
78    /// # Note
79    ///
80    /// This method should be called after segmenting the buffer using [`Self::segment`].
81    pub fn append_offset(&mut self) {
82        debug_assert!(self.tail.is_some());
83        debug_assert_eq!(self.tail_offset % 32, 0);
84        // The "overall" offset for dynamic data combines the head length and current
85        // offset in the tail buffer.
86        let offset = self.head.len().checked_add(self.tail_offset).unwrap();
87        self.append_as_be_bytes(offset);
88    }
89
90    /// Appends length of a sequence.
91    pub fn append_length(&mut self, len: usize) {
92        self.append_as_be_bytes(len);
93    }
94
95    /// Segments the buffer into a head and tail, with the head taking the next `n` words.
96    pub fn segment(&mut self, n_words: usize) -> Encoder<'_> {
97        debug_assert_eq!(self.head_offset % 32, 0);
98        let (_, buffer) = self.head.split_at_mut(self.head_offset);
99        let (head, tail) = buffer.split_at_mut(n_words.checked_mul(32).unwrap());
100        Encoder {
101            head,
102            tail: Some(tail),
103            head_offset: 0,
104            tail_offset: 0,
105        }
106    }
107
108    /// Takes `n` words from the tail buffer.
109    ///
110    /// # Note
111    ///
112    /// This method must be called after segmenting the buffer using [`Self::segment`].
113    ///
114    /// # Panics
115    ///
116    /// Panics if the buffer isn't segmented.
117    pub fn take_tail(&mut self, n_words: usize) -> Encoder<'_> {
118        let tail = core::mem::take(&mut self.tail)
119            .expect("Expected a segmented buffer, call `Self::segment` first");
120        let len = n_words.checked_mul(32).unwrap();
121        let (target, rest) = tail.split_at_mut(len);
122        self.tail = Some(rest);
123        self.tail_offset = self.tail_offset.checked_add(len).unwrap();
124        debug_assert_eq!(self.tail_offset % 32, 0);
125        Encoder::new(target)
126    }
127
128    /// Fills the next `n` words with the given value.
129    pub fn fill(&mut self, value: u8, n_words: usize) {
130        debug_assert_eq!(self.head_offset % 32, 0);
131        let end_offset = self
132            .head_offset
133            .checked_add(n_words.checked_mul(32).unwrap())
134            .unwrap();
135        self.head[self.head_offset..end_offset].fill(value);
136        self.head_offset = end_offset;
137    }
138
139    /// Appends the big endian bytes for value (e.g. an offset or length).
140    fn append_as_be_bytes(&mut self, len: usize) {
141        debug_assert_eq!(self.head_offset % 32, 0);
142        let bytes = len.to_be_bytes();
143        // `usize` can't theoretically be any larger than 128 bits (16 bytes),
144        // and practically it's never more than 64 bits (8 bytes).
145        let end_offset = self.head_offset.checked_add(32).unwrap();
146        let start_offset = end_offset.checked_sub(bytes.len()).unwrap();
147        self.head[self.head_offset..start_offset].fill(0);
148        self.head[start_offset..end_offset].copy_from_slice(bytes.as_slice());
149        debug_assert_eq!(end_offset % 32, 0);
150        self.head_offset = end_offset;
151    }
152}