Preview of the proposed up-rust native-frame-model branch (up-rust b6b99c6d, up-spec f0e9b17) — not released documentation. branch · write-up

up_rust/zero_copy/
mod.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//! Zero-copy loans, receive leases, and transport contracts.
15
16use std::{
17    any::Any,
18    collections::HashMap,
19    io::Cursor,
20    mem::MaybeUninit,
21    ops::Deref,
22    sync::{Arc, LazyLock, Mutex},
23};
24
25use async_trait::async_trait;
26#[cfg(any(test, feature = "test-util"))]
27use std::collections::VecDeque;
28use tracing::warn;
29
30#[cfg(feature = "zero-copy-transport")]
31use crate::payload::{
32    loan::{LoanUninitPayload, LoanedInitPayload, LoanedUninitPayload},
33    stable::{InitializedStablePayload, StablePayloadInit, StablePayloadInitContext},
34};
35#[cfg(feature = "selected-wire-transport-adapter")]
36use crate::wire::UWirePayload;
37#[cfg(feature = "selected-wire-transport-adapter")]
38use crate::wire_transport::UHasWire;
39use crate::{
40    payload::{
41        codec::PayloadCodec,
42        loan::{BorrowPayload, LoanPayload},
43        stable::{StableContainerPayload, StablePayload},
44        UWireError,
45    },
46    utransport::verify_filter_criteria,
47    UCode, UFrameMetadata, UStatus, UUri,
48};
49
50mod rx;
51mod tests;
52mod transport;
53mod tx;
54
55pub use rx::*;
56#[cfg(feature = "test-util")]
57pub use tests::InMemoryZeroCopyTransport;
58pub use tests::{UVecRxLease, UVecTxBuffer, UVecUninitTxBuffer};
59pub use transport::UZeroCopyUninitTransportImpl;
60#[cfg(feature = "perf-diagnostics")]
61#[doc(hidden)]
62pub use transport::UninitStableSendPhases;
63pub use transport::{UZeroCopyTransport, UZeroCopyTransportExt, UZeroCopyTransportImpl};
64#[cfg(feature = "zero-copy-transport")]
65pub use transport::{UZeroCopyUninitTransport, UZeroCopyUninitTransportExt};
66pub use tx::*;
67
68/// Diagnostic provenance for loan-backed payload storage.
69#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
70pub enum PayloadLoanProvenance {
71    /// Payload bytes are backed by a transport loan whose domain is opaque to up-rust.
72    OpaqueTransportLoan,
73}
74
75/// Immutable payload bytes with explicit transport-loan provenance.
76#[derive(Clone, Copy, Debug, Eq, PartialEq)]
77pub struct LoanedPayload<'a> {
78    bytes: &'a [u8],
79    provenance: PayloadLoanProvenance,
80}
81
82impl<'a> LoanedPayload<'a> {
83    /// Creates a loaned payload view from transport-owned storage.
84    ///
85    /// # Safety
86    ///
87    /// Callers must guarantee `bytes` stays valid and immutable for `'a`, and
88    /// that the storage was not allocated or coalesced solely to manufacture a
89    /// zero-copy receive proof.
90    #[must_use]
91    pub unsafe fn new_unchecked(bytes: &'a [u8], provenance: PayloadLoanProvenance) -> Self {
92        Self { bytes, provenance }
93    }
94
95    #[must_use]
96    /// Returns where this payload's storage came from.
97    pub fn provenance(self) -> PayloadLoanProvenance {
98        self.provenance
99    }
100
101    #[must_use]
102    /// Returns the payload as one contiguous byte slice.
103    pub fn as_bytes(self) -> &'a [u8] {
104        self.bytes
105    }
106
107    #[must_use]
108    /// Returns the payload length in bytes.
109    pub fn len(self) -> usize {
110        self.bytes.len()
111    }
112
113    #[must_use]
114    /// Returns `true` if the payload is empty.
115    pub fn is_empty(self) -> bool {
116        self.bytes.is_empty()
117    }
118}
119
120impl AsRef<[u8]> for LoanedPayload<'_> {
121    fn as_ref(&self) -> &[u8] {
122        self.bytes
123    }
124}
125
126impl Deref for LoanedPayload<'_> {
127    type Target = [u8];
128
129    fn deref(&self) -> &Self::Target {
130        self.bytes
131    }
132}
133
134fn validate_tx_loan_spec(spec: &UTxLoanSpec) -> Result<(), UStatus> {
135    validate_metadata(spec.metadata())?;
136    validate_payload_presence(
137        spec.has_payload(),
138        spec.metadata().payload_encoding().is_some(),
139        "TX loan spec",
140    )?;
141    validate_payload_layout(spec.payload_len(), spec.payload_alignment())
142}
143
144pub(crate) use crate::frame::view::{
145    frame_metadata_error, validate_metadata, validate_payload_presence,
146};
147
148fn validate_payload_layout(payload_len: usize, alignment: usize) -> Result<(), UStatus> {
149    PayloadAlignment::new(alignment)?;
150    let _ = payload_len;
151    Ok(())
152}
153
154fn verify_payload_slice_layout(
155    payload: &[u8],
156    payload_len: usize,
157    alignment: usize,
158) -> Result<(), UStatus> {
159    if payload.len() != payload_len {
160        return Err(internal_zero_copy_error(format!(
161            "transport returned TX payload length {} but {payload_len} was requested",
162            payload.len()
163        )));
164    }
165    if !payload.is_empty() && !(payload.as_ptr() as usize).is_multiple_of(alignment) {
166        return Err(internal_zero_copy_error(format!(
167            "transport returned TX payload alignment {} but {alignment} was requested",
168            payload.as_ptr() as usize
169        )));
170    }
171    Ok(())
172}
173
174fn aligned_offset(address: usize, alignment: usize) -> usize {
175    if alignment == 1 {
176        0
177    } else {
178        (alignment - (address % alignment)) % alignment
179    }
180}
181
182fn invalid_argument(message: impl Into<String>) -> UStatus {
183    UStatus::fail_with_code(UCode::InvalidArgument, message.into())
184}
185
186fn internal_zero_copy_error(message: impl Into<String>) -> UStatus {
187    UStatus::fail_with_code(UCode::Internal, message.into())
188}