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
142wrapper!(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
186wrapper!(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
267wrapper!(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
363wrapper!(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
396wrapper!(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
499pub 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
526unsafe 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
555wrapper!(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
620wrapper!(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
674wrapper!(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
723wrapper!(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}