Skip to main content

xdrs/
write_xdr.rs

1//! Writing Rust values as XDR data.
2//!
3//! This module provides the [`WriteXDR`] trait and implementations for
4//! converting Rust values into [XDR][RFC-4506]-encoded data.
5//!
6//! [RFC-4506]: https://datatracker.ietf.org/doc/html/rfc4506
7
8use crate::serialize::writer::*;
9use crate::serialize::writer_unchecked::*;
10use crate::valid_u32::*;
11use core::convert::Infallible;
12
13/// Types that can be written as XDR data.
14///
15/// Types implementing this trait can be serialized into XDR format
16/// using any writer that implements [`Writer`].
17///
18/// The trait has two required methods: [`write_xdr`](Self::write_xdr) (the
19/// checked, fallible path) and [`write_xdr_unchecked`](Self::write_xdr_unchecked)
20/// (an `unsafe`, infallible path with no runtime checks). `#[derive(WriteXDR)]`
21/// implements both automatically; manual implementors must provide both — the
22/// unchecked method must uphold the buffer-size and value-bound contract
23/// documented on [`write_xdr_unchecked`](Self::write_xdr_unchecked).
24/// Manual implementors should also add `impl EncodesAs<Self> for T {}`
25/// (which the derive emits), so the type can be passed where an
26/// [`EncodesAs`](crate::EncodesAs) bound expects it.
27///
28/// # Example
29///
30/// ```rust
31/// use xdrs::cxs;
32/// use core::mem::MaybeUninit;
33///
34/// let value: u32 = 42;
35/// let mut buf = [MaybeUninit::uninit(); 16];
36/// let written = cxs::encode_into(&value, &mut buf)?;
37/// # Ok::<(), xdrs::WriteError>(())
38/// ```
39pub trait WriteXDR {
40    /// Writes this value as XDR data to the writer.
41    ///
42    /// This method should write the appropriate XDR-encoded representation
43    /// of `self` to the writer.
44    fn write_xdr<F, W: Writer<F>>(&self, w: &mut W) -> WriteResult<()>;
45
46    /// Writes this value as XDR data to the writer, without runtime checks.
47    ///
48    /// This is the infallible, unsafe counterpart to
49    /// [`write_xdr`](Self::write_xdr), for use with
50    /// [`WriterUnchecked`] writers on code-size-sensitive targets.
51    ///
52    /// # Safety
53    ///
54    /// - The writer's underlying buffer must have enough space remaining
55    ///   for the encoded form of `self`.
56    /// - `self` must respect all declared XDR bounds (e.g. a
57    ///   `VecM<T, MAX>` must not be longer than `MAX`), since a violation
58    ///   would make the encoding exceed any precomputed size bound. This
59    ///   includes the implicit `u32::MAX` upper limit on variable-length
60    ///   arrays and strings: the checked path returns
61    ///   [`WriteError::InvalidArrayLength`] for lengths exceeding
62    ///   `u32::MAX`; the unchecked path only `debug_assert!`s this
63    ///   condition.
64    ///
65    /// Violations are `debug_assert!`ed; in release builds they are
66    /// undefined behavior.
67    unsafe fn write_xdr_unchecked<F, W: WriterUnchecked<F>>(&self, w: &mut W);
68}
69
70/// Writes an XDR value to the writer.
71///
72/// This is a convenience function that calls [`WriteXDR::write_xdr`] on the value.
73///
74/// # Example
75///
76/// ```rust
77/// use xdrs::cxs;
78/// use core::mem::MaybeUninit;
79///
80/// let value: u32 = 42;
81/// let mut buf = [MaybeUninit::uninit(); 16];
82/// let written = cxs::encode_into(&value, &mut buf)?;
83/// # Ok::<(), xdrs::WriteError>(())
84/// ```
85#[inline]
86pub fn write_xdr<T, F, W>(this: &T, w: &mut W) -> WriteResult<()>
87where
88    T: WriteXDR,
89    W: Writer<F>,
90{
91    this.write_xdr(w)
92}
93
94/// Writes an XDR value to the writer, without runtime checks.
95///
96/// This is a convenience function that calls
97/// [`WriteXDR::write_xdr_unchecked`] on the value.
98///
99/// # Safety
100///
101/// Same contract as [`WriteXDR::write_xdr_unchecked`]: the writer must have
102/// space for the encoded form of `this`, and `this` must respect all
103/// declared XDR bounds.
104#[inline]
105pub unsafe fn write_xdr_unchecked<T, F, W>(this: &T, w: &mut W)
106where
107    T: WriteXDR,
108    W: WriterUnchecked<F>,
109{
110    // SAFETY: forwarded caller contract.
111    unsafe { this.write_xdr_unchecked(w) }
112}
113
114macro_rules! write_xdr_int_impls {
115    { $( $write_method:ident for $int_ty:ty ),* $(,)? } => { $(
116
117        impl WriteXDR for $int_ty {
118            #[inline]
119            fn write_xdr<F, W: Writer<F>>(&self, w: &mut W) -> WriteResult<()> {
120                w.$write_method(*self)
121            }
122
123            #[inline]
124            unsafe fn write_xdr_unchecked<F, W: WriterUnchecked<F>>(&self, w: &mut W) {
125                // SAFETY: forwarded caller contract.
126                unsafe { w.$write_method(*self) }
127            }
128        }
129
130    )* };
131}
132
133write_xdr_int_impls! {
134    write_i64 for i64,
135    write_i32 for i32,
136    write_i16 for i16,
137
138    write_u64 for u64,
139    write_u32 for u32,
140    write_u16 for u16,
141
142    write_bool for bool,
143    write_f32 for f32,
144    write_f64 for f64,
145}
146
147impl WriteXDR for [u8] {
148    fn write_xdr<F, W: Writer<F>>(&self, w: &mut W) -> WriteResult<()> {
149        w.write_opaque_variable(self)
150    }
151
152    unsafe fn write_xdr_unchecked<F, W: WriterUnchecked<F>>(&self, w: &mut W) {
153        // SAFETY: forwarded caller contract.
154        unsafe { w.write_opaque_variable(self) }
155    }
156}
157
158impl<const N: usize> WriteXDR for [u8; N] {
159    #[inline]
160    fn write_xdr<F, W: Writer<F>>(&self, w: &mut W) -> WriteResult<()> {
161        let () = ValidU32::<N>::OK;
162
163        w.write_opaque_fixed(self)
164    }
165
166    #[inline]
167    unsafe fn write_xdr_unchecked<F, W: WriterUnchecked<F>>(&self, w: &mut W) {
168        let () = ValidU32::<N>::OK;
169
170        // SAFETY: forwarded caller contract.
171        unsafe { w.write_opaque_fixed(self) }
172    }
173}
174
175impl<T: WriteXDR, const N: usize> WriteXDR for [T; N] {
176    fn write_xdr<F, W: Writer<F>>(&self, w: &mut W) -> WriteResult<()> {
177        let () = ValidU32::<N>::OK;
178
179        for elt in self {
180            elt.write_xdr(w)?;
181        }
182        Ok(())
183    }
184
185    unsafe fn write_xdr_unchecked<F, W: WriterUnchecked<F>>(&self, w: &mut W) {
186        let () = ValidU32::<N>::OK;
187
188        for elt in self {
189            // SAFETY: the caller guarantees space for every element.
190            unsafe { elt.write_xdr_unchecked(w) };
191        }
192    }
193}
194
195impl<T: WriteXDR> WriteXDR for [T] {
196    fn write_xdr<F, W: Writer<F>>(&self, w: &mut W) -> WriteResult<()> {
197        if self.len() > u32::MAX as usize {
198            return Err(WriteError::InvalidArrayLength);
199        }
200        let len = self.len() as u32;
201        w.write_u32(len)?;
202
203        for elt in self {
204            elt.write_xdr(w)?;
205        }
206        Ok(())
207    }
208
209    unsafe fn write_xdr_unchecked<F, W: WriterUnchecked<F>>(&self, w: &mut W) {
210        debug_assert!(self.len() <= u32::MAX as usize);
211        // SAFETY: the caller guarantees space for the length prefix and
212        // every element.
213        unsafe {
214            w.write_u32(self.len() as u32);
215            for elt in self {
216                elt.write_xdr_unchecked(w);
217            }
218        }
219    }
220}
221
222// `?Sized` so a `&'a T` field under a `T: WriteXDR + ?Sized` bound can derive.
223// This subsumes `&[u8]`, `&[T]` and `&str` through their pointee impls above —
224// a concrete impl for any of them would overlap this one and fail coherence.
225impl<T: WriteXDR + ?Sized> WriteXDR for &T {
226    #[inline]
227    fn write_xdr<F, W: Writer<F>>(&self, w: &mut W) -> WriteResult<()> {
228        (*self).write_xdr(w)
229    }
230
231    #[inline]
232    unsafe fn write_xdr_unchecked<F, W: WriterUnchecked<F>>(&self, w: &mut W) {
233        // SAFETY: forwarded caller contract.
234        unsafe { (*self).write_xdr_unchecked(w) }
235    }
236}
237
238impl<T: WriteXDR + ?Sized> WriteXDR for &mut T {
239    #[inline]
240    fn write_xdr<F, W: Writer<F>>(&self, w: &mut W) -> WriteResult<()> {
241        (**self).write_xdr(w)
242    }
243
244    #[inline]
245    unsafe fn write_xdr_unchecked<F, W: WriterUnchecked<F>>(&self, w: &mut W) {
246        // SAFETY: forwarded caller contract.
247        unsafe { (**self).write_xdr_unchecked(w) }
248    }
249}
250
251impl WriteXDR for str {
252    fn write_xdr<F, W: Writer<F>>(&self, w: &mut W) -> WriteResult<()> {
253        w.write_opaque_variable(self.as_bytes())
254    }
255
256    unsafe fn write_xdr_unchecked<F, W: WriterUnchecked<F>>(&self, w: &mut W) {
257        // SAFETY: forwarded caller contract.
258        unsafe { w.write_opaque_variable(self.as_bytes()) }
259    }
260}
261
262#[cfg(feature = "alloc")]
263#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
264impl WriteXDR for alloc::string::String {
265    fn write_xdr<F, W: Writer<F>>(&self, w: &mut W) -> WriteResult<()> {
266        self.as_str().write_xdr(w)
267    }
268
269    unsafe fn write_xdr_unchecked<F, W: WriterUnchecked<F>>(&self, w: &mut W) {
270        // SAFETY: forwarded caller contract.
271        unsafe { self.as_str().write_xdr_unchecked(w) }
272    }
273}
274
275impl WriteXDR for () {
276    fn write_xdr<F, W: Writer<F>>(&self, _w: &mut W) -> WriteResult<()> {
277        Ok(())
278    }
279
280    unsafe fn write_xdr_unchecked<F, W: WriterUnchecked<F>>(&self, _w: &mut W) {}
281}
282
283impl WriteXDR for Infallible {
284    fn write_xdr<F, W: Writer<F>>(&self, _w: &mut W) -> WriteResult<()> {
285        unreachable!()
286    }
287
288    unsafe fn write_xdr_unchecked<F, W: WriterUnchecked<F>>(&self, _w: &mut W) {
289        match *self {}
290    }
291}
292
293macro_rules! write_tuple {
294    { $( ($v:ident, $t:ident) ),+ } => {
295
296        impl<$($t),+> WriteXDR for ($($t),+)
297        where
298            $( $t: WriteXDR, )+
299        {
300            fn write_xdr<F, W: Writer<F>>(&self, w: &mut W) -> WriteResult<()> {
301                let ( $($v),+ ) = self;
302                $( write_xdr($v, w)?; )+
303                Ok(())
304            }
305
306            unsafe fn write_xdr_unchecked<F, W: WriterUnchecked<F>>(&self, w: &mut W) {
307                let ( $($v),+ ) = self;
308                // SAFETY: the caller guarantees space for the whole tuple.
309                unsafe { $( write_xdr_unchecked($v, w); )+ }
310            }
311        }
312
313    };
314}
315
316write_tuple! { (t0, T0), (t1, T1) }
317write_tuple! { (t0, T0), (t1, T1), (t2, T2) }
318write_tuple! { (t0, T0), (t1, T1), (t2, T2), (t3, T3) }
319write_tuple! { (t0, T0), (t1, T1), (t2, T2), (t3, T3), (t4, T4) }
320write_tuple! { (t0, T0), (t1, T1), (t2, T2), (t3, T3), (t4, T4), (t5, T5) }
321write_tuple! { (t0, T0), (t1, T1), (t2, T2), (t3, T3), (t4, T4), (t5, T5), (t6, T6) }
322write_tuple! { (t0, T0), (t1, T1), (t2, T2), (t3, T3), (t4, T4), (t5, T5), (t6, T6), (t7, T7) }
323write_tuple! { (t0, T0), (t1, T1), (t2, T2), (t3, T3), (t4, T4), (t5, T5), (t6, T6), (t7, T7), (t8, T8) }
324write_tuple! { (t0, T0), (t1, T1), (t2, T2), (t3, T3), (t4, T4), (t5, T5), (t6, T6), (t7, T7), (t8, T8), (t9, T9) }
325write_tuple! { (t0, T0), (t1, T1), (t2, T2), (t3, T3), (t4, T4), (t5, T5), (t6, T6), (t7, T7), (t8, T8), (t9, T9), (t10, T10) }
326write_tuple! { (t0, T0), (t1, T1), (t2, T2), (t3, T3), (t4, T4), (t5, T5), (t6, T6), (t7, T7), (t8, T8), (t9, T9), (t10, T10), (t11, T11) }
327write_tuple! { (t0, T0), (t1, T1), (t2, T2), (t3, T3), (t4, T4), (t5, T5), (t6, T6), (t7, T7), (t8, T8), (t9, T9), (t10, T10), (t11, T11), (t12, T12) }
328write_tuple! { (t0, T0), (t1, T1), (t2, T2), (t3, T3), (t4, T4), (t5, T5), (t6, T6), (t7, T7), (t8, T8), (t9, T9), (t10, T10), (t11, T11), (t12, T12), (t13, T13) }
329write_tuple! { (t0, T0), (t1, T1), (t2, T2), (t3, T3), (t4, T4), (t5, T5), (t6, T6), (t7, T7), (t8, T8), (t9, T9), (t10, T10), (t11, T11), (t12, T12), (t13, T13), (t14, T14) }
330write_tuple! { (t0, T0), (t1, T1), (t2, T2), (t3, T3), (t4, T4), (t5, T5), (t6, T6), (t7, T7), (t8, T8), (t9, T9), (t10, T10), (t11, T11), (t12, T12), (t13, T13), (t14, T14), (t15, T15) }
331
332// impl for single-element tuples
333impl<T: WriteXDR> WriteXDR for (T,) {
334    fn write_xdr<F, W: Writer<F>>(&self, w: &mut W) -> WriteResult<()> {
335        let (t,) = self;
336        t.write_xdr(w)
337    }
338
339    unsafe fn write_xdr_unchecked<F, W: WriterUnchecked<F>>(&self, w: &mut W) {
340        let (t,) = self;
341        // SAFETY: forwarded caller contract.
342        unsafe { t.write_xdr_unchecked(w) }
343    }
344}
345
346impl<T: WriteXDR> WriteXDR for Option<T> {
347    fn write_xdr<F, W: Writer<F>>(&self, w: &mut W) -> WriteResult<()> {
348        if let Some(val) = self {
349            w.write_i32(1)?;
350            val.write_xdr(w)
351        } else {
352            w.write_i32(0)
353        }
354    }
355
356    unsafe fn write_xdr_unchecked<F, W: WriterUnchecked<F>>(&self, w: &mut W) {
357        // SAFETY: the caller guarantees space for the tag and the payload.
358        unsafe {
359            if let Some(val) = self {
360                w.write_i32(1);
361                val.write_xdr_unchecked(w);
362            } else {
363                w.write_i32(0);
364            }
365        }
366    }
367}
368
369const _: () = {
370    use super::*;
371    use crate::serialize::writer::Writer;
372    use crate::serialize::writer_unchecked::WriterUnchecked;
373    use xdr_common::*;
374
375    impl<const MAX: u32> WriteXDR for BytesM<MAX> {
376        fn write_xdr<F, W: Writer<F>>(&self, w: &mut W) -> WriteResult<()> {
377            if self.0.len() > MAX as usize {
378                return Err(WriteError::InvalidArrayLength);
379            }
380            self.0.write_xdr(w)
381        }
382
383        unsafe fn write_xdr_unchecked<F, W: WriterUnchecked<F>>(&self, w: &mut W) {
384            debug_assert!(
385                self.0.len() <= MAX as usize,
386                "BytesM exceeds declared bound"
387            );
388            // SAFETY: the caller guarantees the value respects its declared
389            // bound and that the writer has space for the encoded form.
390            unsafe { self.0.write_xdr_unchecked(w) }
391        }
392    }
393
394    // Make VecM<u8> work like BytesM;
395    #[cfg(feature = "alloc")]
396    impl<const MAX: u32> WriteXDR for VecM<u8, MAX> {
397        fn write_xdr<F, W: Writer<F>>(&self, w: &mut W) -> WriteResult<()> {
398            if self.0.len() > MAX as usize {
399                return Err(WriteError::InvalidArrayLength);
400            }
401            self.0.write_xdr(w)
402        }
403
404        unsafe fn write_xdr_unchecked<F, W: WriterUnchecked<F>>(&self, w: &mut W) {
405            debug_assert!(self.0.len() <= MAX as usize, "VecM exceeds declared bound");
406            // SAFETY: the caller guarantees the value respects its declared
407            // bound and that the writer has space for the encoded form.
408            unsafe { self.0.write_xdr_unchecked(w) }
409        }
410    }
411    #[cfg(not(feature = "alloc"))]
412    impl<const MAX: u32> WriteXDR for VecM<u8, MAX> {
413        fn write_xdr<F, W: Writer<F>>(&self, w: &mut W) -> WriteResult<()> {
414            if self.len() > MAX as usize {
415                return Err(WriteError::InvalidArrayLength);
416            }
417            <Self as AsRef<[u8]>>::as_ref(self).write_xdr(w)
418        }
419
420        unsafe fn write_xdr_unchecked<F, W: WriterUnchecked<F>>(&self, w: &mut W) {
421            debug_assert!(self.len() <= MAX as usize, "VecM exceeds declared bound");
422            // SAFETY: the caller guarantees the value respects its declared
423            // bound and that the writer has space for the encoded form.
424            unsafe { <Self as AsRef<[u8]>>::as_ref(self).write_xdr_unchecked(w) }
425        }
426    }
427
428    impl<const MAX: u32> WriteXDR for StringM<MAX> {
429        fn write_xdr<F, W: Writer<F>>(&self, w: &mut W) -> WriteResult<()> {
430            if self.0.len() > MAX as usize {
431                return Err(WriteError::InvalidArrayLength);
432            }
433            self.0.write_xdr(w)
434        }
435
436        unsafe fn write_xdr_unchecked<F, W: WriterUnchecked<F>>(&self, w: &mut W) {
437            debug_assert!(
438                self.0.len() <= MAX as usize,
439                "StringM exceeds declared bound"
440            );
441            // SAFETY: the caller guarantees the value respects its declared
442            // bound and that the writer has space for the encoded form.
443            unsafe { self.0.write_xdr_unchecked(w) }
444        }
445    }
446
447    #[cfg(feature = "alloc")]
448    impl<const MAX: u32, T> WriteXDR for VecM<T, MAX>
449    where
450        T: WriteXDR,
451    {
452        fn write_xdr<F, W: Writer<F>>(&self, w: &mut W) -> WriteResult<()> {
453            if self.len() > MAX as usize {
454                return Err(WriteError::InvalidArrayLength);
455            }
456            <Self as AsRef<[T]>>::as_ref(self).write_xdr(w)
457        }
458
459        unsafe fn write_xdr_unchecked<F, W: WriterUnchecked<F>>(&self, w: &mut W) {
460            debug_assert!(self.len() <= MAX as usize, "VecM exceeds declared bound");
461            // SAFETY: the caller guarantees the value respects its declared
462            // bound and that the writer has space for the encoded form.
463            unsafe { <Self as AsRef<[T]>>::as_ref(self).write_xdr_unchecked(w) }
464        }
465    }
466
467    #[cfg(not(feature = "alloc"))]
468    impl<const MAX: u32, T> WriteXDR for VecM<T, MAX>
469    where
470        T: WriteXDR + 'static,
471    {
472        fn write_xdr<F, W: Writer<F>>(&self, w: &mut W) -> WriteResult<()> {
473            if self.len() > MAX as usize {
474                return Err(WriteError::InvalidArrayLength);
475            }
476            <Self as AsRef<[T]>>::as_ref(self).write_xdr(w)
477        }
478
479        unsafe fn write_xdr_unchecked<F, W: WriterUnchecked<F>>(&self, w: &mut W) {
480            debug_assert!(self.len() <= MAX as usize, "VecM exceeds declared bound");
481            // SAFETY: the caller guarantees the value respects its declared
482            // bound and that the writer has space for the encoded form.
483            unsafe { <Self as AsRef<[T]>>::as_ref(self).write_xdr_unchecked(w) }
484        }
485    }
486
487    // On the heap `Box` itself rather than the `xdr_common::Box` alias, whose
488    // parameter is implicitly `Sized`: `?Sized` admits `Box<[T]>` and
489    // `Box<str>`, as for `&T` and `Arc<T>`.
490    #[cfg(feature = "alloc")]
491    #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
492    impl<T: WriteXDR + ?Sized> WriteXDR for alloc::boxed::Box<T> {
493        fn write_xdr<F, W: Writer<F>>(&self, w: &mut W) -> WriteResult<()> {
494            (**self).write_xdr(w)
495        }
496
497        unsafe fn write_xdr_unchecked<F, W: WriterUnchecked<F>>(&self, w: &mut W) {
498            // SAFETY: forwarded caller contract.
499            unsafe { (**self).write_xdr_unchecked(w) }
500        }
501    }
502};
503
504#[cfg(not(feature = "alloc"))]
505impl<T: WriteXDR> WriteXDR for xdr_common::Box<T> {
506    fn write_xdr<F, W: Writer<F>>(&self, w: &mut W) -> WriteResult<()> {
507        (**self).write_xdr(w)
508    }
509
510    unsafe fn write_xdr_unchecked<F, W: WriterUnchecked<F>>(&self, w: &mut W) {
511        // SAFETY: forwarded caller contract.
512        unsafe { (**self).write_xdr_unchecked(w) }
513    }
514}
515
516impl<T, E> WriteXDR for Result<T, E>
517where
518    T: WriteXDR,
519    E: WriteXDR,
520{
521    fn write_xdr<F, W: Writer<F>>(&self, w: &mut W) -> WriteResult<()> {
522        match self {
523            Ok(x) => {
524                w.write_i32(0)?;
525                x.write_xdr(w)
526            }
527            Err(e) => {
528                w.write_i32(1)?;
529                e.write_xdr(w)
530            }
531        }
532    }
533
534    unsafe fn write_xdr_unchecked<F, W: WriterUnchecked<F>>(&self, w: &mut W) {
535        // SAFETY: the caller guarantees space for the tag and the payload.
536        unsafe {
537            match self {
538                Ok(x) => {
539                    w.write_i32(0);
540                    x.write_xdr_unchecked(w);
541                }
542                Err(e) => {
543                    w.write_i32(1);
544                    e.write_xdr_unchecked(w);
545                }
546            }
547        }
548    }
549}
550
551impl<T> WriteXDR for core::marker::PhantomData<T> {
552    fn write_xdr<F, W: Writer<F>>(&self, _w: &mut W) -> WriteResult<()> {
553        Ok(())
554    }
555
556    unsafe fn write_xdr_unchecked<F, W: WriterUnchecked<F>>(&self, _w: &mut W) {}
557}
558
559#[cfg(feature = "alloc")]
560#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
561const _: () = {
562    use crate::serialize::writer_unchecked::WriterUnchecked;
563    use alloc::vec::Vec;
564
565    impl WriteXDR for Vec<u8> {
566        fn write_xdr<F, W: Writer<F>>(&self, w: &mut W) -> WriteResult<()> {
567            w.write_opaque_variable(self.as_ref())
568        }
569
570        unsafe fn write_xdr_unchecked<F, W: WriterUnchecked<F>>(&self, w: &mut W) {
571            // SAFETY: forwarded caller contract.
572            unsafe { w.write_opaque_variable(self.as_ref()) }
573        }
574    }
575
576    impl<T> WriteXDR for Vec<T>
577    where
578        T: WriteXDR,
579    {
580        fn write_xdr<F, W: Writer<F>>(&self, w: &mut W) -> WriteResult<()> {
581            let slice: &[T] = self.as_ref();
582            slice.write_xdr(w)
583        }
584
585        unsafe fn write_xdr_unchecked<F, W: WriterUnchecked<F>>(&self, w: &mut W) {
586            let slice: &[T] = self.as_ref();
587            // SAFETY: forwarded caller contract.
588            unsafe { slice.write_xdr_unchecked(w) }
589        }
590    }
591
592    // `alloc::sync` exists only on targets with pointer-sized atomics.
593    #[cfg(target_has_atomic = "ptr")]
594    impl<T: WriteXDR + ?Sized> WriteXDR for alloc::sync::Arc<T> {
595        #[inline]
596        fn write_xdr<F, W: Writer<F>>(&self, w: &mut W) -> WriteResult<()> {
597            (**self).write_xdr(w)
598        }
599
600        #[inline]
601        unsafe fn write_xdr_unchecked<F, W: WriterUnchecked<F>>(&self, w: &mut W) {
602            // SAFETY: forwarded caller contract.
603            unsafe { (**self).write_xdr_unchecked(w) }
604        }
605    }
606};
607
608#[cfg(test)]
609mod tests {
610    use super::*;
611    use core::mem::MaybeUninit;
612
613    #[test]
614    fn tuple_unchecked_writes_txs() {
615        let mut buf = [MaybeUninit::uninit(); 8];
616        let mut w = crate::txs::unchecked_buf_writer::UncheckedBufWriter::new(&mut buf);
617        // SAFETY: two u32s occupy exactly the 8-byte buffer.
618        unsafe { (1u32, 2u32).write_xdr_unchecked(&mut w) };
619        assert_eq!(w.written(), 8);
620        // SAFETY: the first 8 bytes were just initialized.
621        let bytes = unsafe { buf[..8].assume_init_ref() };
622        assert_eq!(bytes, [0, 0, 0, 1, 0, 0, 0, 2]);
623    }
624}