use std::os::raw::c_void;
use crate::error::check_status;
use crate::raw;
use crate::types::maps::{MlxMapStringToArray, MlxMapStringToString};
use crate::types::vectors::MlxVectorString;
use crate::{MlxArray, MlxCError, MlxStream, MlxString};
fn path_argument(path: &str, operation: &'static str) -> Result<std::ffi::CString, MlxCError> {
std::ffi::CString::new(path).map_err(|_| MlxCError {
operation,
description: "path contains an interior null byte".to_owned(),
})
}
impl MlxArray {
pub fn load_from_file(path: &str, stream: &MlxStream) -> Result<Self, MlxCError> {
let operation: &'static str = "load an MLX array from a file";
let path_value = path_argument(path, operation)?;
let mut output = Self::empty();
let status = unsafe { raw::mlx_load(output.raw_mut(), path_value.as_ptr(), stream.raw()) };
check_status(status, operation)?;
output.require_populated(operation)?;
Ok(output)
}
pub fn save_to_file(&self, path: &str) -> Result<(), MlxCError> {
let operation: &'static str = "save an MLX array to a file";
let path_value = path_argument(path, operation)?;
let status = unsafe { raw::mlx_save(path_value.as_ptr(), self.raw()) };
check_status(status, operation)
}
}
pub fn load_safetensors(
path: &str,
stream: &MlxStream,
) -> Result<(MlxMapStringToArray, MlxMapStringToString), MlxCError> {
let operation: &'static str = "load MLX safetensors maps from a file";
let path_value = path_argument(path, operation)?;
let mut arrays = MlxMapStringToArray::new()?;
let mut metadata = MlxMapStringToString::new()?;
let status = unsafe {
raw::mlx_load_safetensors(
arrays.raw_mut(),
metadata.raw_mut(),
path_value.as_ptr(),
stream.raw(),
)
};
check_status(status, operation)?;
Ok((arrays, metadata))
}
pub fn save_safetensors(
path: &str,
arrays: &MlxMapStringToArray,
metadata: &MlxMapStringToString,
) -> Result<(), MlxCError> {
let operation: &'static str = "save MLX safetensors maps to a file";
let path_value = path_argument(path, operation)?;
let status =
unsafe { raw::mlx_save_safetensors(path_value.as_ptr(), arrays.raw(), metadata.raw()) };
check_status(status, operation)
}
#[derive(Debug)]
pub struct MlxIoReader {
raw_reader: raw::mlx_io_reader,
}
impl MlxIoReader {
pub unsafe fn from_parts(
descriptor: *mut c_void,
vtable: raw::mlx_io_vtable,
) -> Result<Self, MlxCError> {
let operation: &'static str = "create an MLX reader transport";
let raw_reader = unsafe { raw::mlx_io_reader_new(descriptor, vtable) };
if raw_reader.ctx.is_null() {
return Err(MlxCError {
operation,
description: "MLX returned an empty reader handle".to_owned(),
});
}
Ok(Self { raw_reader })
}
pub fn descriptor(&self) -> Result<*mut c_void, MlxCError> {
let operation: &'static str = "read the MLX reader descriptor";
let mut descriptor: *mut c_void = std::ptr::null_mut();
let status = unsafe { raw::mlx_io_reader_descriptor(&mut descriptor, self.raw_reader) };
check_status(status, operation)?;
Ok(descriptor)
}
pub fn to_string_lossy(&self) -> Result<String, MlxCError> {
let operation: &'static str = "describe an MLX reader transport";
let mut output = MlxString::empty();
let status = unsafe { raw::mlx_io_reader_tostring(output.raw_mut(), self.raw_reader) };
check_status(status, operation)?;
output.require_populated(operation)?;
Ok(output.to_string_lossy())
}
pub(crate) const fn raw(&self) -> raw::mlx_io_reader {
self.raw_reader
}
}
impl Drop for MlxIoReader {
fn drop(&mut self) {
unsafe {
raw::mlx_io_reader_free(self.raw_reader);
}
}
}
#[derive(Debug)]
pub struct MlxIoWriter {
raw_writer: raw::mlx_io_writer,
}
impl MlxIoWriter {
pub unsafe fn from_parts(
descriptor: *mut c_void,
vtable: raw::mlx_io_vtable,
) -> Result<Self, MlxCError> {
let operation: &'static str = "create an MLX writer transport";
let raw_writer = unsafe { raw::mlx_io_writer_new(descriptor, vtable) };
if raw_writer.ctx.is_null() {
return Err(MlxCError {
operation,
description: "MLX returned an empty writer handle".to_owned(),
});
}
Ok(Self { raw_writer })
}
pub fn descriptor(&self) -> Result<*mut c_void, MlxCError> {
let operation: &'static str = "read the MLX writer descriptor";
let mut descriptor: *mut c_void = std::ptr::null_mut();
let status = unsafe { raw::mlx_io_writer_descriptor(&mut descriptor, self.raw_writer) };
check_status(status, operation)?;
Ok(descriptor)
}
pub fn to_string_lossy(&self) -> Result<String, MlxCError> {
let operation: &'static str = "describe an MLX writer transport";
let mut output = MlxString::empty();
let status = unsafe { raw::mlx_io_writer_tostring(output.raw_mut(), self.raw_writer) };
check_status(status, operation)?;
output.require_populated(operation)?;
Ok(output.to_string_lossy())
}
pub(crate) const fn raw(&self) -> raw::mlx_io_writer {
self.raw_writer
}
}
impl Drop for MlxIoWriter {
fn drop(&mut self) {
unsafe {
raw::mlx_io_writer_free(self.raw_writer);
}
}
}
pub fn load_array_from_reader(
reader: &MlxIoReader,
stream: &MlxStream,
) -> Result<MlxArray, MlxCError> {
let operation: &'static str = "load an MLX array from a reader";
let mut output = MlxArray::empty();
let status = unsafe { raw::mlx_load_reader(output.raw_mut(), reader.raw(), stream.raw()) };
check_status(status, operation)?;
output.require_populated(operation)?;
Ok(output)
}
pub fn load_safetensors_from_reader(
reader: &MlxIoReader,
stream: &MlxStream,
) -> Result<(MlxMapStringToArray, MlxMapStringToString), MlxCError> {
let operation: &'static str = "load MLX safetensors maps from a reader";
let mut arrays = MlxMapStringToArray::new()?;
let mut metadata = MlxMapStringToString::new()?;
let status = unsafe {
raw::mlx_load_safetensors_reader(
arrays.raw_mut(),
metadata.raw_mut(),
reader.raw(),
stream.raw(),
)
};
check_status(status, operation)?;
Ok((arrays, metadata))
}
pub fn save_array_to_writer(writer: &MlxIoWriter, array: &MlxArray) -> Result<(), MlxCError> {
let operation: &'static str = "save an MLX array through a writer";
let status = unsafe { raw::mlx_save_writer(writer.raw(), array.raw()) };
check_status(status, operation)
}
pub fn save_safetensors_to_writer(
writer: &MlxIoWriter,
arrays: &MlxMapStringToArray,
metadata: &MlxMapStringToString,
) -> Result<(), MlxCError> {
let operation: &'static str = "save MLX safetensors maps through a writer";
let status =
unsafe { raw::mlx_save_safetensors_writer(writer.raw(), arrays.raw(), metadata.raw()) };
check_status(status, operation)
}
#[derive(Debug)]
pub struct MlxIoGguf(raw::mlx_io_gguf);
impl MlxIoGguf {
pub fn new() -> Result<Self, MlxCError> {
let operation: &'static str = "create an MLX GGUF container";
let raw_gguf = unsafe { raw::mlx_io_gguf_new() };
if raw_gguf.ctx.is_null() {
return Err(MlxCError {
operation,
description: "MLX returned an empty GGUF handle".to_owned(),
});
}
Ok(Self(raw_gguf))
}
pub(crate) fn raw_mut(&mut self) -> *mut raw::mlx_io_gguf {
&mut self.0
}
pub fn load_from_file(path: &str, stream: &MlxStream) -> Result<Self, MlxCError> {
let operation: &'static str = "load an MLX GGUF container from a file";
let path_value = path_argument(path, operation)?;
let mut output = Self::new()?;
let status =
unsafe { raw::mlx_load_gguf(output.raw_mut(), path_value.as_ptr(), stream.raw()) };
check_status(status, operation)?;
Ok(output)
}
pub fn save_to_file(&self, path: &str) -> Result<(), MlxCError> {
let operation: &'static str = "save an MLX GGUF container to a file";
let path_value = path_argument(path, operation)?;
let status = unsafe { raw::mlx_save_gguf(path_value.as_ptr(), self.0) };
check_status(status, operation)
}
pub fn array_keys(&self) -> Result<MlxVectorString, MlxCError> {
let operation: &'static str = "list MLX GGUF array keys";
let mut output = MlxVectorString::empty();
let status = unsafe { raw::mlx_io_gguf_get_keys(output.raw_mut(), self.0) };
check_status(status, operation)?;
Ok(output)
}
pub fn array_at(&self, key: &str) -> Result<MlxArray, MlxCError> {
let operation: &'static str = "read an MLX GGUF array";
let key_argument = path_argument(key, operation)?;
let mut output = MlxArray::empty();
let status =
unsafe { raw::mlx_io_gguf_get_array(output.raw_mut(), self.0, key_argument.as_ptr()) };
check_status(status, operation)?;
output.require_populated(operation)?;
Ok(output)
}
pub fn metadata_array_at(&self, key: &str) -> Result<MlxArray, MlxCError> {
let operation: &'static str = "read an MLX GGUF metadata array";
let key_argument = path_argument(key, operation)?;
let mut output = MlxArray::empty();
let status = unsafe {
raw::mlx_io_gguf_get_metadata_array(output.raw_mut(), self.0, key_argument.as_ptr())
};
check_status(status, operation)?;
output.require_populated(operation)?;
Ok(output)
}
pub fn metadata_string_at(&self, key: &str) -> Result<String, MlxCError> {
let operation: &'static str = "read an MLX GGUF metadata string";
let key_argument = path_argument(key, operation)?;
let mut output = MlxString::empty();
let status = unsafe {
raw::mlx_io_gguf_get_metadata_string(output.raw_mut(), self.0, key_argument.as_ptr())
};
check_status(status, operation)?;
output.require_populated(operation)?;
Ok(output.to_string_lossy())
}
pub fn metadata_string_vector_at(&self, key: &str) -> Result<MlxVectorString, MlxCError> {
let operation: &'static str = "read an MLX GGUF metadata string vector";
let key_argument = path_argument(key, operation)?;
let mut output = MlxVectorString::empty();
let status = unsafe {
raw::mlx_io_gguf_get_metadata_vector_string(
output.raw_mut(),
self.0,
key_argument.as_ptr(),
)
};
check_status(status, operation)?;
Ok(output)
}
fn has_metadata(
&self,
key: &str,
probe: unsafe extern "C" fn(
*mut bool,
raw::mlx_io_gguf,
*const std::os::raw::c_char,
) -> i32,
operation: &'static str,
) -> Result<bool, MlxCError> {
let key_argument = path_argument(key, operation)?;
let mut exists = false;
let status = unsafe { probe(&mut exists, self.0, key_argument.as_ptr()) };
check_status(status, operation)?;
Ok(exists)
}
pub fn has_metadata_array(&self, key: &str) -> Result<bool, MlxCError> {
self.has_metadata(
key,
raw::mlx_io_gguf_has_metadata_array,
"probe MLX GGUF array metadata",
)
}
pub fn has_metadata_string(&self, key: &str) -> Result<bool, MlxCError> {
self.has_metadata(
key,
raw::mlx_io_gguf_has_metadata_string,
"probe MLX GGUF string metadata",
)
}
pub fn has_metadata_string_vector(&self, key: &str) -> Result<bool, MlxCError> {
self.has_metadata(
key,
raw::mlx_io_gguf_has_metadata_vector_string,
"probe MLX GGUF string-vector metadata",
)
}
pub fn set_array(&mut self, key: &str, value: &MlxArray) -> Result<(), MlxCError> {
let operation: &'static str = "store an MLX GGUF array";
let key_argument = path_argument(key, operation)?;
let status =
unsafe { raw::mlx_io_gguf_set_array(self.0, key_argument.as_ptr(), value.raw()) };
check_status(status, operation)
}
pub fn set_metadata_array(&mut self, key: &str, value: &MlxArray) -> Result<(), MlxCError> {
let operation: &'static str = "store MLX GGUF metadata array";
let key_argument = path_argument(key, operation)?;
let status = unsafe {
raw::mlx_io_gguf_set_metadata_array(self.0, key_argument.as_ptr(), value.raw())
};
check_status(status, operation)
}
pub fn set_metadata_string(&mut self, key: &str, value: &str) -> Result<(), MlxCError> {
let operation: &'static str = "store MLX GGUF metadata string";
let key_argument = path_argument(key, operation)?;
let value_argument = path_argument(value, operation)?;
let status = unsafe {
raw::mlx_io_gguf_set_metadata_string(
self.0,
key_argument.as_ptr(),
value_argument.as_ptr(),
)
};
check_status(status, operation)
}
pub fn set_metadata_string_vector(
&mut self,
key: &str,
values: &MlxVectorString,
) -> Result<(), MlxCError> {
let operation: &'static str = "store MLX GGUF metadata string vector";
let key_argument = path_argument(key, operation)?;
let status = unsafe {
raw::mlx_io_gguf_set_metadata_vector_string(self.0, key_argument.as_ptr(), values.raw())
};
check_status(status, operation)
}
}
impl Drop for MlxIoGguf {
fn drop(&mut self) {
unsafe {
raw::mlx_io_gguf_free(self.0);
}
}
}