up_rust/payload/loan.rs
1/********************************************************************************
2 * Copyright (c) 2026 Contributors to the Eclipse Foundation
3 *
4 * See the NOTICE file(s) distributed with this work for additional
5 * information regarding copyright ownership.
6 *
7 * This program and the accompanying materials are made available under the
8 * terms of the Apache License Version 2.0 which is available at
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * SPDX-License-Identifier: Apache-2.0
12 ********************************************************************************/
13
14#[cfg(feature = "zero-copy-transport")]
15use std::{mem, ptr::NonNull};
16
17#[cfg(feature = "zero-copy-transport")]
18use crate::zero_copy::LoanedPayloadUninitMut;
19
20use super::{codec::PayloadCodec, codec::PayloadLayout, UWireError};
21
22/// Borrows a typed payload directly from contiguous receive storage.
23///
24/// This is the receive-side true zero-copy codec hook. Implementors must not
25/// allocate, copy, or deserialize into owned storage before returning `&T`.
26///
27/// # Safety
28///
29/// Implementors must validate that `src` has the exact length and alignment for
30/// one initialized `T`, that the bytes are a valid representation for `T` under
31/// this codec, and that returning `&T` cannot observe uninitialized padding or
32/// process-local state.
33pub unsafe trait BorrowPayload<T>: PayloadCodec {
34 /// Borrows `T` from contiguous payload bytes.
35 ///
36 /// # Errors
37 ///
38 /// Returns an error if the payload bytes cannot be borrowed as one valid `T`.
39 fn borrow_payload(src: &[u8]) -> Result<&T, UWireError>;
40}
41
42/// Initializes and borrows a typed value directly in initialized transmit storage.
43///
44/// # Safety
45///
46/// Implementors must guarantee that [`LoanPayload::loan_payload`] returns
47/// `&mut T` only when the destination byte range is uniquely borrowed, has the
48/// exact layout returned by [`LoanPayload::loan_layout`], and contains one valid
49/// initialized `T` for the returned lifetime.
50pub unsafe trait LoanPayload<T>: PayloadCodec {
51 /// Returns the exact layout required for a typed initialized transmit loan.
52 ///
53 /// # Errors
54 ///
55 /// Returns an error if this codec cannot loan `T` into initialized TX storage.
56 fn loan_layout() -> Result<PayloadLayout, UWireError>;
57
58 /// Initializes `dst` and returns a typed mutable view over the loaned payload.
59 ///
60 /// # Errors
61 ///
62 /// Returns an error if `dst` does not have the required length or alignment.
63 fn loan_payload(dst: &mut [u8]) -> Result<&mut T, UWireError>;
64}
65
66/// Initializes a typed value directly in uninitialized transmit storage.
67///
68/// # Safety
69///
70/// Implementors must guarantee that [`LoanUninitPayload::loan_uninit_payload`]
71/// validates the destination range and only returns a slot when the range is one
72/// uniquely borrowed allocation, has the exact layout returned by
73/// [`LoanUninitPayload::loan_uninit_layout`], and is valid for writes of one
74/// `MaybeUninit<T>`.
75#[cfg(feature = "zero-copy-transport")]
76pub unsafe trait LoanUninitPayload<T>: PayloadCodec {
77 /// Returns the exact layout required for a typed uninitialized transmit loan.
78 ///
79 /// # Errors
80 ///
81 /// Returns an error if this codec cannot loan `T` into uninitialized TX storage.
82 fn loan_uninit_layout() -> Result<PayloadLayout, UWireError>;
83
84 /// Validates `dst` and returns an uninitialized typed payload slot.
85 ///
86 /// # Errors
87 ///
88 /// Returns an error if `dst` does not have the required length or alignment.
89 fn loan_uninit_payload<'a>(
90 dst: LoanedPayloadUninitMut<'a>,
91 ) -> Result<LoanedUninitPayload<'a, T>, UWireError>;
92}
93
94/// Uninitialized typed payload slot borrowed from a transmit loan.
95#[cfg(feature = "zero-copy-transport")]
96pub struct LoanedUninitPayload<'a, T> {
97 // Safe-by-construction: an exclusive borrow of the uninitialized target.
98 slot: &'a mut mem::MaybeUninit<T>,
99}
100
101#[cfg(feature = "zero-copy-transport")]
102impl<'a, T> core::fmt::Debug for LoanedUninitPayload<'a, T> {
103 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
104 f.debug_struct("LoanedUninitPayload")
105 .finish_non_exhaustive()
106 }
107}
108
109#[cfg(feature = "zero-copy-transport")]
110impl<'a, T> LoanedUninitPayload<'a, T> {
111 /// Wraps an exclusive borrow of the uninitialized target value.
112 #[must_use]
113 pub fn new(slot: &'a mut mem::MaybeUninit<T>) -> Self {
114 Self { slot }
115 }
116
117 /// Initializes the target by move and returns the initialized handle.
118 ///
119 /// Fully safe: `MaybeUninit::write` performs the move and hands back the
120 /// initialized reference; no copy occurs beyond the value move itself.
121 #[must_use]
122 pub fn write(self, value: T) -> LoanedInitPayload<'a, T> {
123 LoanedInitPayload {
124 value: self.slot.write(value),
125 }
126 }
127
128 pub(crate) fn uninit_ptr(&self) -> NonNull<mem::MaybeUninit<T>> {
129 NonNull::from(&*self.slot)
130 }
131}
132
133/// An initialized, exclusively borrowed payload value inside loaned storage.
134#[cfg(feature = "zero-copy-transport")]
135pub struct LoanedInitPayload<'a, T> {
136 value: &'a mut T,
137}
138
139#[cfg(feature = "zero-copy-transport")]
140impl<'a, T> core::fmt::Debug for LoanedInitPayload<'a, T> {
141 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
142 f.debug_struct("LoanedInitPayload").finish_non_exhaustive()
143 }
144}
145
146#[cfg(feature = "zero-copy-transport")]
147impl<'a, T> LoanedInitPayload<'a, T> {
148 pub(crate) fn initialized_ptr(&self) -> NonNull<T> {
149 NonNull::from(&*self.value)
150 }
151}
152
153#[cfg(feature = "zero-copy-transport")]
154impl<T> AsMut<T> for LoanedInitPayload<'_, T> {
155 fn as_mut(&mut self) -> &mut T {
156 self.value
157 }
158}
159
160#[cfg(feature = "zero-copy-transport")]
161impl<T> AsRef<T> for LoanedInitPayload<'_, T> {
162 fn as_ref(&self) -> &T {
163 self.value
164 }
165}