use std::{marker::PhantomData, sync::Arc};
use crate::{Context, Error, Result, Stream};
mod transfer;
mod sealed {
pub trait Sealed {}
}
pub trait DeviceElement: sealed::Sealed + Copy + Send + Sync + 'static {}
macro_rules! device_elements {
($($element:ty),+ $(,)?) => {$ (
impl sealed::Sealed for $element {}
impl DeviceElement for $element {}
)+ };
}
device_elements!(
u8,
i8,
u16,
i16,
u32,
i32,
u64,
i64,
usize,
isize,
f32,
f64,
half::f16,
half::bf16,
);
#[derive(Clone, Debug)]
pub struct MemoryPool {
native: mircuda_sys::MemoryPool,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct MemoryPoolStats {
pub reserved: u64,
pub used: u64,
}
#[derive(Clone, Debug)]
pub struct DeviceBuffer<T: DeviceElement> {
pub(crate) native: Arc<mircuda_sys::DeviceBuffer>,
len: usize,
marker: PhantomData<T>,
}
#[derive(Debug)]
pub struct PinnedBuffer<T: DeviceElement> {
pub(crate) native: mircuda_sys::PinnedBuffer,
len: usize,
marker: PhantomData<T>,
}
impl Context {
pub fn default_memory_pool(&self) -> Result<MemoryPool> {
Ok(MemoryPool {
native: self.native.default_memory_pool()?,
})
}
pub fn allocate_pinned<T: DeviceElement>(&self, len: usize) -> Result<PinnedBuffer<T>> {
let bytes = allocation_bytes::<T>(len)?;
Ok(PinnedBuffer {
native: self.native.allocate_pinned(bytes)?,
len,
marker: PhantomData,
})
}
}
impl MemoryPool {
pub fn set_release_threshold(&self, bytes: u64) -> Result<()> {
Ok(self.native.set_release_threshold(bytes)?)
}
pub fn stats(&self) -> Result<MemoryPoolStats> {
let stats = self.native.stats()?;
Ok(MemoryPoolStats {
reserved: stats.reserved,
used: stats.used,
})
}
pub fn trim_to(&self, bytes: usize) -> Result<()> {
Ok(self.native.trim_to(bytes)?)
}
pub fn allocate<T: DeviceElement>(
&self,
stream: &Stream,
len: usize,
) -> Result<DeviceBuffer<T>> {
self.allocate_with(stream, len, false)
}
pub fn allocate_zeroed<T: DeviceElement>(
&self,
stream: &Stream,
len: usize,
) -> Result<DeviceBuffer<T>> {
self.allocate_with(stream, len, true)
}
fn allocate_with<T: DeviceElement>(
&self,
stream: &Stream,
len: usize,
zeroed: bool,
) -> Result<DeviceBuffer<T>> {
let bytes = allocation_bytes::<T>(len)?;
Ok(DeviceBuffer {
native: Arc::new(self.native.allocate(&stream.native, bytes, zeroed)?),
len,
marker: PhantomData,
})
}
}
impl<T: DeviceElement> DeviceBuffer<T> {
#[must_use]
pub const fn len(&self) -> usize {
self.len
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.len == 0
}
#[must_use]
pub fn bytes(&self) -> usize {
self.native.bytes()
}
}
impl<T: DeviceElement> PinnedBuffer<T> {
#[must_use]
pub const fn len(&self) -> usize {
self.len
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.len == 0
}
pub fn copy_from_slice(&mut self, values: &[T]) -> Result<()> {
validate_lengths(values.len(), self.len)?;
Ok(self.native.with_mut_slice(self.len, |target| target.copy_from_slice(values))?)
}
pub fn write_prefix(&mut self, values: &[T]) -> Result<()> {
if values.len() > self.len {
return Err(Error::LengthMismatch {
source_len: values.len(),
target_len: self.len,
});
}
Ok(self.native.with_mut_slice(self.len, |target| {
target[..values.len()].copy_from_slice(values);
})?)
}
pub fn with_bytes_mut<R>(&mut self, write: impl FnOnce(&mut [u8]) -> R) -> Result<R> {
Ok(self.native.with_mut_slice(self.native.bytes(), write)?)
}
pub fn with_bytes<R>(&self, read: impl FnOnce(&[u8]) -> R) -> Result<R> {
Ok(self.native.with_slice(self.native.bytes(), read)?)
}
pub fn to_vec(&self) -> Result<Vec<T>> {
Ok(self.native.with_slice(self.len, <[T]>::to_vec)?)
}
}
fn allocation_bytes<T>(len: usize) -> Result<usize> {
if len == 0 {
return Err(Error::EmptyAllocation);
}
let element_bytes = std::mem::size_of::<T>();
len.checked_mul(element_bytes)
.ok_or(Error::AllocationOverflow { elements: len, element_bytes })
}
const fn validate_lengths(source: usize, target: usize) -> Result<()> {
if source == target {
Ok(())
} else {
Err(Error::LengthMismatch { source_len: source, target_len: target })
}
}