xdrs/txs/utils.rs
1//! Convenient interfaces to TXS serialization.
2//!
3//! This module provides high-level functions for encoding and decoding XDR data
4//! using the TXS (Traditional XDR Serialization) format.
5
6use crate::txs::buf_reader::*;
7use crate::txs::into_buf_writer::*;
8use crate::txs::unchecked_buf_writer::*;
9use crate::*;
10use core::mem::MaybeUninit as MU;
11
12/// Decodes TXS-encoded XDR data from a byte slice.
13///
14/// This function reads the entire buffer and deserializes a value of type `T`.
15/// It returns an error if there are any remaining bytes after decoding.
16///
17/// # Example
18///
19/// ```rust
20/// use xdrs::txs;
21///
22/// let data = [0u8; 4]; // valid TXS encoding for the unsigned integer 0
23/// let value: u32 = txs::decode(&data)?;
24/// # Ok::<(), xdrs::ReadError>(())
25/// ```
26// Expose the reader's initial position and aggregate return value to callers.
27#[cfg_attr(not(target_arch = "riscv32"), inline(always))]
28pub fn decode<'r, 'a: 'r, T>(buf: &'a [u8]) -> ReadResult<T>
29where
30 T: ReadXDR<'r>,
31{
32 let mut reader = BufReader::new(buf);
33
34 let value = read_xdr(&mut reader)?;
35
36 debug_assert!(reader.position() <= buf.len());
37
38 let remaining = buf.len() - reader.position();
39 if remaining > 0 {
40 let remaining = remaining as u64;
41 return Err(ReadError::source_remaining(remaining));
42 }
43
44 Ok(value)
45}
46
47/// Encodes a value as TXS-encoded XDR data into a pre-allocated buffer.
48///
49/// This function serializes the value into the provided buffer and returns the
50/// number of bytes written. Returns `Err(WriteError::SpaceExhausted)` if the
51/// buffer is not large enough to hold the serialized data.
52///
53/// # Example
54///
55/// ```rust
56/// use xdrs::txs;
57/// use core::mem::MaybeUninit;
58///
59/// let value: u32 = 42;
60///
61/// let mut buf = [MaybeUninit::uninit(); 16];
62///
63/// let written = txs::encode_into(&value, &mut buf)?;
64/// # Ok::<(), xdrs::WriteError>(())
65/// ```
66pub fn encode_into<T>(value: &T, buf: &mut [MU<u8>]) -> WriteResult<usize>
67where
68 T: WriteXDR + ?Sized,
69{
70 let mut writer = IntoBufWriter::new(buf);
71
72 value.write_xdr(&mut writer)?;
73
74 Ok(writer.written())
75}
76
77/// Encodes a value as TXS-encoded XDR data into a pre-allocated buffer,
78/// without runtime checks.
79///
80/// This is the infallible, unsafe counterpart to [`encode_into`]: no bounds
81/// checking is performed and no error can be returned, minimizing code size
82/// on constrained targets. Returns the number of bytes written.
83///
84/// # Safety
85///
86/// - `buf.len()` must be at least the encoded size of `value`. With
87/// `#[derive(ConstXDRSize)]`, `<T as txs::ConstMaxSize>::MAX_SIZE` (worst
88/// case over all values) — or, with `#[derive(ConstWriteXDR)]`,
89/// `const_txs_size()` (exact for this value) — provide suitable sizes.
90/// - `value` must respect all declared XDR bounds (e.g. a `VecM<T, MAX>`
91/// must not be longer than `MAX`), since a violation would make the
92/// encoding exceed any precomputed size bound. This includes the implicit
93/// `u32::MAX` upper limit on variable-length arrays and strings: the
94/// checked path returns [`WriteError::InvalidArrayLength`] for lengths
95/// exceeding `u32::MAX`; the unchecked path only `debug_assert!`s this.
96///
97/// Violations are caught by `debug_assert!` in debug builds; in release
98/// builds they are undefined behavior.
99///
100/// # Example
101///
102/// ```rust
103/// use xdrs::txs;
104/// use core::mem::MaybeUninit;
105///
106/// let value: u32 = 42;
107///
108/// let mut buf = [MaybeUninit::uninit(); 4];
109///
110/// // SAFETY: a u32 always encodes to exactly 4 TXS bytes.
111/// let written = unsafe { txs::encode_into_unchecked(&value, &mut buf) };
112/// assert_eq!(written, 4);
113/// ```
114pub unsafe fn encode_into_unchecked<T>(value: &T, buf: &mut [MU<u8>]) -> usize
115where
116 T: WriteXDR + ?Sized,
117{
118 let mut writer = UncheckedBufWriter::new(buf);
119
120 // SAFETY: the caller guarantees buf can hold the encoded form of value
121 // and that value respects its declared bounds.
122 unsafe { value.write_xdr_unchecked(&mut writer) };
123
124 writer.written()
125}
126
127#[cfg(feature = "alloc")]
128mod utils_alloc {
129 use crate::txs::buf_writer::*;
130 use crate::*;
131 use alloc::vec::Vec;
132
133 /// Encodes a value as TXS-encoded XDR data, returning an owned vector.
134 ///
135 /// This function serializes the value and returns a `Vec<u8>` containing
136 /// the serialized data.
137 ///
138 /// *Requires feature `alloc`.*
139 ///
140 /// # Example
141 ///
142 /// ```rust
143 /// use xdrs::txs;
144 ///
145 /// let value: u32 = 42;
146 /// let encoded = txs::encode(&value)?;
147 /// # Ok::<(), xdrs::WriteError>(())
148 /// ```
149 #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
150 pub fn encode<T>(value: &T) -> WriteResult<Vec<u8>>
151 where
152 T: WriteXDR + ?Sized,
153 {
154 let mut writer = BufWriter::new();
155
156 value.write_xdr(&mut writer)?;
157
158 Ok(writer.into())
159 }
160}
161
162#[cfg(feature = "alloc")]
163pub use utils_alloc::*;