use std::ffi::{CStr, CString};
use crate::MlxCError;
use crate::error::check_status;
use crate::raw;
use crate::types::vectors::MlxVectorString;
#[derive(Debug)]
pub struct MlxDeviceHandle(raw::mlx_device);
impl MlxDeviceHandle {
pub(crate) fn empty() -> Self {
let raw_device = unsafe { raw::mlx_device_new() };
Self(raw_device)
}
pub fn of_type(
device_type: raw::mlx_device_type,
device_index: i32,
) -> Result<Self, MlxCError> {
let operation: &'static str = "create an MLX device handle";
let raw_device = unsafe { raw::mlx_device_new_type(device_type, device_index) };
Self::from_live_raw(raw_device, operation)
}
pub fn default_device() -> Result<Self, MlxCError> {
let operation: &'static str = "acquire the MLX default device";
let mut output = Self::empty();
let status = unsafe { raw::mlx_get_default_device(output.raw_mut()) };
check_status(status, operation)?;
output.require_populated(operation)?;
Ok(output)
}
pub fn set_as_default(&self) -> Result<(), MlxCError> {
let status = unsafe { raw::mlx_set_default_device(self.0) };
check_status(status, "set the MLX default device")
}
pub fn set(&mut self, source: &Self) -> Result<(), MlxCError> {
let status = unsafe { raw::mlx_device_set(&mut self.0, source.0) };
check_status(status, "copy an MLX device handle")
}
#[must_use]
pub fn equals(&self, other: &Self) -> bool {
unsafe { raw::mlx_device_equal(self.0, other.0) }
}
pub fn index(&self) -> Result<i32, MlxCError> {
let operation: &'static str = "read the MLX device index";
let mut device_index: i32 = 0;
let status = unsafe { raw::mlx_device_get_index(&mut device_index, self.0) };
check_status(status, operation)?;
Ok(device_index)
}
pub fn device_type(&self) -> Result<raw::mlx_device_type, MlxCError> {
let operation: &'static str = "read the MLX device type";
let mut device_type = raw::mlx_device_type__MLX_CPU;
let status = unsafe { raw::mlx_device_get_type(&mut device_type, self.0) };
check_status(status, operation)?;
Ok(device_type)
}
pub fn is_available(&self) -> Result<bool, MlxCError> {
let operation: &'static str = "query MLX device availability";
let mut available = false;
let status = unsafe { raw::mlx_device_is_available(&mut available, self.0) };
check_status(status, operation)?;
Ok(available)
}
pub fn count_of_type(device_type: raw::mlx_device_type) -> Result<i32, MlxCError> {
let operation: &'static str = "count MLX devices";
let mut device_count: i32 = 0;
let status = unsafe { raw::mlx_device_count(&mut device_count, device_type) };
check_status(status, operation)?;
Ok(device_count)
}
pub fn to_string_lossy(&self) -> Result<String, MlxCError> {
let operation: &'static str = "describe an MLX device";
let mut output = crate::MlxString::empty();
let status = unsafe { raw::mlx_device_tostring(output.raw_mut(), self.0) };
check_status(status, operation)?;
output.require_populated(operation)?;
Ok(output.to_string_lossy())
}
pub fn information(&self) -> Result<MlxDeviceInfo, MlxCError> {
let operation: &'static str = "read MLX device information";
let mut output = MlxDeviceInfo::empty();
let status = unsafe { raw::mlx_device_info_get(output.raw_mut(), self.0) };
check_status(status, operation)?;
output.require_populated(operation)?;
Ok(output)
}
pub(crate) const fn raw(&self) -> raw::mlx_device {
self.0
}
pub(crate) fn raw_mut(&mut self) -> *mut raw::mlx_device {
&mut self.0
}
pub(crate) fn from_live_raw(
raw_device: raw::mlx_device,
operation: &'static str,
) -> Result<Self, MlxCError> {
if raw_device.ctx.is_null() {
return Err(MlxCError {
operation,
description: "MLX returned an empty device handle".to_owned(),
});
}
Ok(Self(raw_device))
}
fn require_populated(&self, operation: &'static str) -> Result<(), MlxCError> {
if self.0.ctx.is_null() {
return Err(MlxCError {
operation,
description: "MLX returned an empty device handle".to_owned(),
});
}
Ok(())
}
}
impl Drop for MlxDeviceHandle {
fn drop(&mut self) {
unsafe {
raw::mlx_device_free(self.0);
}
}
}
#[derive(Debug)]
pub struct MlxDeviceInfo(raw::mlx_device_info);
impl MlxDeviceInfo {
pub(crate) fn empty() -> Self {
let raw_info = unsafe { raw::mlx_device_info_new() };
Self(raw_info)
}
pub fn has_key(&self, key: &str) -> Result<bool, MlxCError> {
let operation: &'static str = "query MLX device information keys";
let key_argument = CString::new(key).map_err(|_| MlxCError {
operation,
description: "key contains an interior null byte".to_owned(),
})?;
let mut exists = false;
let status =
unsafe { raw::mlx_device_info_has_key(&mut exists, self.0, key_argument.as_ptr()) };
check_status(status, operation)?;
Ok(exists)
}
pub fn is_string(&self, key: &str) -> Result<bool, MlxCError> {
let operation: &'static str = "query MLX device information value types";
let key_argument = CString::new(key).map_err(|_| MlxCError {
operation,
description: "key contains an interior null byte".to_owned(),
})?;
let mut is_string = false;
let status = unsafe {
raw::mlx_device_info_is_string(&mut is_string, self.0, key_argument.as_ptr())
};
check_status(status, operation)?;
Ok(is_string)
}
pub fn string_value(&self, key: &str) -> Result<String, MlxCError> {
let operation: &'static str = "read an MLX device information string";
let key_argument = CString::new(key).map_err(|_| MlxCError {
operation,
description: "key contains an interior null byte".to_owned(),
})?;
let mut value_pointer: *const std::os::raw::c_char = std::ptr::null();
let status = unsafe {
raw::mlx_device_info_get_string(&mut value_pointer, self.0, key_argument.as_ptr())
};
check_status(status, operation)?;
if value_pointer.is_null() {
return Err(MlxCError {
operation,
description: "MLX returned a null device information string".to_owned(),
});
}
let borrowed = unsafe { CStr::from_ptr(value_pointer) };
Ok(borrowed.to_string_lossy().into_owned())
}
pub fn size_value(&self, key: &str) -> Result<usize, MlxCError> {
let operation: &'static str = "read an MLX device information size";
let key_argument = CString::new(key).map_err(|_| MlxCError {
operation,
description: "key contains an interior null byte".to_owned(),
})?;
let mut value: usize = 0;
let status =
unsafe { raw::mlx_device_info_get_size(&mut value, self.0, key_argument.as_ptr()) };
check_status(status, operation)?;
Ok(value)
}
pub fn keys(&self) -> Result<MlxVectorString, MlxCError> {
let operation: &'static str = "list MLX device information keys";
let mut output = MlxVectorString::empty();
let status = unsafe { raw::mlx_device_info_get_keys(output.raw_mut(), self.0) };
check_status(status, operation)?;
Ok(output)
}
pub(crate) fn raw_mut(&mut self) -> *mut raw::mlx_device_info {
&mut self.0
}
fn require_populated(&self, operation: &'static str) -> Result<(), MlxCError> {
if self.0.ctx.is_null() {
return Err(MlxCError {
operation,
description: "MLX returned an empty device info handle".to_owned(),
});
}
Ok(())
}
}
impl Drop for MlxDeviceInfo {
fn drop(&mut self) {
unsafe {
raw::mlx_device_info_free(self.0);
}
}
}