use crate::ffi::*;
use core::ffi::c_void;
#[derive(Clone, Debug)]
pub struct TensorInfo {
pub data: *mut c_void,
pub device: DLDevice,
pub dtype: DLDataType,
pub shape: Vec<i64>,
pub strides: Option<Vec<i64>>,
pub byte_offset: u64,
}
impl TensorInfo {
pub fn contiguous(
data: *mut c_void,
device: DLDevice,
dtype: DLDataType,
shape: Vec<i64>,
) -> Self {
Self {
data,
device,
dtype,
shape,
strides: None,
byte_offset: 0,
}
}
pub fn strided(
data: *mut c_void,
device: DLDevice,
dtype: DLDataType,
shape: Vec<i64>,
strides: Vec<i64>,
) -> Self {
Self {
data,
device,
dtype,
shape,
strides: Some(strides),
byte_offset: 0,
}
}
pub fn with_byte_offset(mut self, off: u64) -> Self {
self.byte_offset = off;
self
}
}
pub fn cpu_device() -> DLDevice {
DLDevice {
device_type: DLDeviceType::kDLCPU,
device_id: 0,
}
}
pub fn cuda_device(id: i32) -> DLDevice {
DLDevice {
device_type: DLDeviceType::kDLCUDA,
device_id: id,
}
}
pub fn dtype_u8() -> DLDataType {
DLDataType {
code: DLDataTypeCode::kDLUInt as u8,
bits: 8,
lanes: 1,
}
}
pub fn dtype_u16() -> DLDataType {
DLDataType {
code: DLDataTypeCode::kDLUInt as u8,
bits: 16,
lanes: 1,
}
}
pub fn dtype_i32() -> DLDataType {
DLDataType {
code: DLDataTypeCode::kDLInt as u8,
bits: 32,
lanes: 1,
}
}
pub fn dtype_i64() -> DLDataType {
DLDataType {
code: DLDataTypeCode::kDLInt as u8,
bits: 64,
lanes: 1,
}
}
pub fn dtype_f32() -> DLDataType {
DLDataType {
code: DLDataTypeCode::kDLFloat as u8,
bits: 32,
lanes: 1,
}
}
pub fn dtype_f64() -> DLDataType {
DLDataType {
code: DLDataTypeCode::kDLFloat as u8,
bits: 64,
lanes: 1,
}
}
pub fn dtype_bool() -> DLDataType {
DLDataType {
code: DLDataTypeCode::kDLBool as u8,
bits: 8,
lanes: 1,
}
}
struct ManagedContext<T> {
_keepalive: T,
shape: Vec<i64>,
strides: Option<Vec<i64>>,
}
pub fn pack<T: 'static>(keepalive: T, info: TensorInfo) -> *mut DLManagedTensor {
let mut ctx = Box::new(ManagedContext {
_keepalive: keepalive,
shape: info.shape,
strides: info.strides,
});
let shape_ptr = ctx.shape.as_mut_ptr();
let ndim = ctx.shape.len() as i32;
let strides_ptr = match ctx.strides.as_mut() {
Some(s) => s.as_mut_ptr(),
None => core::ptr::null_mut(),
};
let dl_tensor = DLTensor {
data: info.data,
device: info.device,
ndim,
dtype: info.dtype,
shape: shape_ptr,
strides: strides_ptr,
byte_offset: info.byte_offset,
};
let ctx_ptr = Box::into_raw(ctx) as *mut c_void;
let mt = Box::new(DLManagedTensor {
dl_tensor,
manager_ctx: ctx_ptr,
deleter: Some(deleter::<T>),
});
Box::into_raw(mt)
}
unsafe extern "C" fn deleter<T: 'static>(mt: *mut DLManagedTensor) {
if mt.is_null() {
return;
}
let mt = Box::from_raw(mt);
if !mt.manager_ctx.is_null() {
drop(Box::from_raw(mt.manager_ctx as *mut ManagedContext<T>));
}
}
pub fn pack_versioned<T: 'static>(
keepalive: T,
info: TensorInfo,
flags: u64,
) -> *mut DLManagedTensorVersioned {
let mut ctx = Box::new(ManagedContext {
_keepalive: keepalive,
shape: info.shape,
strides: info.strides,
});
let shape_ptr = ctx.shape.as_mut_ptr();
let ndim = ctx.shape.len() as i32;
let strides_ptr = match ctx.strides.as_mut() {
Some(s) => s.as_mut_ptr(),
None => core::ptr::null_mut(),
};
let dl_tensor = DLTensor {
data: info.data,
device: info.device,
ndim,
dtype: info.dtype,
shape: shape_ptr,
strides: strides_ptr,
byte_offset: info.byte_offset,
};
let ctx_ptr = Box::into_raw(ctx) as *mut c_void;
let mt = Box::new(DLManagedTensorVersioned {
version: DLPackVersion { major: 1, minor: 0 },
manager_ctx: ctx_ptr,
deleter: Some(deleter_versioned::<T>),
flags,
dl_tensor,
});
Box::into_raw(mt)
}
unsafe extern "C" fn deleter_versioned<T: 'static>(mt: *mut DLManagedTensorVersioned) {
if mt.is_null() {
return;
}
let mt = Box::from_raw(mt);
if !mt.manager_ctx.is_null() {
drop(Box::from_raw(mt.manager_ctx as *mut ManagedContext<T>));
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::cell::Cell;
use std::rc::Rc;
#[test]
fn pack_then_delete_drops_keepalive_once() {
struct Guard(Rc<Cell<u32>>);
impl Drop for Guard {
fn drop(&mut self) {
self.0.set(self.0.get() + 1);
}
}
let flag = Rc::new(Cell::new(0u32));
let mut buf = vec![1.0f32, 2.0, 3.0, 4.0];
let info = TensorInfo::contiguous(
buf.as_mut_ptr() as *mut _,
cpu_device(),
dtype_f32(),
vec![2, 2],
);
let mt = pack(Guard(flag.clone()), info);
unsafe {
assert_eq!((*mt).dl_tensor.ndim, 2);
assert_eq!(*(*mt).dl_tensor.shape.add(0), 2);
let del = (*mt).deleter.unwrap();
del(mt);
}
assert_eq!(flag.get(), 1, "keepalive dropped exactly once");
drop(buf);
}
}