ab_transaction/owned/
builder_buffer.rs

1use crate::owned::{OwnedTransactionBuilderError, OwnedTransactionLengths};
2use crate::{TransactionHeader, TransactionSlot};
3use ab_aligned_buffer::OwnedAlignedBuffer;
4use ab_contracts_io_type::trivial_type::TrivialType;
5use core::slice;
6
7#[derive(Debug, Clone)]
8pub(super) struct BuilderBuffer {
9    buffer: OwnedAlignedBuffer,
10}
11
12impl BuilderBuffer {
13    pub(super) fn new(header: &TransactionHeader) -> Self {
14        const _: () = {
15            // Writing `OwnedTransactionLengths` after `TransactionHeader` must be aligned
16            assert!(size_of::<TransactionHeader>() % align_of::<OwnedTransactionLengths>() == 0);
17        };
18        let mut buffer = OwnedAlignedBuffer::with_capacity(
19            TransactionHeader::SIZE + OwnedTransactionLengths::SIZE,
20        );
21        // Always fits into `u32`
22        let _: bool = buffer.append(header.as_bytes());
23        // Always fits into `u32`
24        let _: bool = buffer.append(OwnedTransactionLengths::default().as_bytes());
25        Self { buffer }
26    }
27
28    fn get_lengths(&mut self) -> &mut OwnedTransactionLengths {
29        unsafe {
30            self.buffer
31                .as_mut_ptr()
32                .add(size_of::<TransactionHeader>())
33                .cast::<OwnedTransactionLengths>()
34                .as_mut_unchecked()
35        }
36    }
37
38    pub(super) fn append_read_slots(
39        &mut self,
40        slots: &[TransactionSlot],
41    ) -> Result<(), OwnedTransactionBuilderError> {
42        {
43            let lengths = self.get_lengths();
44            let Ok(slots_len) = u16::try_from(slots.len()) else {
45                return Err(OwnedTransactionBuilderError::TooManyReadSlots);
46            };
47            let Some(new_read_slots) = lengths.read_slots.checked_add(slots_len) else {
48                return Err(OwnedTransactionBuilderError::TooManyReadSlots);
49            };
50
51            lengths.read_slots = new_read_slots;
52        }
53
54        const _: () = {
55            // Writing `TransactionSlot` after `OwnedTransactionLengths` and `TransactionHeader`
56            // must be aligned
57            assert!(
58                (size_of::<TransactionHeader>() + size_of::<OwnedTransactionLengths>())
59                    % align_of::<TransactionSlot>()
60                    == 0
61            );
62        };
63        // SAFETY: `TransactionSlot` implements `TrivialType` and is safe to copy as bytes, it is
64        // also correctly aligned due to check above
65        if !self.buffer.append(unsafe {
66            slice::from_raw_parts(slots.as_ptr().cast::<u8>(), size_of_val(slots))
67        }) {
68            return Err(OwnedTransactionBuilderError::TransactionTooLarge);
69        }
70
71        Ok(())
72    }
73
74    pub(super) fn append_write_slots(
75        &mut self,
76        slots: &[TransactionSlot],
77    ) -> Result<(), OwnedTransactionBuilderError> {
78        {
79            let lengths = self.get_lengths();
80            let Ok(slots_len) = u16::try_from(slots.len()) else {
81                return Err(OwnedTransactionBuilderError::TooManyWriteSlots);
82            };
83            let Some(new_write_slots) = lengths.write_slots.checked_add(slots_len) else {
84                return Err(OwnedTransactionBuilderError::TooManyWriteSlots);
85            };
86
87            lengths.write_slots = new_write_slots;
88        }
89
90        const _: () = {
91            // Writing `TransactionSlot` after `OwnedTransactionLengths` and `TransactionHeader`
92            // must be aligned
93            assert!(
94                (size_of::<TransactionHeader>() + size_of::<OwnedTransactionLengths>())
95                    % align_of::<TransactionSlot>()
96                    == 0
97            );
98        };
99        // SAFETY: `TransactionSlot` implements `TrivialType` and is safe to copy as bytes, it is
100        // also correctly aligned due to check above
101        if !self.buffer.append(unsafe {
102            slice::from_raw_parts(slots.as_ptr().cast::<u8>(), size_of_val(slots))
103        }) {
104            return Err(OwnedTransactionBuilderError::TransactionTooLarge);
105        }
106
107        Ok(())
108    }
109
110    pub(super) fn append_payload(
111        &mut self,
112        payload: &[u8],
113    ) -> Result<(), OwnedTransactionBuilderError> {
114        {
115            let lengths = self.get_lengths();
116            let Ok(payload_len) = u32::try_from(payload.len()) else {
117                return Err(OwnedTransactionBuilderError::PayloadTooLarge);
118            };
119            let Some(new_payload_len) = lengths.payload.checked_add(payload_len) else {
120                return Err(OwnedTransactionBuilderError::PayloadTooLarge);
121            };
122
123            lengths.payload = new_payload_len;
124        }
125
126        const _: () = {
127            // Writing after `OwnedTransactionLengths`, `TransactionHeader` and (optionally)
128            // `TransactionSlot` must be aligned to `u128`
129            assert!(
130                (size_of::<TransactionHeader>() + size_of::<OwnedTransactionLengths>())
131                    % align_of::<u128>()
132                    == 0
133            );
134            assert!(
135                (size_of::<TransactionHeader>()
136                    + size_of::<OwnedTransactionLengths>()
137                    + size_of::<TransactionSlot>())
138                    % align_of::<u128>()
139                    == 0
140            );
141        };
142        if !self.buffer.append(payload) {
143            return Err(OwnedTransactionBuilderError::TransactionTooLarge);
144        }
145
146        Ok(())
147    }
148
149    pub(super) fn append_seal(&mut self, seal: &[u8]) -> Result<(), OwnedTransactionBuilderError> {
150        {
151            let lengths = self.get_lengths();
152            if lengths.payload % u128::SIZE != 0 {
153                return Err(OwnedTransactionBuilderError::PayloadIsNotMultipleOfU128);
154            }
155            let Ok(seal_len) = u32::try_from(seal.len()) else {
156                return Err(OwnedTransactionBuilderError::SealTooLarge);
157            };
158            let Some(new_seal_len) = lengths.seal.checked_add(seal_len) else {
159                return Err(OwnedTransactionBuilderError::SealTooLarge);
160            };
161
162            lengths.seal = new_seal_len;
163        }
164
165        const _: () = {
166            // Writing after `OwnedTransactionLengths`, `TransactionHeader` and (optionally)
167            // `TransactionSlot` must be aligned to `u128`
168            assert!(
169                (size_of::<TransactionHeader>() + size_of::<OwnedTransactionLengths>())
170                    % align_of::<u128>()
171                    == 0
172            );
173            assert!(
174                (size_of::<TransactionHeader>()
175                    + size_of::<OwnedTransactionLengths>()
176                    + size_of::<TransactionSlot>())
177                    % align_of::<u128>()
178                    == 0
179            );
180        };
181        if !self.buffer.append(seal) {
182            return Err(OwnedTransactionBuilderError::TransactionTooLarge);
183        }
184
185        Ok(())
186    }
187
188    pub(super) fn finish(mut self) -> Result<OwnedAlignedBuffer, OwnedTransactionBuilderError> {
189        let lengths = self.get_lengths();
190        if lengths.payload % u128::SIZE != 0 {
191            return Err(OwnedTransactionBuilderError::PayloadIsNotMultipleOfU128);
192        }
193        Ok(self.buffer)
194    }
195}