Skip to main content

teeny_cuda/device/
buffer.rs

1/*
2 * Copyright (c) 2026 Teenygrad.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *   http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17use std::marker::PhantomData;
18
19use teeny_core::{device::buffer::Buffer, dtype::Num};
20
21use crate::device::mem::{self, DevicePtr};
22use crate::errors::{Error, Result};
23
24/// A device-side buffer holding `count` elements of type `N`.
25///
26/// The allocation size is derived from `N::BITS`: `count * N::BITS / 8` bytes.
27/// Memory is freed automatically on drop.
28pub struct CudaBuffer<'a, N: Num> {
29    ptr: DevicePtr,
30    count: usize,
31    _unused: PhantomData<&'a ()>,
32    _num: PhantomData<N>,
33}
34
35impl<'a, N: Num> CudaBuffer<'a, N> {
36    /// Allocates a new device buffer for `count` elements of `N`.
37    pub fn try_new(count: usize) -> Result<Self> {
38        let byte_size = count * N::BITS as usize / 8;
39        let ptr = mem::alloc(byte_size)?;
40        Ok(Self {
41            ptr,
42            count,
43            _unused: PhantomData,
44            _num: PhantomData,
45        })
46    }
47
48    /// The underlying device pointer.
49    pub fn as_device_ptr(&self) -> DevicePtr {
50        self.ptr
51    }
52
53    /// The number of elements this buffer holds.
54    pub fn count(&self) -> usize {
55        self.count
56    }
57}
58
59impl<'a, N: Num> Drop for CudaBuffer<'a, N> {
60    fn drop(&mut self) {
61        if let Err(e) = mem::free(self.ptr) {
62            eprintln!("Failed to free CUDA buffer: {e}");
63        }
64    }
65}
66
67impl<'a, N: Num> Buffer<'a, N> for CudaBuffer<'a, N> {
68    fn to_device(&mut self, src: &[N]) -> teeny_core::errors::Result<()> {
69        if src.len() > self.count {
70            return Err(Error::BufferOverflow {
71                src: src.len(),
72                buf: self.count,
73            }
74            .into());
75        }
76        // SAFETY: src slice is valid for src.len() reads by construction.
77        unsafe { mem::copy_h_to_d(self.ptr, src.as_ptr(), src.len()) }
78    }
79
80    fn to_host(&self, dst: &mut [N]) -> teeny_core::errors::Result<()> {
81        // SAFETY: dst slice is valid for dst.len() writes by construction.
82        unsafe { mem::copy_d_to_h(dst.as_mut_ptr(), self.ptr, dst.len()) }
83    }
84}