Skip to main content

cubecl_environment/bytes/
access.rs

1//! Access policy and reader/writer configuration for [`Bytes`](super::Bytes).
2//!
3//! Accessing a [`Bytes`] is parameterized by an [`AccessPolicy`] that dictates what the
4//! backing [`AllocationController`](super::AllocationController) is *allowed* to do to satisfy
5//! the access. The policy makes the previously-implicit decisions explicit:
6//!
7//! - **copy-on-read**: materializing lazy storage (file / device) into a host buffer.
8//! - **copy-on-write**: copying shared storage into a private buffer before mutation.
9//!
10//! A [`Reader`]/[`Writer`] is a small builder over a policy, used with
11//! [`Bytes::read`](super::Bytes::read) / [`Bytes::write`](super::Bytes::write).
12
13use alloc::string::String;
14
15/// What an access is allowed to do to satisfy a read/write of a [`Bytes`](super::Bytes).
16#[derive(Clone, Copy, Debug, PartialEq, Eq)]
17pub struct AccessPolicy {
18    allow_copy: bool,
19}
20
21impl Default for AccessPolicy {
22    fn default() -> Self {
23        Self::allow_copy()
24    }
25}
26
27impl AccessPolicy {
28    /// Allow allocating/copying a new buffer to satisfy the access (copy-on-read
29    /// materialization and copy-on-write).
30    pub fn allow_copy() -> Self {
31        Self { allow_copy: true }
32    }
33
34    /// Forbid any copy: the access succeeds only if it can be served from already-resident
35    /// memory, otherwise it fails with [`AccessError::WouldCopy`].
36    pub fn zero_copy() -> Self {
37        Self { allow_copy: false }
38    }
39
40    /// Whether copying is allowed to satisfy the access.
41    pub fn copy_allowed(&self) -> bool {
42        self.allow_copy
43    }
44}
45
46/// Error returned when accessing a [`Bytes`](super::Bytes).
47#[derive(Debug, Clone, PartialEq, Eq)]
48pub enum AccessError {
49    /// The access required allocating/copying a new buffer, which the [`AccessPolicy`]
50    /// forbade.
51    WouldCopy,
52    /// Materializing the data failed (e.g. a file or device read error). The payload is a
53    /// human-readable message.
54    Read(String),
55}
56
57impl core::fmt::Display for AccessError {
58    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
59        match self {
60            Self::WouldCopy => f.write_str("access would require a copy, which the policy forbids"),
61            Self::Read(msg) => write!(f, "failed to materialize bytes: {msg}"),
62        }
63    }
64}
65
66impl core::error::Error for AccessError {}
67
68/// Configures a read of a [`Bytes`](super::Bytes::read).
69///
70/// Defaults to allowing copies (the same behavior as `Deref`). Use [`Reader::no_copy`] to
71/// require resident memory.
72#[derive(Clone, Copy, Debug, Default)]
73pub struct Reader {
74    pub(crate) policy: AccessPolicy,
75}
76
77impl Reader {
78    /// A reader that allows copies to satisfy the read.
79    pub fn new() -> Self {
80        Self::default()
81    }
82
83    /// Require the read to be served without any copy/materialization.
84    pub fn no_copy(mut self) -> Self {
85        self.policy = AccessPolicy::zero_copy();
86        self
87    }
88}
89
90/// Configures a mutable access of a [`Bytes`](super::Bytes::write).
91///
92/// Defaults to allowing copies (copy-on-write). Use [`Writer::no_copy`] to require that the
93/// buffer is already private/mutable.
94#[derive(Clone, Copy, Debug, Default)]
95pub struct Writer {
96    pub(crate) policy: AccessPolicy,
97}
98
99impl Writer {
100    /// A writer that allows copy-on-write to satisfy the access.
101    pub fn new() -> Self {
102        Self::default()
103    }
104
105    /// Require mutable access without any copy-on-write (fails on still-shared buffers).
106    pub fn no_copy(mut self) -> Self {
107        self.policy = AccessPolicy::zero_copy();
108        self
109    }
110}