Skip to main content

xdrs/txs/
put.rs

1//! Provide a generic TXS implementation for any type
2//! that implements some basic byte operations
3
4#![allow(clippy::upper_case_acronyms)]
5
6use crate::serialize::writer::*;
7use crate::serialize::writer_unchecked::*;
8
9/// Implement this basic byte operation in order
10/// to get a `Writer<TXS>` implementation for free
11pub(crate) trait Put {
12    // fn put_byte(&mut self, b: u8) -> Result<()>;
13    fn put_bytes(&mut self, bytes: &[u8]) -> WriteResult<()>;
14
15    // Per benchmarks, putting an opaque vec using just put_bytes is quite expensiver
16    // (and somewhat surprisingly, much slower than the equivalent on CXS).
17    // As previously implemented with put_bytes(), it was 3 put_bytes calls
18    // (header, value, padding).
19    fn put_opaque_variable(&mut self, bytes: &[u8]) -> WriteResult<()>;
20
21    // put_opaque_fixed is similarly expensive
22    fn put_opaque_fixed(&mut self, bytes: &[u8]) -> WriteResult<()>;
23}
24
25pub struct TXS;
26
27/// The number of zero bytes that pad an opaque of `len` bytes to a 4-byte
28/// boundary.
29#[inline]
30fn padding_len(len: usize) -> usize {
31    (4 - len % 4) % 4
32}
33
34static PADDING: [u8; 3] = [0; 3];
35
36macro_rules! write_int {
37    [ $($write:ident($nbytes:expr): $ty:ty),* $(,)? ] => {
38
39    $(
40        #[inline]
41        fn $write(&mut self, n: $ty) -> WriteResult<()> {
42            let bytes: [u8; $nbytes] = n.to_be_bytes();
43            self.put_bytes(&bytes)
44        }
45    )*
46
47    };
48}
49
50impl<P> Writer<TXS> for P
51where
52    P: Put,
53{
54    #[inline]
55    fn write_opaque_fixed(&mut self, bytes: &[u8]) -> WriteResult<()> {
56        debug_assert!(bytes.len() <= u32::MAX as usize);
57        self.put_opaque_fixed(bytes)
58    }
59
60    #[inline]
61    fn write_opaque_unpadded(&mut self, bytes: &[u8]) -> WriteResult<()> {
62        self.put_bytes(bytes)
63    }
64
65    #[inline]
66    fn write_opaque_padding(&mut self, len: usize) -> WriteResult<()> {
67        self.put_bytes(&PADDING[..padding_len(len)])
68    }
69
70    #[inline]
71    fn write_opaque_variable(&mut self, bytes: &[u8]) -> WriteResult<()> {
72        if bytes.len() > u32::MAX as usize {
73            return Err(WriteError::InvalidArrayLength);
74        }
75
76        self.put_opaque_variable(bytes)
77    }
78
79    #[inline]
80    fn write_bool(&mut self, v: bool) -> WriteResult<()> {
81        self.write_i32(if v { 1 } else { 0 })
82    }
83
84    #[inline]
85    fn write_i16(&mut self, n: i16) -> WriteResult<()> {
86        self.write_i32(n as i32)
87    }
88
89    #[inline]
90    fn write_u16(&mut self, n: u16) -> WriteResult<()> {
91        self.write_u32(n as u32)
92    }
93
94    write_int! [
95        write_i64(8): i64,
96        write_i32(4): i32,
97
98        write_u64(8): u64,
99        write_u32(4): u32,
100    ];
101
102    #[inline]
103    fn write_f32(&mut self, n: f32) -> WriteResult<()> {
104        let bytes: [u8; 4] = n.to_be_bytes();
105        self.put_bytes(&bytes)
106    }
107
108    #[inline]
109    fn write_f64(&mut self, n: f64) -> WriteResult<()> {
110        let bytes: [u8; 8] = n.to_be_bytes();
111        self.put_bytes(&bytes)
112    }
113}
114
115/// The unchecked counterpart to [`Put`]: implement these basic byte
116/// operations to get a `WriterUnchecked<TXS>` implementation for free.
117pub(crate) trait PutUnchecked {
118    /// # Safety
119    /// The underlying buffer must have space for `bytes.len()` more bytes.
120    unsafe fn put_bytes(&mut self, bytes: &[u8]);
121
122    /// # Safety
123    /// The underlying buffer must have space for the 4-byte length prefix
124    /// plus the data padded to a 4-byte boundary, and
125    /// `bytes.len() <= u32::MAX`.
126    unsafe fn put_opaque_variable(&mut self, bytes: &[u8]);
127
128    /// # Safety
129    /// The underlying buffer must have space for the data padded to a
130    /// 4-byte boundary, and `bytes.len() <= u32::MAX`.
131    unsafe fn put_opaque_fixed(&mut self, bytes: &[u8]);
132}
133
134macro_rules! write_int_unchecked {
135    [ $($write:ident($nbytes:expr): $ty:ty),* $(,)? ] => {
136
137    $(
138        #[inline]
139        unsafe fn $write(&mut self, n: $ty) {
140            let bytes: [u8; $nbytes] = n.to_be_bytes();
141            // SAFETY: the caller guarantees space for the encoded form.
142            unsafe { self.put_bytes(&bytes) }
143        }
144    )*
145
146    };
147}
148
149impl<P> WriterUnchecked<TXS> for P
150where
151    P: PutUnchecked,
152{
153    #[inline]
154    unsafe fn write_opaque_fixed(&mut self, bytes: &[u8]) {
155        debug_assert!(bytes.len() <= u32::MAX as usize);
156        // SAFETY: forwarded caller contract.
157        unsafe { self.put_opaque_fixed(bytes) }
158    }
159
160    #[inline]
161    unsafe fn write_opaque_unpadded(&mut self, bytes: &[u8]) {
162        // SAFETY: forwarded caller contract.
163        unsafe { self.put_bytes(bytes) }
164    }
165
166    #[inline]
167    unsafe fn write_opaque_padding(&mut self, len: usize) {
168        // SAFETY: forwarded caller contract.
169        unsafe { self.put_bytes(&PADDING[..padding_len(len)]) }
170    }
171
172    #[inline]
173    unsafe fn write_opaque_variable(&mut self, bytes: &[u8]) {
174        debug_assert!(bytes.len() <= u32::MAX as usize);
175        // SAFETY: forwarded caller contract.
176        unsafe { self.put_opaque_variable(bytes) }
177    }
178
179    #[inline]
180    unsafe fn write_bool(&mut self, v: bool) {
181        // SAFETY: forwarded caller contract.
182        unsafe { self.write_i32(if v { 1 } else { 0 }) }
183    }
184
185    #[inline]
186    unsafe fn write_i16(&mut self, n: i16) {
187        // SAFETY: forwarded caller contract.
188        unsafe { self.write_i32(n as i32) }
189    }
190
191    #[inline]
192    unsafe fn write_u16(&mut self, n: u16) {
193        // SAFETY: forwarded caller contract.
194        unsafe { self.write_u32(n as u32) }
195    }
196
197    write_int_unchecked![
198        write_i64(8): i64,
199        write_i32(4): i32,
200
201        write_u64(8): u64,
202        write_u32(4): u32,
203    ];
204
205    #[inline]
206    unsafe fn write_f32(&mut self, n: f32) {
207        let bytes: [u8; 4] = n.to_be_bytes();
208        // SAFETY: forwarded caller contract.
209        unsafe { self.put_bytes(&bytes) }
210    }
211
212    #[inline]
213    unsafe fn write_f64(&mut self, n: f64) {
214        let bytes: [u8; 8] = n.to_be_bytes();
215        // SAFETY: forwarded caller contract.
216        unsafe { self.put_bytes(&bytes) }
217    }
218}