Skip to main content

tract_proxy/
lib.rs

1use std::ffi::{CStr, CString};
2use std::path::Path;
3use std::ptr::{null, null_mut};
4
5use tract_api::*;
6use tract_proxy_sys as sys;
7
8use anyhow::{Context, Result};
9
10mod ndarray_interop;
11pub use ndarray_interop::__ndarray_interop;
12
13macro_rules! check {
14    ($expr:expr) => {
15        unsafe {
16            if $expr == sys::TRACT_RESULT_TRACT_RESULT_KO {
17                let buf = CStr::from_ptr(sys::tract_get_last_error());
18                Err(anyhow::anyhow!(buf.to_string_lossy().to_string()))
19            } else {
20                Ok(())
21            }
22        }
23    };
24}
25
26macro_rules! wrapper {
27    ($new_type:ident, $c_type:ident, $dest:ident $(, $typ:ty )*) => {
28        #[derive(Debug)]
29        pub struct $new_type(*mut sys::$c_type $(, $typ)*);
30
31        impl Drop for $new_type {
32            fn drop(&mut self) {
33                unsafe {
34                    sys::$dest(&mut self.0);
35                }
36            }
37        }
38    };
39}
40
41macro_rules! wrapper_clone {
42    ($new_type:ident, $clone_fn:ident) => {
43        impl Clone for $new_type {
44            fn clone(&self) -> Self {
45                let mut clone = null_mut();
46                unsafe {
47                    sys::$clone_fn(self.0, &mut clone);
48                }
49                $new_type(clone)
50            }
51        }
52    };
53}
54
55pub fn nnef() -> Result<Nnef> {
56    let mut nnef = null_mut();
57    check!(sys::tract_nnef_create(&mut nnef))?;
58    Ok(Nnef(nnef))
59}
60
61pub fn onnx() -> Result<Onnx> {
62    let mut onnx = null_mut();
63    check!(sys::tract_onnx_create(&mut onnx))?;
64    Ok(Onnx(onnx))
65}
66
67pub fn version() -> &'static str {
68    unsafe { CStr::from_ptr(sys::tract_version()).to_str().unwrap() }
69}
70
71wrapper!(Nnef, TractNnef, tract_nnef_destroy);
72impl NnefInterface for Nnef {
73    type Model = Model;
74    fn load(&self, path: impl AsRef<Path>) -> Result<Model> {
75        let path = path.as_ref();
76        let path = CString::new(
77            path.to_str().with_context(|| format!("Failed to re-encode {path:?} to uff-8"))?,
78        )?;
79        let mut model = null_mut();
80        check!(sys::tract_nnef_load(self.0, path.as_ptr(), &mut model))?;
81        Ok(Model(model))
82    }
83
84    fn load_buffer(&self, data: &[u8]) -> Result<Model> {
85        let mut model = null_mut();
86        check!(sys::tract_nnef_load_buffer(self.0, data.as_ptr() as _, data.len(), &mut model))?;
87        Ok(Model(model))
88    }
89
90    fn disable_tract_core(&mut self) -> Result<()> {
91        check!(sys::tract_nnef_disable_tract_core(self.0))
92    }
93
94    fn enable_tract_extra(&mut self) -> Result<()> {
95        check!(sys::tract_nnef_enable_tract_extra(self.0))
96    }
97
98    fn enable_tract_transformers(&mut self) -> Result<()> {
99        check!(sys::tract_nnef_enable_tract_transformers(self.0))
100    }
101
102    fn enable_onnx(&mut self) -> Result<()> {
103        check!(sys::tract_nnef_enable_onnx(self.0))
104    }
105
106    fn enable_pulse(&mut self) -> Result<()> {
107        check!(sys::tract_nnef_enable_pulse(self.0))
108    }
109
110    fn enable_extended_identifier_syntax(&mut self) -> Result<()> {
111        check!(sys::tract_nnef_enable_extended_identifier_syntax(self.0))
112    }
113
114    fn write_model_to_dir(&self, path: impl AsRef<Path>, model: &Model) -> Result<()> {
115        let path = path.as_ref();
116        let path = CString::new(
117            path.to_str().with_context(|| format!("Failed to re-encode {path:?} to uff-8"))?,
118        )?;
119        check!(sys::tract_nnef_write_model_to_dir(self.0, path.as_ptr(), model.0))?;
120        Ok(())
121    }
122
123    fn write_model_to_tar(&self, path: impl AsRef<Path>, model: &Model) -> Result<()> {
124        let path = path.as_ref();
125        let path = CString::new(
126            path.to_str().with_context(|| format!("Failed to re-encode {path:?} to uff-8"))?,
127        )?;
128        check!(sys::tract_nnef_write_model_to_tar(self.0, path.as_ptr(), model.0))?;
129        Ok(())
130    }
131
132    fn write_model_to_tar_gz(&self, path: impl AsRef<Path>, model: &Model) -> Result<()> {
133        let path = path.as_ref();
134        let path = CString::new(
135            path.to_str().with_context(|| format!("Failed to re-encode {path:?} to uff-8"))?,
136        )?;
137        check!(sys::tract_nnef_write_model_to_tar_gz(self.0, path.as_ptr(), model.0))?;
138        Ok(())
139    }
140}
141
142// ONNX
143wrapper!(Onnx, TractOnnx, tract_onnx_destroy);
144
145impl OnnxInterface for Onnx {
146    type InferenceModel = InferenceModel;
147
148    fn load_with_options(
149        &self,
150        path: impl AsRef<Path>,
151        options: impl Into<OnnxOptionsSpec>,
152    ) -> Result<InferenceModel> {
153        let path = path.as_ref();
154        let path = CString::new(
155            path.to_str().with_context(|| format!("Failed to re-encode {path:?} to uff-8"))?,
156        )?;
157        let options = CString::new(options.into().to_json())?;
158        let mut model = null_mut();
159        check!(sys::tract_onnx_load_with_options(
160            self.0,
161            path.as_ptr(),
162            options.as_ptr(),
163            &mut model
164        ))?;
165        Ok(InferenceModel(model))
166    }
167
168    fn load_buffer_with_options(
169        &self,
170        data: &[u8],
171        options: impl Into<OnnxOptionsSpec>,
172    ) -> Result<InferenceModel> {
173        let options = CString::new(options.into().to_json())?;
174        let mut model = null_mut();
175        check!(sys::tract_onnx_load_buffer_with_options(
176            self.0,
177            data.as_ptr() as _,
178            data.len(),
179            options.as_ptr(),
180            &mut model
181        ))?;
182        Ok(InferenceModel(model))
183    }
184}
185
186// INFERENCE MODEL
187wrapper!(InferenceModel, TractInferenceModel, tract_inference_model_destroy);
188impl InferenceModelInterface for InferenceModel {
189    type Model = Model;
190    type InferenceFact = InferenceFact;
191    fn input_count(&self) -> Result<usize> {
192        let mut count = 0;
193        check!(sys::tract_inference_model_input_count(self.0, &mut count))?;
194        Ok(count)
195    }
196
197    fn output_count(&self) -> Result<usize> {
198        let mut count = 0;
199        check!(sys::tract_inference_model_output_count(self.0, &mut count))?;
200        Ok(count)
201    }
202
203    fn input_name(&self, id: usize) -> Result<String> {
204        let mut ptr = null_mut();
205        check!(sys::tract_inference_model_input_name(self.0, id, &mut ptr))?;
206        unsafe {
207            let ret = CStr::from_ptr(ptr).to_str()?.to_owned();
208            sys::tract_free_cstring(ptr);
209            Ok(ret)
210        }
211    }
212
213    fn output_name(&self, id: usize) -> Result<String> {
214        let mut ptr = null_mut();
215        check!(sys::tract_inference_model_output_name(self.0, id, &mut ptr))?;
216        unsafe {
217            let ret = CStr::from_ptr(ptr).to_str()?.to_owned();
218            sys::tract_free_cstring(ptr);
219            Ok(ret)
220        }
221    }
222
223    fn input_fact(&self, id: usize) -> Result<InferenceFact> {
224        let mut ptr = null_mut();
225        check!(sys::tract_inference_model_input_fact(self.0, id, &mut ptr))?;
226        Ok(InferenceFact(ptr))
227    }
228
229    fn set_input_fact(
230        &mut self,
231        id: usize,
232        fact: impl AsFact<Self, Self::InferenceFact>,
233    ) -> Result<()> {
234        let fact = fact.as_fact(self)?;
235        check!(sys::tract_inference_model_set_input_fact(self.0, id, fact.0))?;
236        Ok(())
237    }
238
239    fn output_fact(&self, id: usize) -> Result<InferenceFact> {
240        let mut ptr = null_mut();
241        check!(sys::tract_inference_model_output_fact(self.0, id, &mut ptr))?;
242        Ok(InferenceFact(ptr))
243    }
244
245    fn set_output_fact(
246        &mut self,
247        id: usize,
248        fact: impl AsFact<InferenceModel, InferenceFact>,
249    ) -> Result<()> {
250        let fact = fact.as_fact(self)?;
251        check!(sys::tract_inference_model_set_output_fact(self.0, id, fact.0))?;
252        Ok(())
253    }
254
255    fn analyse(&mut self) -> Result<()> {
256        check!(sys::tract_inference_model_analyse(self.0))?;
257        Ok(())
258    }
259
260    fn into_model(mut self) -> Result<Self::Model> {
261        let mut ptr = null_mut();
262        check!(sys::tract_inference_model_into_model(&mut self.0, &mut ptr))?;
263        Ok(Model(ptr))
264    }
265}
266
267// MODEL
268wrapper!(Model, TractModel, tract_model_destroy);
269
270impl ModelInterface for Model {
271    type Fact = Fact;
272    type Tensor = Tensor;
273    type Runnable = Runnable;
274    fn input_count(&self) -> Result<usize> {
275        let mut count = 0;
276        check!(sys::tract_model_input_count(self.0, &mut count))?;
277        Ok(count)
278    }
279
280    fn output_count(&self) -> Result<usize> {
281        let mut count = 0;
282        check!(sys::tract_model_output_count(self.0, &mut count))?;
283        Ok(count)
284    }
285
286    fn input_name(&self, id: usize) -> Result<String> {
287        let mut ptr = null_mut();
288        check!(sys::tract_model_input_name(self.0, id, &mut ptr))?;
289        unsafe {
290            let ret = CStr::from_ptr(ptr).to_str()?.to_owned();
291            sys::tract_free_cstring(ptr);
292            Ok(ret)
293        }
294    }
295
296    fn output_name(&self, id: usize) -> Result<String> {
297        let mut ptr = null_mut();
298        check!(sys::tract_model_output_name(self.0, id, &mut ptr))?;
299        unsafe {
300            let ret = CStr::from_ptr(ptr).to_str()?.to_owned();
301            sys::tract_free_cstring(ptr);
302            Ok(ret)
303        }
304    }
305
306    fn input_fact(&self, id: usize) -> Result<Fact> {
307        let mut ptr = null_mut();
308        check!(sys::tract_model_input_fact(self.0, id, &mut ptr))?;
309        Ok(Fact(ptr))
310    }
311
312    fn output_fact(&self, id: usize) -> Result<Fact> {
313        let mut ptr = null_mut();
314        check!(sys::tract_model_output_fact(self.0, id, &mut ptr))?;
315        Ok(Fact(ptr))
316    }
317
318    fn into_runnable(self) -> Result<Runnable> {
319        let mut model = self;
320        let mut runnable = null_mut();
321        check!(sys::tract_model_into_runnable(&mut model.0, &mut runnable))?;
322        Ok(Runnable(runnable))
323    }
324
325    fn transform(&mut self, spec: impl Into<TransformSpec>) -> Result<()> {
326        let transform = spec.into().to_transform_string();
327        let t = CString::new(transform)?;
328        check!(sys::tract_model_transform(self.0, t.as_ptr()))?;
329        Ok(())
330    }
331
332    fn property_keys(&self) -> Result<Vec<String>> {
333        let mut len = 0;
334        check!(sys::tract_model_property_count(self.0, &mut len))?;
335        let mut keys = vec![null_mut(); len];
336        check!(sys::tract_model_property_names(self.0, keys.as_mut_ptr()))?;
337        unsafe {
338            keys.into_iter()
339                .map(|pc| {
340                    let s = CStr::from_ptr(pc).to_str()?.to_owned();
341                    sys::tract_free_cstring(pc);
342                    Ok(s)
343                })
344                .collect()
345        }
346    }
347
348    fn property(&self, name: impl AsRef<str>) -> Result<Tensor> {
349        let mut v = null_mut();
350        let name = CString::new(name.as_ref())?;
351        check!(sys::tract_model_property(self.0, name.as_ptr(), &mut v))?;
352        Ok(Tensor(v))
353    }
354
355    fn parse_fact(&self, spec: &str) -> Result<Self::Fact> {
356        let spec = CString::new(spec)?;
357        let mut ptr = null_mut();
358        check!(sys::tract_model_parse_fact(self.0, spec.as_ptr(), &mut ptr))?;
359        Ok(Fact(ptr))
360    }
361}
362
363// RUNTIME
364wrapper!(Runtime, TractRuntime, tract_runtime_release);
365
366pub fn runtime_for_name(name: &str) -> Result<Runtime> {
367    let mut rt = null_mut();
368    let name = CString::new(name)?;
369    check!(sys::tract_runtime_for_name(name.as_ptr(), &mut rt))?;
370    Ok(Runtime(rt))
371}
372
373impl RuntimeInterface for Runtime {
374    type Runnable = Runnable;
375
376    type Model = Model;
377
378    fn name(&self) -> Result<String> {
379        let mut ptr = null_mut();
380        check!(sys::tract_runtime_name(self.0, &mut ptr))?;
381        unsafe {
382            let ret = CStr::from_ptr(ptr).to_str()?.to_owned();
383            sys::tract_free_cstring(ptr);
384            Ok(ret)
385        }
386    }
387
388    fn prepare(&self, model: Self::Model) -> Result<Self::Runnable> {
389        let mut model = model;
390        let mut runnable = null_mut();
391        check!(sys::tract_runtime_prepare(self.0, &mut model.0, &mut runnable))?;
392        Ok(Runnable(runnable))
393    }
394}
395
396// RUNNABLE
397wrapper!(Runnable, TractRunnable, tract_runnable_release);
398unsafe impl Send for Runnable {}
399unsafe impl Sync for Runnable {}
400
401impl RunnableInterface for Runnable {
402    type Tensor = Tensor;
403    type State = State;
404    type Fact = Fact;
405
406    fn run(&self, inputs: impl IntoInputs<Tensor>) -> Result<Vec<Tensor>> {
407        StateInterface::run(&mut self.spawn_state()?, inputs.into_inputs()?)
408    }
409
410    fn spawn_state(&self) -> Result<State> {
411        let mut state = null_mut();
412        check!(sys::tract_runnable_spawn_state(self.0, &mut state))?;
413        Ok(State(state))
414    }
415
416    fn input_count(&self) -> Result<usize> {
417        let mut count = 0;
418        check!(sys::tract_runnable_input_count(self.0, &mut count))?;
419        Ok(count)
420    }
421
422    fn output_count(&self) -> Result<usize> {
423        let mut count = 0;
424        check!(sys::tract_runnable_output_count(self.0, &mut count))?;
425        Ok(count)
426    }
427
428    fn input_fact(&self, id: usize) -> Result<Self::Fact> {
429        let mut ptr = null_mut();
430        check!(sys::tract_runnable_input_fact(self.0, id, &mut ptr))?;
431        Ok(Fact(ptr))
432    }
433
434    fn output_fact(&self, id: usize) -> Result<Self::Fact> {
435        let mut ptr = null_mut();
436        check!(sys::tract_runnable_output_fact(self.0, id, &mut ptr))?;
437        Ok(Fact(ptr))
438    }
439
440    fn property_keys(&self) -> Result<Vec<String>> {
441        let mut len = 0;
442        check!(sys::tract_runnable_property_count(self.0, &mut len))?;
443        let mut keys = vec![null_mut(); len];
444        check!(sys::tract_runnable_property_names(self.0, keys.as_mut_ptr()))?;
445        unsafe {
446            keys.into_iter()
447                .map(|pc| {
448                    let s = CStr::from_ptr(pc).to_str()?.to_owned();
449                    sys::tract_free_cstring(pc);
450                    Ok(s)
451                })
452                .collect()
453        }
454    }
455
456    fn property(&self, name: impl AsRef<str>) -> Result<Tensor> {
457        let mut v = null_mut();
458        let name = CString::new(name.as_ref())?;
459        check!(sys::tract_runnable_property(self.0, name.as_ptr(), &mut v))?;
460        Ok(Tensor(v))
461    }
462
463    fn cost_json(&self) -> Result<String> {
464        let input: Option<Vec<Tensor>> = None;
465        self.profile_json(input)
466    }
467
468    fn profile_json<I, IV, IE>(&self, inputs: Option<I>) -> Result<String>
469    where
470        I: IntoIterator<Item = IV>,
471        IV: TryInto<Self::Tensor, Error = IE>,
472        IE: Into<anyhow::Error>,
473    {
474        let inputs = if let Some(inputs) = inputs {
475            let inputs = inputs
476                .into_iter()
477                .map(|i| i.try_into().map_err(|e| e.into()))
478                .collect::<Result<Vec<Tensor>>>()?;
479            anyhow::ensure!(self.input_count()? == inputs.len());
480            Some(inputs)
481        } else {
482            None
483        };
484        let mut iptrs: Option<Vec<*mut sys::TractTensor>> =
485            inputs.as_ref().map(|is| is.iter().map(|v| v.0).collect());
486        let mut json: *mut i8 = null_mut();
487        let values = iptrs.as_mut().map(|it| it.as_mut_ptr()).unwrap_or(null_mut());
488
489        check!(sys::tract_runnable_profile_json(self.0, values, &mut json))?;
490        anyhow::ensure!(!json.is_null());
491        unsafe {
492            let s = CStr::from_ptr(json).to_owned();
493            sys::tract_free_cstring(json);
494            Ok(s.to_str()?.to_owned())
495        }
496    }
497}
498
499// STATE
500pub struct State(*mut sys::TractState);
501
502impl Drop for State {
503    fn drop(&mut self) {
504        unsafe {
505            sys::tract_state_destroy(&mut self.0);
506        }
507    }
508}
509
510impl std::fmt::Debug for State {
511    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
512        write!(f, "State({:?})", self.0)
513    }
514}
515
516impl Clone for State {
517    fn clone(&self) -> Self {
518        let mut clone = null_mut();
519        unsafe {
520            sys::tract_state_clone(self.0, &mut clone);
521        }
522        State(clone)
523    }
524}
525
526// Safety: the handle exclusively owns a `tract::State`, which is Send.
527unsafe impl Send for State {}
528
529impl StateInterface for State {
530    type Tensor = Tensor;
531    type Fact = Fact;
532
533    fn run(&mut self, inputs: impl IntoInputs<Tensor>) -> Result<Vec<Tensor>> {
534        let inputs = inputs.into_inputs()?;
535        let mut outputs = vec![null_mut(); self.output_count()?];
536        let mut inputs: Vec<_> = inputs.iter().map(|v| v.0).collect();
537        check!(sys::tract_state_run(self.0, inputs.as_mut_ptr(), outputs.as_mut_ptr()))?;
538        let outputs = outputs.into_iter().map(Tensor).collect();
539        Ok(outputs)
540    }
541
542    fn input_count(&self) -> Result<usize> {
543        let mut count = 0;
544        check!(sys::tract_state_input_count(self.0, &mut count))?;
545        Ok(count)
546    }
547
548    fn output_count(&self) -> Result<usize> {
549        let mut count = 0;
550        check!(sys::tract_state_output_count(self.0, &mut count))?;
551        Ok(count)
552    }
553}
554
555// TENSOR
556wrapper!(Tensor, TractTensor, tract_tensor_destroy);
557wrapper_clone!(Tensor, tract_tensor_clone);
558unsafe impl Send for Tensor {}
559unsafe impl Sync for Tensor {}
560
561impl TensorInterface for Tensor {
562    fn from_bytes(dt: DatumType, shape: &[usize], data: &[u8]) -> Result<Self> {
563        anyhow::ensure!(data.len() == shape.iter().product::<usize>() * dt.size_of());
564        let mut value = null_mut();
565        check!(sys::tract_tensor_from_bytes(
566            dt as _,
567            shape.len(),
568            shape.as_ptr(),
569            data.as_ptr() as _,
570            &mut value
571        ))?;
572        Ok(Tensor(value))
573    }
574
575    fn as_bytes(&self) -> Result<(DatumType, &[usize], &[u8])> {
576        let mut rank = 0;
577        let mut dt = sys::DatumType_TRACT_DATUM_TYPE_BOOL as _;
578        let mut shape = null();
579        let mut data = null();
580        check!(sys::tract_tensor_as_bytes(self.0, &mut dt, &mut rank, &mut shape, &mut data))?;
581        unsafe {
582            let dt: DatumType = std::mem::transmute(dt);
583            let shape = std::slice::from_raw_parts(shape, rank);
584            let len: usize = shape.iter().product();
585            let data = std::slice::from_raw_parts(data as *const u8, len * dt.size_of());
586            Ok((dt, shape, data))
587        }
588    }
589
590    fn datum_type(&self) -> Result<DatumType> {
591        let mut dt = sys::DatumType_TRACT_DATUM_TYPE_BOOL as _;
592        check!(sys::tract_tensor_as_bytes(
593            self.0,
594            &mut dt,
595            std::ptr::null_mut(),
596            std::ptr::null_mut(),
597            std::ptr::null_mut()
598        ))?;
599        unsafe {
600            let dt: DatumType = std::mem::transmute(dt);
601            Ok(dt)
602        }
603    }
604
605    fn convert_to(&self, to: DatumType) -> Result<Self> {
606        let mut new = null_mut();
607        check!(sys::tract_tensor_convert_to(self.0, to as _, &mut new))?;
608        Ok(Tensor(new))
609    }
610}
611
612impl PartialEq for Tensor {
613    fn eq(&self, other: &Self) -> bool {
614        let Ok((me_dt, me_shape, me_data)) = self.as_bytes() else { return false };
615        let Ok((other_dt, other_shape, other_data)) = other.as_bytes() else { return false };
616        me_dt == other_dt && me_shape == other_shape && me_data == other_data
617    }
618}
619
620// FACT
621wrapper!(Fact, TractFact, tract_fact_destroy);
622wrapper_clone!(Fact, tract_fact_clone);
623
624impl Fact {
625    fn new(model: &Model, spec: impl ToString) -> Result<Fact> {
626        let cstr = CString::new(spec.to_string())?;
627        let mut fact = null_mut();
628        check!(sys::tract_model_parse_fact(model.0, cstr.as_ptr(), &mut fact))?;
629        Ok(Fact(fact))
630    }
631
632    fn dump(&self) -> Result<String> {
633        let mut ptr = null_mut();
634        check!(sys::tract_fact_dump(self.0, &mut ptr))?;
635        unsafe {
636            let s = CStr::from_ptr(ptr).to_owned();
637            sys::tract_free_cstring(ptr);
638            Ok(s.to_str()?.to_owned())
639        }
640    }
641}
642
643impl FactInterface for Fact {
644    type Dim = Dim;
645
646    fn datum_type(&self) -> Result<DatumType> {
647        let mut dt = 0u32;
648        check!(sys::tract_fact_datum_type(self.0, &mut dt as *const u32 as _))?;
649        Ok(unsafe { std::mem::transmute::<u32, DatumType>(dt) })
650    }
651
652    fn rank(&self) -> Result<usize> {
653        let mut rank = 0;
654        check!(sys::tract_fact_rank(self.0, &mut rank))?;
655        Ok(rank)
656    }
657
658    fn dim(&self, axis: usize) -> Result<Self::Dim> {
659        let mut ptr = null_mut();
660        check!(sys::tract_fact_dim(self.0, axis, &mut ptr))?;
661        Ok(Dim(ptr))
662    }
663}
664
665impl std::fmt::Display for Fact {
666    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
667        match self.dump() {
668            Ok(s) => f.write_str(&s),
669            Err(_) => Err(std::fmt::Error),
670        }
671    }
672}
673
674// INFERENCE FACT
675wrapper!(InferenceFact, TractInferenceFact, tract_inference_fact_destroy);
676wrapper_clone!(InferenceFact, tract_inference_fact_clone);
677
678impl InferenceFact {
679    fn new(model: &InferenceModel, spec: impl ToString) -> Result<InferenceFact> {
680        let cstr = CString::new(spec.to_string())?;
681        let mut fact = null_mut();
682        check!(sys::tract_inference_fact_parse(model.0, cstr.as_ptr(), &mut fact))?;
683        Ok(InferenceFact(fact))
684    }
685
686    fn dump(&self) -> Result<String> {
687        let mut ptr = null_mut();
688        check!(sys::tract_inference_fact_dump(self.0, &mut ptr))?;
689        unsafe {
690            let s = CStr::from_ptr(ptr).to_owned();
691            sys::tract_free_cstring(ptr);
692            Ok(s.to_str()?.to_owned())
693        }
694    }
695}
696
697impl InferenceFactInterface for InferenceFact {
698    fn empty() -> Result<InferenceFact> {
699        let mut fact = null_mut();
700        check!(sys::tract_inference_fact_empty(&mut fact))?;
701        Ok(InferenceFact(fact))
702    }
703}
704
705impl Default for InferenceFact {
706    fn default() -> Self {
707        Self::empty().unwrap()
708    }
709}
710
711impl std::fmt::Display for InferenceFact {
712    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
713        match self.dump() {
714            Ok(s) => f.write_str(&s),
715            Err(_) => Err(std::fmt::Error),
716        }
717    }
718}
719
720as_inference_fact_impl!(InferenceModel, InferenceFact);
721as_fact_impl!(Model, Fact);
722
723// Dim
724wrapper!(Dim, TractDim, tract_dim_destroy);
725wrapper_clone!(Dim, tract_dim_clone);
726
727impl Dim {
728    fn dump(&self) -> Result<String> {
729        let mut ptr = null_mut();
730        check!(sys::tract_dim_dump(self.0, &mut ptr))?;
731        unsafe {
732            let s = CStr::from_ptr(ptr).to_owned();
733            sys::tract_free_cstring(ptr);
734            Ok(s.to_str()?.to_owned())
735        }
736    }
737}
738
739impl DimInterface for Dim {
740    fn eval(&self, values: impl IntoIterator<Item = (impl AsRef<str>, i64)>) -> Result<Self> {
741        let (names, values): (Vec<_>, Vec<_>) = values.into_iter().unzip();
742        let c_strings: Vec<CString> =
743            names.into_iter().map(|a| Ok(CString::new(a.as_ref())?)).collect::<Result<_>>()?;
744        let ptrs: Vec<_> = c_strings.iter().map(|cs| cs.as_ptr()).collect();
745        let mut ptr = null_mut();
746        check!(sys::tract_dim_eval(self.0, ptrs.len(), ptrs.as_ptr(), values.as_ptr(), &mut ptr))?;
747        Ok(Dim(ptr))
748    }
749
750    fn to_int64(&self) -> Result<i64> {
751        let mut i = 0;
752        check!(sys::tract_dim_to_int64(self.0, &mut i))?;
753        Ok(i)
754    }
755}
756
757impl std::fmt::Display for Dim {
758    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
759        match self.dump() {
760            Ok(s) => f.write_str(&s),
761            Err(_) => Err(std::fmt::Error),
762        }
763    }
764}
765
766#[cfg(test)]
767mod tests {
768    use super::*;
769
770    #[test]
771    fn clone_tensor_no_double_free() {
772        let t = Tensor::from_slice::<f32>(&[2, 2], &[1.0, 2.0, 3.0, 4.0]).unwrap();
773        let clone = t.clone();
774        assert_eq!(t, clone);
775    }
776}