Skip to main content

mlx_c_rust/types/
maps.rs

1//! The MLX string-keyed map domain: array-valued and string-valued maps
2//! with their iterators.
3
4use std::ffi::{CStr, CString};
5
6use crate::error::check_status;
7use crate::raw;
8use crate::{MlxArray, MlxCError};
9
10/// Owned MLX map from strings to arrays, released exactly once.
11#[derive(Debug)]
12pub struct MlxMapStringToArray(raw::mlx_map_string_to_array);
13
14impl MlxMapStringToArray {
15    /// Creates an empty map.
16    pub fn new() -> Result<Self, MlxCError> {
17        let operation: &'static str = "create an MLX string-to-array map";
18        // SAFETY: The returned handle enters RAII ownership immediately.
19        let raw_map = unsafe { raw::mlx_map_string_to_array_new() };
20        if raw_map.ctx.is_null() {
21            return Err(MlxCError {
22                operation,
23                description: "MLX returned an empty map handle".to_owned(),
24            });
25        }
26        Ok(Self(raw_map))
27    }
28
29    /// Copies the source map handle into this handle.
30    pub fn set(&mut self, source: &Self) -> Result<(), MlxCError> {
31        // SAFETY: Both handles are live and this call only replaces the
32        // owned map value.
33        let status = unsafe { raw::mlx_map_string_to_array_set(&mut self.0, source.0) };
34        check_status(status, "copy an MLX string-to-array map")
35    }
36
37    /// Inserts one copied array under the key.
38    pub fn insert(&mut self, key: &str, value: &MlxArray) -> Result<(), MlxCError> {
39        let operation: &'static str = "insert into an MLX string-to-array map";
40        let key_argument = CString::new(key).map_err(|_| MlxCError {
41            operation,
42            description: "key contains an interior null byte".to_owned(),
43        })?;
44        // SAFETY: The map handle is live, the key outlives the call, and the
45        // array handle is copied.
46        let status = unsafe {
47            raw::mlx_map_string_to_array_insert(self.0, key_argument.as_ptr(), value.raw())
48        };
49        check_status(status, operation)
50    }
51
52    /// Copies the array stored under the key, if any.
53    pub fn value_at(&self, key: &str) -> Result<Option<MlxArray>, MlxCError> {
54        let operation: &'static str = "read an MLX string-to-array map entry";
55        let key_argument = CString::new(key).map_err(|_| MlxCError {
56            operation,
57            description: "key contains an interior null byte".to_owned(),
58        })?;
59        let mut output = MlxArray::empty();
60        // SAFETY: The map handle is live, the key outlives the call, and the
61        // output pointer is valid writable storage for one array handle.
62        let status = unsafe {
63            raw::mlx_map_string_to_array_get(output.raw_mut(), self.0, key_argument.as_ptr())
64        };
65        check_status(status, operation)?;
66        if output.is_empty() {
67            return Ok(None);
68        }
69        Ok(Some(output))
70    }
71
72    /// An owned iterator over the map's entries.
73    pub fn iter(&self) -> Result<MlxMapStringToArrayIterator, MlxCError> {
74        let operation: &'static str = "iterate an MLX string-to-array map";
75        // SAFETY: The map handle is live and the returned iterator enters
76        // RAII ownership.
77        let raw_iterator = unsafe { raw::mlx_map_string_to_array_iterator_new(self.0) };
78        if raw_iterator.ctx.is_null() {
79            return Err(MlxCError {
80                operation,
81                description: "MLX returned an empty map iterator handle".to_owned(),
82            });
83        }
84        Ok(MlxMapStringToArrayIterator(raw_iterator))
85    }
86
87    pub(crate) const fn raw(&self) -> raw::mlx_map_string_to_array {
88        self.0
89    }
90
91    pub(crate) fn raw_mut(&mut self) -> *mut raw::mlx_map_string_to_array {
92        &mut self.0
93    }
94
95    /// A borrowed view over a live map handle owned elsewhere.
96    pub(crate) fn from_borrowed_raw(raw_map: raw::mlx_map_string_to_array) -> Self {
97        Self(raw_map)
98    }
99}
100
101impl Drop for MlxMapStringToArray {
102    fn drop(&mut self) {
103        // SAFETY: This owner releases its live map exactly once and never
104        // accesses the handle afterward.
105        unsafe {
106            raw::mlx_map_string_to_array_free(self.0);
107        }
108    }
109}
110
111/// Owned iterator over an MLX string-to-array map.
112#[derive(Debug)]
113pub struct MlxMapStringToArrayIterator(raw::mlx_map_string_to_array_iterator);
114
115impl MlxMapStringToArrayIterator {
116    /// Advances the iterator, copying the next entry if one remains.
117    pub fn next_entry(&mut self) -> Result<Option<(String, MlxArray)>, MlxCError> {
118        let operation: &'static str = "advance an MLX string-to-array map iterator";
119        let mut key_pointer: *const std::os::raw::c_char = std::ptr::null();
120        let mut value = MlxArray::empty();
121        // SAFETY: The iterator handle is live, and both output pointers are
122        // valid writable storage for one borrowed key and one array handle.
123        let status = unsafe {
124            raw::mlx_map_string_to_array_iterator_next(&mut key_pointer, value.raw_mut(), self.0)
125        };
126        check_status(status, operation)?;
127        if key_pointer.is_null() {
128            return Ok(None);
129        }
130        // SAFETY: MLX loans the null-terminated key from the live map; the
131        // text is copied before this call returns.
132        let key = unsafe { CStr::from_ptr(key_pointer) }
133            .to_string_lossy()
134            .into_owned();
135        Ok(Some((key, value)))
136    }
137}
138
139impl Drop for MlxMapStringToArrayIterator {
140    fn drop(&mut self) {
141        // SAFETY: This owner releases its live iterator exactly once and
142        // never accesses the handle afterward.
143        unsafe {
144            raw::mlx_map_string_to_array_iterator_free(self.0);
145        }
146    }
147}
148
149/// Owned MLX map from strings to strings, released exactly once.
150#[derive(Debug)]
151pub struct MlxMapStringToString(raw::mlx_map_string_to_string);
152
153impl MlxMapStringToString {
154    /// Creates an empty map.
155    pub fn new() -> Result<Self, MlxCError> {
156        let operation: &'static str = "create an MLX string-to-string map";
157        // SAFETY: The returned handle enters RAII ownership immediately.
158        let raw_map = unsafe { raw::mlx_map_string_to_string_new() };
159        if raw_map.ctx.is_null() {
160            return Err(MlxCError {
161                operation,
162                description: "MLX returned an empty map handle".to_owned(),
163            });
164        }
165        Ok(Self(raw_map))
166    }
167
168    /// Copies the source map handle into this handle.
169    pub fn set(&mut self, source: &Self) -> Result<(), MlxCError> {
170        // SAFETY: Both handles are live and this call only replaces the
171        // owned map value.
172        let status = unsafe { raw::mlx_map_string_to_string_set(&mut self.0, source.0) };
173        check_status(status, "copy an MLX string-to-string map")
174    }
175
176    /// Inserts one copied string value under the key.
177    pub fn insert(&mut self, key: &str, value: &str) -> Result<(), MlxCError> {
178        let operation: &'static str = "insert into an MLX string-to-string map";
179        let key_argument = CString::new(key).map_err(|_| MlxCError {
180            operation,
181            description: "key contains an interior null byte".to_owned(),
182        })?;
183        let value_argument = CString::new(value).map_err(|_| MlxCError {
184            operation,
185            description: "value contains an interior null byte".to_owned(),
186        })?;
187        // SAFETY: The map handle is live and both strings outlive the call.
188        let status = unsafe {
189            raw::mlx_map_string_to_string_insert(
190                self.0,
191                key_argument.as_ptr(),
192                value_argument.as_ptr(),
193            )
194        };
195        check_status(status, operation)
196    }
197
198    /// Copies the string stored under the key, if any.
199    pub fn value_at(&self, key: &str) -> Result<Option<String>, MlxCError> {
200        let operation: &'static str = "read an MLX string-to-string map entry";
201        let key_argument = CString::new(key).map_err(|_| MlxCError {
202            operation,
203            description: "key contains an interior null byte".to_owned(),
204        })?;
205        let mut value_pointer: *const std::os::raw::c_char = std::ptr::null();
206        // SAFETY: The map handle is live, the key outlives the call, and the
207        // output pointer is valid writable storage.
208        let status = unsafe {
209            raw::mlx_map_string_to_string_get(&mut value_pointer, self.0, key_argument.as_ptr())
210        };
211        check_status(status, operation)?;
212        if value_pointer.is_null() {
213            return Ok(None);
214        }
215        // SAFETY: MLX loans the null-terminated value from the live map; the
216        // text is copied before this call returns.
217        let borrowed = unsafe { CStr::from_ptr(value_pointer) };
218        Ok(Some(borrowed.to_string_lossy().into_owned()))
219    }
220
221    /// An owned iterator over the map's entries.
222    pub fn iter(&self) -> Result<MlxMapStringToStringIterator, MlxCError> {
223        let operation: &'static str = "iterate an MLX string-to-string map";
224        // SAFETY: The map handle is live and the returned iterator enters
225        // RAII ownership.
226        let raw_iterator = unsafe { raw::mlx_map_string_to_string_iterator_new(self.0) };
227        if raw_iterator.ctx.is_null() {
228            return Err(MlxCError {
229                operation,
230                description: "MLX returned an empty map iterator handle".to_owned(),
231            });
232        }
233        Ok(MlxMapStringToStringIterator(raw_iterator))
234    }
235
236    pub(crate) const fn raw(&self) -> raw::mlx_map_string_to_string {
237        self.0
238    }
239
240    pub(crate) fn raw_mut(&mut self) -> *mut raw::mlx_map_string_to_string {
241        &mut self.0
242    }
243}
244
245impl Drop for MlxMapStringToString {
246    fn drop(&mut self) {
247        // SAFETY: This owner releases its live map exactly once and never
248        // accesses the handle afterward.
249        unsafe {
250            raw::mlx_map_string_to_string_free(self.0);
251        }
252    }
253}
254
255/// Owned iterator over an MLX string-to-string map.
256#[derive(Debug)]
257pub struct MlxMapStringToStringIterator(raw::mlx_map_string_to_string_iterator);
258
259impl MlxMapStringToStringIterator {
260    /// Advances the iterator, copying the next entry if one remains.
261    pub fn next_entry(&mut self) -> Result<Option<(String, String)>, MlxCError> {
262        let operation: &'static str = "advance an MLX string-to-string map iterator";
263        let mut key_pointer: *const std::os::raw::c_char = std::ptr::null();
264        let mut value_pointer: *const std::os::raw::c_char = std::ptr::null();
265        // SAFETY: The iterator handle is live and both output pointers are
266        // valid writable storage for borrowed key and value pointers.
267        let status = unsafe {
268            raw::mlx_map_string_to_string_iterator_next(
269                &mut key_pointer,
270                &mut value_pointer,
271                self.0,
272            )
273        };
274        check_status(status, operation)?;
275        if key_pointer.is_null() {
276            return Ok(None);
277        }
278        // SAFETY: MLX loans the null-terminated entries from the live map;
279        // both texts are copied before this call returns.
280        let key = unsafe { CStr::from_ptr(key_pointer) }
281            .to_string_lossy()
282            .into_owned();
283        let value = if value_pointer.is_null() {
284            String::new()
285        } else {
286            // SAFETY: The value pointer loan is owned by the same live map.
287            unsafe { CStr::from_ptr(value_pointer) }
288                .to_string_lossy()
289                .into_owned()
290        };
291        Ok(Some((key, value)))
292    }
293}
294
295impl Drop for MlxMapStringToStringIterator {
296    fn drop(&mut self) {
297        // SAFETY: This owner releases its live iterator exactly once and
298        // never accesses the handle afterward.
299        unsafe {
300            raw::mlx_map_string_to_string_iterator_free(self.0);
301        }
302    }
303}