Skip to main content

cubecl_environment/bytes/
base.rs

1//! A version of [`bytemuck::BoxBytes`] that is cloneable and allows trailing uninitialized elements.
2
3use crate::bytes::{
4    AccessError, AccessPolicy, Reader, Writer,
5    default_controller::{self, NativeAllocationController},
6    shared_arc::SharedAllocationController,
7};
8use crate::sync::Arc;
9use alloc::{boxed::Box, vec::Vec};
10use core::{
11    alloc::LayoutError,
12    mem::MaybeUninit,
13    ops::{Deref, DerefMut},
14    ptr::NonNull,
15};
16
17/// A buffer similar to `Box<[u8]>` that supports custom memory alignment and allows trailing uninitialized bytes.
18///
19/// `Bytes` is designed for efficient memory management in specialized contexts.
20/// It may use non-standard allocators, such as the CUDA SDK allocator for pinned memory, or leverage memory pooling to reduce allocation overhead.
21///
22/// # Safety
23///
24/// The first `len` bytes of the allocation are guaranteed to be initialized. Accessing bytes beyond `len` is undefined behavior unless explicitly initialized.
25pub struct Bytes {
26    /// The buffer used to store data.
27    controller: Box<dyn AllocationController>,
28    /// The length of data actually used and initialized in the current buffer.
29    len: usize,
30}
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq)]
33/// The kind of allocation behind the [Bytes] type.
34pub enum AllocationProperty {
35    /// A file is used to store the data.
36    File,
37    /// The native allocator of Rust is used.
38    Native,
39    /// Pinned memory is used.
40    Pinned,
41    /// The data still lives on a compute device and is only copied to host memory
42    /// lazily on first access (see [`ComputeClient::read_lazy`](https://docs.rs/cubecl-runtime)).
43    Device,
44    /// Another kind of memory is used.
45    Other,
46}
47
48/// Error when splitting an allocation.
49#[derive(Debug, Clone, Copy)]
50pub enum SplitError {
51    /// The offset isn't valid.
52    InvalidOffset,
53    /// The operation isn't supported.
54    Unsupported,
55}
56
57/// Error when taking a [view](Bytes::view) into an allocation.
58#[derive(Debug, Clone, Copy)]
59pub enum ViewError {
60    /// The range is out of bounds.
61    InvalidRange,
62    /// The backend can't produce a zero-copy window.
63    Unsupported,
64}
65
66/// Controls how [`Bytes::split`] behaves when the allocation can't be split in place.
67#[derive(Debug, Clone, Copy, PartialEq, Eq)]
68pub enum SplitPolicy {
69    /// Prefer zero-copy splits, allowing the allocation to be shared behind an
70    /// [`Arc`] when it can't be split natively.
71    ///
72    /// Mutating a resulting half then triggers a copy-on-write. This is the best
73    /// choice for read-only or clone-heavy use cases, where the extra copy is
74    /// never paid.
75    Shared,
76    /// Ensure both halves are independently owned, copying eagerly when a native
77    /// split isn't possible.
78    ///
79    /// This avoids a later copy-on-write surprise when the halves will be
80    /// mutated. Prefer it when you split *in order to* mutate, so the copy is
81    /// paid once, up front, instead of lazily on first write.
82    Owned,
83}
84
85/// Defines how an ``Allocation`` can be controlled.
86///
87/// This trait enables type erasure of the allocator after an ``Allocation`` is created, while still
88/// providing methods to modify or manage an existing ``Allocation``.
89pub trait AllocationController {
90    /// The alignment this allocation was created with.
91    fn alloc_align(&self) -> usize;
92
93    /// Returns memory property for the current allocation.
94    fn property(&self) -> AllocationProperty;
95
96    /// Returns a mutable view of the memory of the whole allocation, subject to `policy`.
97    ///
98    /// Returns [`AccessError::WouldCopy`] if satisfying the access would require a copy
99    /// (e.g. copy-on-write of shared storage) that `policy` forbids, or [`AccessError::Read`]
100    /// if materializing lazy storage fails.
101    ///
102    /// # Safety
103    ///
104    /// Must only write initialized data to the buffer.
105    unsafe fn memory_mut(
106        &mut self,
107        policy: AccessPolicy,
108    ) -> Result<&mut [MaybeUninit<u8>], AccessError>;
109
110    /// Returns a view of the memory of the whole allocation, subject to `policy`.
111    ///
112    /// Returns [`AccessError::WouldCopy`] if satisfying the access would require a copy
113    /// (e.g. materializing lazy storage) that `policy` forbids, or [`AccessError::Read`] if
114    /// materializing fails.
115    fn memory(&self, policy: AccessPolicy) -> Result<&[MaybeUninit<u8>], AccessError>;
116
117    /// The total byte capacity of the allocation, WITHOUT materializing lazy storage.
118    ///
119    /// The default is correct for already-resident controllers (where `memory()` is free);
120    /// lazy controllers must override it to report their size cheaply so that querying the
121    /// capacity never triggers a copy.
122    fn capacity(&self) -> usize {
123        match self.memory(AccessPolicy::allow_copy()) {
124            Ok(memory) => memory.len(),
125            // A resident controller using this default must never error here; a lazy
126            // controller is expected to override `capacity` and not reach this path.
127            Err(err) => {
128                debug_assert!(false, "capacity(): resident host access failed: {err:?}");
129                0
130            }
131        }
132    }
133
134    /// Splits the current allocation in multiple separate allocations.
135    #[allow(clippy::type_complexity)]
136    fn split(
137        &mut self,
138        _offset: usize,
139    ) -> Result<(Box<dyn AllocationController>, Box<dyn AllocationController>), SplitError> {
140        Err(SplitError::Unsupported)
141    }
142
143    /// Returns a zero-copy controller over the sub-range `[start, end)` of this
144    /// allocation when the backend supports cheap sub-views (files and shared
145    /// buffers), borrowing `&self`. Returns `None` otherwise.
146    fn view(&self, _start: usize, _end: usize) -> Option<Box<dyn AllocationController>> {
147        None
148    }
149
150    /// Duplicates the current allocation with a clone on write strategy if the allocation
151    /// controller supports it.
152    fn duplicate(&self) -> Option<Box<dyn AllocationController>> {
153        None
154    }
155
156    /// Reads the data from the current allocation controller and copy its content into the provided
157    /// buffer.
158    ///
159    /// # Safety
160    ///
161    /// Ensures the length provided reflect initialized values in the current allocation controller.
162    unsafe fn copy_into(&self, buf: &mut [u8]) {
163        let len = buf.len();
164        let memory = self
165            .memory(AccessPolicy::default())
166            .expect("copy_into: host access failed");
167        let memory_slice = &memory[0..len];
168
169        // SAFETY: By construction, bytes up to len are initialized.
170        let data = unsafe {
171            core::slice::from_raw_parts(memory_slice.as_ptr().cast(), memory_slice.len())
172        };
173        buf.copy_from_slice(data);
174    }
175
176    /// Extends the provided ``Allocation`` to a new size with specified alignment.
177    ///
178    /// # Errors
179    ///
180    /// Returns an [`AllocationError`] if the extension fails (e.g., due to insufficient memory or
181    /// unsupported operation by the allocator).
182    #[allow(unused_variables)]
183    fn grow(&mut self, size: usize, align: usize) -> Result<(), AllocationError> {
184        Err(AllocationError::UnsupportedOperation)
185    }
186
187    /// Indicates whether the allocation uses the Rust [alloc](alloc) crate and can be safely
188    /// managed by another data structure.
189    ///
190    /// If `true`, the allocation is not managed by a memory pool and can be safely deallocated
191    /// using the [alloc](alloc) crate.
192    ///
193    /// # Notes
194    ///
195    /// This allows the allocation's pointer to be converted into a native Rust `Vec` without
196    /// requiring a new allocation.
197    ///
198    /// Implementing this incorrectly is unsafe and may lead to undefined behavior.
199    fn try_detach(&mut self) -> Option<NonNull<u8>> {
200        None
201    }
202}
203
204/// Errors that may occur during memory allocation operations.
205///
206/// This enum represents possible failure cases when manipulating an ``Allocation`` using an
207/// [`AllocationController`].
208#[derive(Debug, Clone, PartialEq)]
209pub enum AllocationError {
210    /// The requested allocation operation is not supported by the allocator.
211    ///
212    /// This may occur, for example, when attempting to grow an allocation with an allocator that
213    /// does not support resizing.
214    UnsupportedOperation,
215
216    /// The allocation failed due to insufficient memory.
217    ///
218    /// This typically indicates that the system or allocator could not provide the requested
219    /// amount of memory.
220    OutOfMemory,
221}
222
223impl Bytes {
224    /// Splits the current allocation at the given offset.
225    ///
226    /// Controllers that support a native split (such as files and shared
227    /// buffers) always split in place. When they can't, the behaviour of the
228    /// fallback is selected by `policy`:
229    ///
230    /// - [`SplitPolicy::Shared`] shares the allocation behind an [`Arc`] (via
231    ///   [`Self::shared`]) so both halves reference the same backing memory
232    ///   without copying. This is what makes [`Self::split`] work for native
233    ///   allocations whose element alignment is larger than one byte. Mutating a
234    ///   half then triggers a copy-on-write.
235    /// - [`SplitPolicy::Owned`] copies eagerly so each half is independently
236    ///   owned and can be mutated without a later copy-on-write.
237    ///
238    /// In both policies, when the allocation can be detached into a `Vec`
239    /// without copying, that zero-copy path is taken first.
240    pub fn split(
241        self,
242        offset: usize,
243        policy: SplitPolicy,
244    ) -> Result<(Bytes, Bytes), (Bytes, SplitError)> {
245        if offset > self.len {
246            return Err((self, SplitError::InvalidOffset));
247        }
248        match policy {
249            SplitPolicy::Shared => self.split_shared(offset),
250            SplitPolicy::Owned => self.split_owned(offset),
251        }
252    }
253
254    /// Split preferring zero-copy: native split, else detach into a `Vec`, else
255    /// share behind an [`Arc`]. See [`SplitPolicy::Shared`].
256    fn split_shared(mut self, offset: usize) -> Result<(Bytes, Bytes), (Bytes, SplitError)> {
257        let right_len = self.len - offset;
258        match self.controller.split(offset) {
259            Ok((left, right)) => unsafe {
260                Ok((
261                    Bytes::from_controller(left, offset),
262                    Bytes::from_controller(right, right_len),
263                ))
264            },
265            Err(_) => match self.try_into_vec::<u8>() {
266                Ok(mut left) => {
267                    let right = left.split_off(offset);
268
269                    Ok((Bytes::from_bytes_vec(left), Bytes::from_bytes_vec(right)))
270                }
271                // The allocation can't be detached into a `Vec` (e.g. a native
272                // allocation with element alignment > 1, or already shared
273                // data). Share it behind an `Arc` so both halves reference the
274                // same backing memory without copying.
275                Err(this) => Ok(this.shared_split(offset)),
276            },
277        }
278    }
279
280    /// Split ensuring both halves are independently owned, copying eagerly when
281    /// a zero-copy detach isn't possible. See [`SplitPolicy::Owned`].
282    fn split_owned(self, offset: usize) -> Result<(Bytes, Bytes), (Bytes, SplitError)> {
283        match self.try_into_vec::<u8>() {
284            Ok(mut left) => {
285                let right = left.split_off(offset);
286
287                Ok((Bytes::from_bytes_vec(left), Bytes::from_bytes_vec(right)))
288            }
289            // The allocation can't be detached into a `Vec`. Copy each half into
290            // its own native allocation so they can be mutated without a later
291            // copy-on-write.
292            Err(this) => {
293                let align = this.align();
294                match (
295                    Self::try_from_data(align, &this[..offset]),
296                    Self::try_from_data(align, &this[offset..]),
297                ) {
298                    (Ok(left), Ok(right)) => Ok((left, right)),
299                    // A layout error here is practically unreachable: the data
300                    // already lives in a valid allocation of this alignment.
301                    _ => Err((this, SplitError::Unsupported)),
302                }
303            }
304        }
305    }
306
307    /// Returns the sub-range `[start, end)` as a zero-copy [`Bytes`] window.
308    ///
309    /// The returned [`Bytes`] is independently owned (it shares the backing
310    /// storage by reference count / re-opened file handle rather than borrowing
311    /// `self`), so it can outlive this reference.
312    ///
313    /// Returns [`ViewError::Unsupported`] when the backend can't produce a
314    /// zero-copy window (e.g. a plain heap allocation); [`Self::shared`] it first
315    /// to make views available.
316    pub fn view(&self, start: usize, end: usize) -> Result<Bytes, ViewError> {
317        if start > end || end > self.len {
318            return Err(ViewError::InvalidRange);
319        }
320        let len = end - start;
321
322        match self.controller.view(start, end) {
323            // SAFETY: the sub-view controller reports exactly `len` bytes.
324            Some(controller) => Ok(unsafe { Bytes::from_controller(controller, len) }),
325            None => Err(ViewError::Unsupported),
326        }
327    }
328
329    /// Shares the current allocation behind an [`Arc`], returning a new [`Bytes`]
330    /// that references the same data without copying.
331    ///
332    /// Cloning and [splitting](Self::split) the returned [`Bytes`] is cheap
333    /// (reference counted, zero-copy). Because the data is shared,
334    /// [`Self::try_into_vec`] will never succeed on the result, and mutating it
335    /// triggers a copy-on-write into a private buffer.
336    pub fn shared(self) -> Self {
337        let len = self.len;
338        let controller = SharedAllocationController::new(Arc::new(self), 0, len);
339
340        Self {
341            controller: Box::new(controller),
342            len,
343        }
344    }
345
346    /// Shares the allocation behind an [`Arc`] and returns two zero-copy views
347    /// split at `offset`. The caller must ensure `offset <= self.len`.
348    fn shared_split(self, offset: usize) -> (Bytes, Bytes) {
349        let len = self.len;
350        let inner = Arc::new(self);
351        let left = SharedAllocationController::new(inner.clone(), 0, offset);
352        let right = SharedAllocationController::new(inner, offset, len - offset);
353
354        // SAFETY: each view reports exactly its own length as initialized memory.
355        unsafe {
356            (
357                Bytes::from_controller(Box::new(left), offset),
358                Bytes::from_controller(Box::new(right), len - offset),
359            )
360        }
361    }
362
363    #[cfg(feature = "std")]
364    /// Creates bytes from a file at the given offset of the given size.
365    pub fn from_file<P: Into<std::path::PathBuf>>(file: P, size: u64, offset: u64) -> Self {
366        let controller = crate::bytes::file::FileAllocationController::new(file, size, offset);
367
368        Self {
369            controller: Box::new(controller),
370            len: size as usize,
371        }
372    }
373
374    /// Creates bytes from a shared [`bytes::Bytes`] buffer (zero-copy).
375    ///
376    /// This is useful for zero-copy tensor loading from:
377    /// - Static embedded data via [`bytes::Bytes::from_static`]
378    /// - Memory-mapped files
379    /// - Any other [`bytes::Bytes`] source
380    ///
381    /// The allocation property is used by GPU backends to optimize data transfers:
382    /// - [`AllocationProperty::File`]: Uses pinned memory staging buffers for faster
383    ///   DMA transfers (useful for memory-mapped files)
384    /// - [`AllocationProperty::Native`]: Data is in heap memory
385    /// - [`AllocationProperty::Other`]: Unknown backing storage
386    ///
387    /// # Example
388    ///
389    /// ```
390    /// use cubecl_environment::bytes::{Bytes, AllocationProperty};
391    ///
392    /// // Memory-mapped file data - use File property for optimized GPU transfers
393    /// let mmap_bytes = bytes::Bytes::from_static(&[1, 2, 3, 4]); // pretend this is mmap
394    /// let bytes = Bytes::from_shared(mmap_bytes, AllocationProperty::File);
395    /// assert!(matches!(bytes.property(), AllocationProperty::File));
396    /// ```
397    #[cfg(feature = "shared-bytes")]
398    pub fn from_shared(bytes: bytes::Bytes, property: AllocationProperty) -> Self {
399        let len = bytes.len();
400        let controller =
401            crate::bytes::shared::SharedBytesAllocationController::new(bytes, property);
402
403        Self {
404            controller: Box::new(controller),
405            len,
406        }
407    }
408
409    /// The size of the allocation.
410    ///
411    /// # Notes
412    ///
413    /// This is used so that calling `bytes.len()` doesn't trigger [Deref], which may be expensive.
414    #[allow(clippy::len_without_is_empty)]
415    pub fn len(&self) -> usize {
416        self.len
417    }
418
419    /// Read the bytes, configured by `reader`. Returns the initialized `[0, len)` slice.
420    ///
421    /// Unlike [`Deref`], this is fallible: it surfaces an [`AccessError`] instead of
422    /// panicking, and a [`Reader::no_copy`] reader returns [`AccessError::WouldCopy`] rather
423    /// than silently materializing lazy storage.
424    pub fn read(&self, reader: Reader) -> Result<&[u8], AccessError> {
425        let memory = &self.controller.memory(reader.policy)?[0..self.len];
426        // SAFETY: bytes up to `len` are initialized by construction.
427        Ok(unsafe { core::slice::from_raw_parts(memory.as_ptr().cast(), memory.len()) })
428    }
429
430    /// Mutably access the bytes, configured by `writer`. Returns the initialized `[0, len)`
431    /// slice. A [`Writer::no_copy`] writer fails with [`AccessError::WouldCopy`] on
432    /// still-shared buffers instead of triggering copy-on-write.
433    pub fn write(&mut self, writer: Writer) -> Result<&mut [u8], AccessError> {
434        let len = self.len;
435        // SAFETY: we only ever expose initialized memory in `[0, len)`.
436        let memory = unsafe { self.controller.memory_mut(writer.policy) }?;
437        let memory = &mut memory[0..len];
438        // SAFETY: bytes up to `len` are initialized by construction.
439        Ok(unsafe { core::slice::from_raw_parts_mut(memory.as_mut_ptr().cast(), memory.len()) })
440    }
441
442    /// Default host access (allow copy), panicking on failure — backs [`Deref`].
443    fn memory_default(&self) -> &[MaybeUninit<u8>] {
444        self.controller
445            .memory(AccessPolicy::default())
446            .expect("bytes: host access failed")
447    }
448
449    /// Default mutable host access (allow copy-on-write), panicking on failure — backs
450    /// [`DerefMut`].
451    ///
452    /// # Safety
453    ///
454    /// Caller must only write initialized data into the returned slice.
455    unsafe fn memory_mut_default(&mut self) -> &mut [MaybeUninit<u8>] {
456        unsafe { self.controller.memory_mut(AccessPolicy::default()) }
457            .expect("bytes: host access failed")
458    }
459
460    /// Copy the data from the current allocation to the provided [Bytes].
461    pub fn copy_into(&self, other: &mut Self) {
462        unsafe {
463            self.controller.copy_into(other);
464        }
465    }
466
467    /// Retrieves the allocation property of the given allocation.
468    pub fn property(&self) -> AllocationProperty {
469        self.controller.property()
470    }
471    /// Creates the type from its raw parts.
472    ///
473    /// # Safety
474    ///
475    /// This function is highly unsafe, the provided length must be the actual number of bytes
476    /// initialized in the `AllocationController`.
477    ///
478    /// Note: we intentionally do not assert `len <= controller.memory().len()` here, as
479    /// `memory()` may force a lazy controller to materialize its data (e.g. a device-backed
480    /// allocation), defeating the laziness this constructor is meant to preserve.
481    pub unsafe fn from_controller(controller: Box<dyn AllocationController>, len: usize) -> Self {
482        Self { controller, len }
483    }
484
485    /// Create a sequence of [Bytes] from the memory representation of an unknown type of elements.
486    /// Prefer this over [`Self::from_elems`] when the datatype is not statically known and erased at runtime.
487    pub fn from_bytes_vec(bytes: Vec<u8>) -> Self {
488        let mut bytes = Self::from_elems(bytes);
489        // TODO: this method could be datatype aware and enforce a less strict alignment.
490        // On most platforms, this alignment check is fulfilled either way though, so
491        // the benefits of potentially saving a memcopy are negligible.
492        bytes
493            .try_enforce_runtime_align(default_controller::MAX_ALIGN)
494            .unwrap();
495        bytes
496    }
497
498    /// Erase the element type of a vector by converting into a sequence of [Bytes].
499    ///
500    /// In case the element type is not statically known at runtime, prefer to use [`Self::from_bytes_vec`].
501    pub fn from_elems<E>(elems: Vec<E>) -> Self
502    where
503        // NoUninit implies Copy
504        E: bytemuck::NoUninit + Send + Sync,
505    {
506        let _: () = const {
507            assert!(
508                core::mem::align_of::<E>() <= default_controller::MAX_ALIGN,
509                "element type not supported due to too large alignment"
510            );
511        };
512
513        // Note: going through a Box as in Vec::into_boxed_slice would re-allocate on excess capacity. Avoid that.
514        let byte_len = elems.len() * core::mem::size_of::<E>();
515        let controller = NativeAllocationController::from_elems(elems);
516
517        Self {
518            controller: Box::new(controller),
519            len: byte_len,
520        }
521    }
522
523    /// Extend the byte buffer from a slice of bytes
524    pub fn extend_from_byte_slice(&mut self, bytes: &[u8]) {
525        self.extend_from_byte_slice_aligned(bytes, default_controller::MAX_ALIGN)
526    }
527
528    /// Get the total capacity, in bytes, of the wrapped allocation.
529    ///
530    /// This never materializes lazy storage (see [`AllocationController::capacity`]).
531    pub fn capacity(&self) -> usize {
532        self.controller.capacity()
533    }
534
535    /// Convert the bytes back into a vector. This requires that the type has the same alignment as the element
536    /// type this [Bytes] was initialized with.
537    /// This only returns with Ok(_) if the conversion can be done without a memcopy
538    pub fn try_into_vec<E: bytemuck::CheckedBitPattern + bytemuck::NoUninit>(
539        mut self,
540    ) -> Result<Vec<E>, Self> {
541        // See if the length is compatible.
542        // Use immutable validation to avoid triggering copy-on-write for SharedBytesAllocationController.
543        // Note: This still calls memory() via Deref, which may trigger file I/O for FileAllocationController.
544        let Ok(data) = bytemuck::checked::try_cast_slice::<_, E>(&self) else {
545            return Err(self);
546        };
547        let length = data.len();
548        // If so, try to convert the allocation to a vec. The data is already host-resident
549        // here (the cast above went through `Deref`), so `capacity()` is free.
550        let byte_capacity = self.controller.capacity();
551
552        let Some(capacity) = byte_capacity.checked_div(size_of::<E>()) else {
553            return Err(self);
554        };
555        if capacity * size_of::<E>() != byte_capacity {
556            return Err(self);
557        };
558        // Vec::from_raw_parts requires that the pointer was allocated with
559        // Layout::array::<E>(capacity). On drop, Vec deallocates with that
560        // layout. If our allocation used a different alignment, the dealloc
561        // layout won't match and that's UB per the GlobalAlloc contract:
562        // https://doc.rust-lang.org/std/alloc/trait.GlobalAlloc.html#safety-1
563        if self.controller.alloc_align() != align_of::<E>() {
564            return Err(self);
565        }
566
567        let Some(ptr) = self.controller.try_detach() else {
568            return Err(self);
569        };
570
571        // SAFETY:
572        // - ptr was allocated by the global allocator as per type-invariant
573        // - alloc_align == align_of::<E> (checked above), so Vec will dealloc
574        //   with the same layout as the original allocation.
575        // - capacity * size_of::<E> == layout.size()
576        // - 0 <= capacity
577        // - length was computed from the bytemuck-ed slice into this allocation
578        // - the layout represents a valid allocation, hence has allocation size less than isize::MAX
579        let vec = unsafe { Vec::from_raw_parts(ptr.as_ptr().cast(), length, capacity) };
580        Ok(vec)
581    }
582
583    /// Get the alignment of the wrapped allocation.
584    pub fn align(&self) -> usize {
585        self.controller.alloc_align()
586    }
587
588    /// Extend the byte buffer from a slice of bytes.
589    ///
590    /// This is used internally to preserve the alignment of the memory layout when matching elements
591    /// are extended. Prefer [`Self::extend_from_byte_slice`] otherwise.
592    pub fn extend_from_byte_slice_aligned(&mut self, bytes: &[u8], align: usize) {
593        debug_assert!(align.is_power_of_two(), "alignment must be a power of two");
594        debug_assert!(
595            align <= default_controller::MAX_ALIGN,
596            "alignment exceeds maximum supported alignment"
597        );
598
599        let additional = bytes.len();
600        self.reserve(additional, align);
601
602        let len = self.len();
603        let new_cap = len.wrapping_add(additional); // Can not overflow, as we've just successfully reserved sufficient space for it
604        debug_assert!(
605            new_cap <= self.capacity(),
606            "new capacity must not exceed allocated capacity"
607        );
608
609        unsafe {
610            // SAFETY: Will only write initialized memory to this ptr.
611            let uninit_spare = &mut self.memory_mut_default()[len..new_cap];
612            // SAFETY: reinterpreting the slice as a MaybeUninit<u8>.
613            // See also #![feature(maybe_uninit_write_slice)], which would replace this with safe code
614            uninit_spare.copy_from_slice(core::slice::from_raw_parts(
615                bytes.as_ptr().cast(),
616                additional,
617            ));
618        };
619        self.len = new_cap;
620    }
621
622    /// Copy an existing slice of data into Bytes that are aligned to `align`
623    fn try_from_data(align: usize, data: &[u8]) -> Result<Self, LayoutError> {
624        let controller = NativeAllocationController::alloc_with_data(data, align)?;
625
626        Ok(Self {
627            controller: Box::new(controller),
628            len: data.len(),
629        })
630    }
631
632    /// Ensure the allocation's reported alignment is at least `align`, reallocating
633    /// into a fresh controller if not. We check the controller's reported alignment
634    /// (not the raw pointer) because downstream callers such as `try_into_vec::<E>`
635    /// depend on `alloc_align()` matching the element alignment.
636    fn try_enforce_runtime_align(&mut self, align: usize) -> Result<(), LayoutError> {
637        if self.controller.alloc_align() >= align {
638            return Ok(());
639        }
640        *self = Self::try_from_data(align, self)?;
641        Ok(())
642    }
643
644    fn reserve(&mut self, additional: usize, align: usize) {
645        debug_assert!(
646            align <= default_controller::MAX_ALIGN,
647            "alignment exceeds maximum supported alignment"
648        );
649
650        let needs_to_grow = additional > self.capacity().wrapping_sub(self.len());
651        if !needs_to_grow {
652            return;
653        }
654        let Some(required_cap) = self.len().checked_add(additional) else {
655            default_controller::alloc_overflow()
656        };
657        // guarantee exponential growth for amortization
658        let new_cap = required_cap.max(self.capacity() * 2);
659        let new_cap = new_cap.max(align); // Small allocations would be pointless
660
661        match self.controller.grow(new_cap, align) {
662            Ok(()) => {}
663            Err(_err) => {
664                let new_controller: Box<dyn AllocationController> = Box::new(
665                    NativeAllocationController::alloc_with_capacity(new_cap, align).unwrap(),
666                );
667                let mut new_bytes = Self {
668                    controller: new_controller,
669                    len: self.len,
670                };
671                // Copy memory into new bytes.
672                new_bytes.copy_from_slice(&*self);
673                *self = new_bytes;
674            }
675        }
676    }
677}
678
679impl Deref for Bytes {
680    type Target = [u8];
681
682    fn deref(&self) -> &Self::Target {
683        let memory = &self.memory_default()[0..self.len];
684        // SAFETY: By construction, bytes up to len are initialized.
685        unsafe { core::slice::from_raw_parts(memory.as_ptr().cast(), memory.len()) }
686    }
687}
688
689impl DerefMut for Bytes {
690    fn deref_mut(&mut self) -> &mut Self::Target {
691        let len = self.len;
692        // SAFETY: We only expose this as initialized memory so cannot write uninitialized memory to this slice.
693        let slice = unsafe { self.memory_mut_default() };
694        // Get initialized part of this slice.
695        let memory = &mut slice[0..len];
696        // SAFETY: By construction, bytes up to len are initialized.
697        unsafe { core::slice::from_raw_parts_mut(memory.as_mut_ptr().cast(), memory.len()) }
698    }
699}
700
701// SAFETY: Bytes behaves like a Box<[u8]> and can contain only elements that are themselves Send
702unsafe impl Send for Bytes {}
703// SAFETY: Bytes behaves like a Box<[u8]> and can contain only elements that are themselves Sync
704unsafe impl Sync for Bytes {}
705
706fn debug_from_fn<F: Fn(&mut core::fmt::Formatter<'_>) -> core::fmt::Result>(
707    f: F,
708) -> impl core::fmt::Debug {
709    // See also: std::fmt::from_fn
710    struct FromFn<F>(F);
711    impl<F> core::fmt::Debug for FromFn<F>
712    where
713        F: Fn(&mut core::fmt::Formatter<'_>) -> core::fmt::Result,
714    {
715        fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
716            (self.0)(f)
717        }
718    }
719    FromFn(f)
720}
721
722impl core::fmt::Debug for Bytes {
723    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
724        let data = &**self;
725        let fmt_data = move |f: &mut core::fmt::Formatter<'_>| {
726            if data.len() > 3 {
727                // There is a nightly API `debug_more_non_exhaustive` which has `finish_non_exhaustive`
728                f.debug_list().entries(&data[0..3]).entry(&"...").finish()
729            } else {
730                f.debug_list().entries(data).finish()
731            }
732        };
733        f.debug_struct("Bytes")
734            .field("data", &debug_from_fn(fmt_data))
735            .field("len", &self.len)
736            .finish()
737    }
738}
739
740impl serde::Serialize for Bytes {
741    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
742    where
743        S: serde::Serializer,
744    {
745        serde_bytes::serialize(self.deref(), serializer)
746    }
747}
748
749impl<'de> serde::Deserialize<'de> for Bytes {
750    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
751    where
752        D: serde::Deserializer<'de>,
753    {
754        #[cold]
755        fn too_large<E: serde::de::Error>(len: usize, align: usize) -> E {
756            // max_length = largest multiple of align that is <= isize::MAX
757            // align is a power of 2, hence a multiple has the lower bits unset. Mask them off to find the largest multiple
758            let max_length = (isize::MAX as usize) & !(align - 1);
759            E::custom(core::format_args!(
760                "length too large: {len}. Expected at most {max_length} bytes"
761            ))
762        }
763
764        // TODO: we can possibly avoid one copy here by deserializing into an existing, correctly aligned, slice of bytes.
765        // We might not be able to predict the length of the data, hence it's far more convenient to let `Vec` handle the growth and re-allocations.
766        // Further, on a lot of systems, the allocator naturally aligns data to some reasonably large alignment, where no further copy is then
767        // necessary.
768        let data: Vec<u8> = serde_bytes::deserialize(deserializer)?;
769        // When deserializing, we over-align the data. This saves us from having to encode the alignment (which is platform-dependent in any case).
770        // If we had more context information here, we could enforce some (smaller) alignment per data type. But this information is only available
771        // in `TensorData`. Moreover it depends on the Deserializer there whether the datatype or data comes first.
772        let align = default_controller::MAX_ALIGN;
773        let mut bytes = Self::from_elems(data);
774        bytes
775            .try_enforce_runtime_align(align)
776            .map_err(|_| too_large(bytes.len(), align))?;
777        Ok(bytes)
778    }
779}
780
781impl Clone for Bytes {
782    fn clone(&self) -> Self {
783        if let Some(controller) = self.controller.duplicate() {
784            return Self {
785                controller,
786                len: self.len,
787            };
788        }
789
790        // unwrap here: the layout is valid as it has the alignment & size of self
791        Self::try_from_data(self.align(), self.deref()).unwrap()
792    }
793}
794
795impl PartialEq for Bytes {
796    fn eq(&self, other: &Self) -> bool {
797        self.deref() == other.deref()
798    }
799}
800
801impl Eq for Bytes {}
802
803// The type is no-std; its tests need std (test_log, std collections).
804#[cfg(all(test, feature = "std"))]
805mod tests {
806    use super::{Bytes, SplitPolicy, ViewError};
807    use alloc::{vec, vec::Vec};
808
809    const _CONST_ASSERTS: fn() = || {
810        fn test_send<T: Send>() {}
811        fn test_sync<T: Sync>() {}
812        test_send::<Bytes>();
813        test_sync::<Bytes>();
814    };
815
816    fn test_serialization_roundtrip(bytes: &Bytes) {
817        let mut serialized = Vec::new();
818        ciborium::ser::into_writer(bytes, &mut serialized).expect("serialization to succeed");
819        let roundtripped: Bytes = ciborium::de::from_reader(&mut serialized.as_slice())
820            .expect("deserialization to succeed");
821        assert_eq!(
822            bytes, &roundtripped,
823            "roundtripping through serialization didn't lead to equal Bytes"
824        );
825    }
826
827    #[test_log::test]
828    fn test_serialization() {
829        test_serialization_roundtrip(&Bytes::from_elems::<i32>(vec![]));
830        test_serialization_roundtrip(&Bytes::from_elems(vec![0xdead, 0xbeaf]));
831    }
832
833    #[test_log::test]
834    fn test_into_vec() {
835        // We test an edge case here, where the capacity (but not actual size) makes it impossible to convert to a vec
836        let mut bytes = Vec::with_capacity(6);
837        let actual_cap = bytes.capacity();
838        bytes.extend_from_slice(&[0, 1, 2, 3]);
839        let mut bytes = Bytes::from_elems::<u8>(bytes);
840
841        bytes = bytes
842            .try_into_vec::<[u8; 0]>()
843            .expect_err("Conversion should not succeed for a zero-sized type");
844        if actual_cap % 4 != 0 {
845            // We most likely get actual_cap == 6, we can't force Vec to actually do that. Code coverage should complain if the actual test misses this
846            bytes = bytes.try_into_vec::<[u8; 4]>().err().unwrap_or_else(|| {
847                panic!("Conversion should not succeed due to capacity {actual_cap} not fitting a whole number of elements");
848            });
849        }
850        bytes = bytes
851            .try_into_vec::<u16>()
852            .expect_err("Conversion should not succeed due to mismatched alignment");
853        bytes = bytes.try_into_vec::<[u8; 3]>().expect_err(
854            "Conversion should not succeed due to size not fitting a whole number of elements",
855        );
856        let bytes = bytes.try_into_vec::<[u8; 2]>().expect("Conversion should succeed for bit-convertible types of equal alignment and compatible size");
857        assert_eq!(bytes, &[[0, 1], [2, 3]]);
858    }
859
860    #[test_log::test]
861    fn test_grow() {
862        let mut bytes = Bytes::from_elems::<u8>(vec![]);
863        bytes.extend_from_byte_slice(&[0, 1, 2, 3]);
864        assert_eq!(bytes[..], [0, 1, 2, 3][..]);
865
866        let mut bytes = Bytes::from_elems(vec![42u8; 4]);
867        bytes.extend_from_byte_slice(&[0, 1, 2, 3]);
868        assert_eq!(bytes[..], [42, 42, 42, 42, 0, 1, 2, 3][..]);
869    }
870
871    #[test_log::test]
872    fn test_large_elems() {
873        let mut bytes = Bytes::from_elems(vec![42u128]);
874        const TEST_BYTES: [u8; 16] = [
875            0x12, 0x90, 0x78, 0x56, 0x34, 0x12, 0x90, 0x78, 0x56, 0x34, 0x12, 0x90, 0x78, 0x56,
876            0x34, 0x12,
877        ];
878        bytes.extend_from_byte_slice(&TEST_BYTES);
879        let vec = bytes.try_into_vec::<u128>().unwrap();
880        assert_eq!(vec, [42u128, u128::from_ne_bytes(TEST_BYTES)]);
881    }
882
883    #[test_log::test]
884    fn test_split_and_use() {
885        let bytes = Bytes::from_elems(vec![0u8, 1, 2, 3, 4, 5, 6, 7]);
886        let (left, right) = bytes.split(4, SplitPolicy::Shared).unwrap();
887        assert_eq!(&left[..], &[0, 1, 2, 3]);
888        assert_eq!(&right[..], &[4, 5, 6, 7]);
889        let left2 = left.clone();
890        assert_eq!(&left2[..], &[0, 1, 2, 3]);
891    }
892
893    #[test_log::test]
894    fn test_split_at_zero() {
895        let bytes = Bytes::from_elems(vec![10u8, 20, 30, 40]);
896        let (left, right) = bytes.split(0, SplitPolicy::Shared).unwrap();
897        assert_eq!(left.len(), 0);
898        assert_eq!(&right[..], &[10, 20, 30, 40]);
899    }
900
901    /// A native allocation with element alignment > 1 can't be detached into a
902    /// `Vec<u8>`, so it used to fail to split. With [`SplitPolicy::Shared`] it
903    /// now shares behind an `Arc`.
904    #[test_log::test]
905    fn test_split_native_over_aligned_shared() {
906        let bytes = Bytes::from_elems(vec![0u32, 1, 2, 3]);
907        let (left, right) = bytes.split(8, SplitPolicy::Shared).unwrap();
908        assert_eq!(&left[..], &[0, 0, 0, 0, 1, 0, 0, 0]);
909        assert_eq!(&right[..], &[2, 0, 0, 0, 3, 0, 0, 0]);
910        // The shared halves stay usable after cloning.
911        assert_eq!(&left.clone()[..], &left[..]);
912    }
913
914    /// [`SplitPolicy::Owned`] copies eagerly so each half is independently owned
915    /// and can be mutated in place (no copy-on-write occurs on first write).
916    #[test_log::test]
917    fn test_split_native_over_aligned_owned() {
918        let bytes = Bytes::from_elems(vec![0u32, 1, 2, 3]);
919        let (mut left, mut right) = bytes.split(8, SplitPolicy::Owned).unwrap();
920        left[0] = 9;
921        right[0] = 8;
922        assert_eq!(&left[..], &[9, 0, 0, 0, 1, 0, 0, 0]);
923        assert_eq!(&right[..], &[8, 0, 0, 0, 3, 0, 0, 0]);
924    }
925
926    #[test_log::test]
927    fn test_split_at_end() {
928        let bytes = Bytes::from_elems(vec![10u8, 20, 30, 40]);
929        let (left, right) = bytes.split(4, SplitPolicy::Owned).unwrap();
930        assert_eq!(&left[..], &[10, 20, 30, 40]);
931        assert_eq!(right.len(), 0);
932    }
933
934    /// A plain heap allocation has no zero-copy window, so `view` reports
935    /// [`ViewError::Unsupported`] rather than silently copying. `view` only
936    /// borrows, so the original stays usable.
937    #[test_log::test]
938    fn test_view_heap_unsupported() {
939        let bytes = Bytes::from_elems(vec![0u8, 1, 2, 3, 4, 5, 6, 7]);
940        assert!(matches!(bytes.view(2, 5), Err(ViewError::Unsupported)));
941        // The original is still readable.
942        assert_eq!(&bytes[..], &[0, 1, 2, 3, 4, 5, 6, 7]);
943    }
944
945    /// Sharing first (the caller owns the data) makes a zero-copy `view`
946    /// available; mutating the window then copies on write, leaving the original
947    /// untouched.
948    #[test_log::test]
949    fn test_view_shared_then_mutate() {
950        let shared = Bytes::from_elems(vec![0u32, 1, 2, 3]).shared();
951        let mut view = shared.view(4, 12).unwrap();
952        view[0] = 9;
953        assert_eq!(&view[..], &[9, 0, 0, 0, 2, 0, 0, 0]);
954        // The original is untouched by the mutation.
955        assert_eq!(
956            &shared[..],
957            &[0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0]
958        );
959    }
960
961    #[test_log::test]
962    fn test_view_invalid_range() {
963        let bytes = Bytes::from_elems(vec![10u8, 20, 30, 40]);
964        // end past the length, and start > end, are both rejected.
965        assert!(bytes.view(0, 5).is_err());
966        assert!(bytes.view(3, 1).is_err());
967    }
968
969    /// `from_bytes_vec` enforces `MAX_ALIGN`, so converting the result to a Vec of
970    /// any type whose alignment is `<= MAX_ALIGN` must succeed. We iterate so the
971    /// test hits a range of underlying allocator addresses.
972    #[test_log::test]
973    fn test_from_bytes_vec_try_into_vec_aligned_type() {
974        for _ in 0..64 {
975            let bytes = Bytes::from_bytes_vec(vec![0u8; 16]);
976            let vec: Vec<u128> = bytes
977                .try_into_vec::<u128>()
978                .expect("MAX_ALIGN-aligned bytes must convert to Vec<u128>");
979            assert_eq!(vec.len(), 1);
980        }
981    }
982
983    #[test_log::test]
984    fn test_many_extends_with_growth() {
985        let mut bytes = Bytes::from_elems::<u8>(vec![]);
986        for i in 0u8..=255 {
987            bytes.extend_from_byte_slice(&[i]);
988        }
989        assert_eq!(bytes.len(), 256);
990        assert_eq!(bytes[0], 0);
991        assert_eq!(bytes[255], 255);
992    }
993}