use std::any::Any;
use std::sync::Arc;
use oxmera_core::{DType, Device, Error, Result};
#[derive(Debug, Clone)]
pub enum CpuStorage {
F32(Vec<f32>),
F64(Vec<f64>),
I64(Vec<i64>),
U8(Vec<u8>),
}
impl CpuStorage {
pub fn dtype(&self) -> DType {
match self {
CpuStorage::F32(_) => DType::F32,
CpuStorage::F64(_) => DType::F64,
CpuStorage::I64(_) => DType::I64,
CpuStorage::U8(_) => DType::U8,
}
}
pub fn len(&self) -> usize {
match self {
CpuStorage::F32(v) => v.len(),
CpuStorage::F64(v) => v.len(),
CpuStorage::I64(v) => v.len(),
CpuStorage::U8(v) => v.len(),
}
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn f32s(&self) -> Result<&[f32]> {
match self {
CpuStorage::F32(v) => Ok(v),
other => Err(Error::DTypeMismatch {
expected: DType::F32,
got: other.dtype(),
op: "CpuStorage::f32s",
}),
}
}
pub fn f64s(&self) -> Result<&[f64]> {
match self {
CpuStorage::F64(v) => Ok(v),
other => Err(Error::DTypeMismatch {
expected: DType::F64,
got: other.dtype(),
op: "CpuStorage::f64s",
}),
}
}
pub fn i64s(&self) -> Result<&[i64]> {
match self {
CpuStorage::I64(v) => Ok(v),
other => Err(Error::DTypeMismatch {
expected: DType::I64,
got: other.dtype(),
op: "CpuStorage::i64s",
}),
}
}
}
#[cfg(target_os = "macos")]
#[derive(Debug)]
pub struct MetalBuffer {
buffer: metal::Buffer,
pub device_index: usize,
}
#[cfg(target_os = "macos")]
impl MetalBuffer {
pub fn new(buffer: metal::Buffer, device_index: usize) -> Self {
Self {
buffer,
device_index,
}
}
pub fn buffer(&self) -> &metal::Buffer {
&self.buffer
}
}
#[cfg(target_os = "macos")]
#[allow(unsafe_code)]
unsafe impl Send for MetalBuffer {}
#[cfg(target_os = "macos")]
#[allow(unsafe_code)]
unsafe impl Sync for MetalBuffer {}
pub struct OpaqueBuffer {
inner: Arc<dyn Any + Send + Sync>,
len: usize,
}
impl OpaqueBuffer {
pub fn new(inner: Arc<dyn Any + Send + Sync>, len: usize) -> Self {
Self { inner, len }
}
pub fn inner(&self) -> &Arc<dyn Any + Send + Sync> {
&self.inner
}
pub fn len(&self) -> usize {
self.len
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
}
impl std::fmt::Debug for OpaqueBuffer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("OpaqueBuffer")
.field("len", &self.len)
.finish_non_exhaustive()
}
}
#[derive(Debug)]
pub enum StorageData {
Cpu(CpuStorage),
#[cfg(target_os = "macos")]
Metal(MetalBuffer),
Opaque(OpaqueBuffer),
}
#[derive(Debug)]
pub struct Storage {
data: StorageData,
dtype: DType,
device: Device,
}
impl Storage {
pub fn cpu_f32_zeros(numel: usize) -> Self {
Self::from_f32_vec(vec![0.0; numel])
}
pub fn from_f32_vec(data: Vec<f32>) -> Self {
Self {
data: StorageData::Cpu(CpuStorage::F32(data)),
dtype: DType::F32,
device: Device::Cpu,
}
}
pub fn from_f64_vec(data: Vec<f64>) -> Self {
Self {
data: StorageData::Cpu(CpuStorage::F64(data)),
dtype: DType::F64,
device: Device::Cpu,
}
}
pub fn from_i64_vec(data: Vec<i64>) -> Self {
Self {
data: StorageData::Cpu(CpuStorage::I64(data)),
dtype: DType::I64,
device: Device::Cpu,
}
}
#[cfg(target_os = "macos")]
pub fn from_metal(buffer: MetalBuffer, dtype: DType) -> Self {
let device = Device::Metal {
index: buffer.device_index,
};
Self {
data: StorageData::Metal(buffer),
dtype,
device,
}
}
pub fn dtype(&self) -> DType {
self.dtype
}
pub fn device(&self) -> Device {
self.device
}
pub fn data(&self) -> &StorageData {
&self.data
}
pub fn cpu(&self) -> Result<&CpuStorage> {
match &self.data {
StorageData::Cpu(c) => Ok(c),
_ => Err(Error::DeviceMismatch {
lhs: self.device,
rhs: Device::Cpu,
op: "Storage::cpu",
}),
}
}
pub fn from_opaque(buffer: OpaqueBuffer, dtype: DType, device: Device) -> Self {
Self {
data: StorageData::Opaque(buffer),
dtype,
device,
}
}
pub fn opaque(&self) -> Option<&OpaqueBuffer> {
match &self.data {
StorageData::Opaque(b) => Some(b),
_ => None,
}
}
}