1use std::ffi::CStr;
4use std::marker::PhantomData;
5use std::os::raw::c_int;
6use std::sync::Arc;
7
8use crate::error::{Error, Result};
9use crate::ffi::{TfLiteQuantizationParams, TfLiteTensor, TfLiteType};
10use crate::library::TfLiteLibrary;
11
12pub struct Tensor<'a> {
18 raw: *const TfLiteTensor,
19 lib: Arc<TfLiteLibrary>,
20 _marker: PhantomData<&'a ()>,
21}
22
23pub struct TensorMut<'a> {
28 raw: *mut TfLiteTensor,
29 lib: Arc<TfLiteLibrary>,
30 _marker: PhantomData<&'a mut ()>,
31}
32
33impl<'a> Tensor<'a> {
34 pub(crate) unsafe fn from_raw(raw: *const TfLiteTensor, lib: Arc<TfLiteLibrary>) -> Self {
40 Self { raw, lib, _marker: PhantomData }
41 }
42
43 pub fn dims(&self) -> Vec<i32> {
45 common_dims(self.raw, &self.lib)
46 }
47
48 pub fn dtype(&self) -> TfLiteType {
50 common_dtype(self.raw, &self.lib)
51 }
52
53 pub fn name(&self) -> Option<&str> {
55 common_name(self.raw, &self.lib)
56 }
57
58 pub fn byte_size(&self) -> usize {
60 common_byte_size(self.raw, &self.lib)
61 }
62
63 pub fn quantization(&self) -> TfLiteQuantizationParams {
65 common_quantization(self.raw, &self.lib)
66 }
67
68 pub fn data(&self) -> &[u8] {
72 common_data(self.raw, &self.lib)
73 }
74
75 pub fn as_slice_f32(&self) -> Result<&[f32]> {
77 typed_slice::<f32>(self.raw, &self.lib, TfLiteType::Float32)
78 }
79
80 pub fn as_slice_i8(&self) -> Result<&[i8]> {
82 typed_slice::<i8>(self.raw, &self.lib, TfLiteType::Int8)
83 }
84
85 pub fn as_slice_u8(&self) -> Result<&[u8]> {
87 typed_slice::<u8>(self.raw, &self.lib, TfLiteType::UInt8)
88 }
89
90 pub fn as_slice_i32(&self) -> Result<&[i32]> {
92 typed_slice::<i32>(self.raw, &self.lib, TfLiteType::Int32)
93 }
94
95 pub fn to_vec_f32(&self) -> Result<Vec<f32>> {
101 match self.dtype() {
102 TfLiteType::Float32 => self.as_slice_f32().map(<[f32]>::to_vec),
103 TfLiteType::Int8 => {
104 let raw = self.as_slice_i8()?;
105 Ok(dequantize_i8(raw, self.quantization()))
106 }
107 TfLiteType::UInt8 => {
108 let raw = self.as_slice_u8()?;
109 Ok(dequantize_u8(raw, self.quantization()))
110 }
111 actual => Err(Error::DtypeMismatch { expected: TfLiteType::Float32, actual }),
112 }
113 }
114}
115
116impl<'a> TensorMut<'a> {
117 pub(crate) unsafe fn from_raw(raw: *mut TfLiteTensor, lib: Arc<TfLiteLibrary>) -> Self {
123 Self { raw, lib, _marker: PhantomData }
124 }
125
126 pub fn dims(&self) -> Vec<i32> {
128 common_dims(self.raw, &self.lib)
129 }
130
131 pub fn dtype(&self) -> TfLiteType {
133 common_dtype(self.raw, &self.lib)
134 }
135
136 pub fn name(&self) -> Option<&str> {
138 common_name(self.raw, &self.lib)
139 }
140
141 pub fn byte_size(&self) -> usize {
143 common_byte_size(self.raw, &self.lib)
144 }
145
146 pub fn quantization(&self) -> TfLiteQuantizationParams {
148 common_quantization(self.raw, &self.lib)
149 }
150
151 pub fn data(&self) -> &[u8] {
153 common_data(self.raw, &self.lib)
154 }
155
156 pub fn data_mut(&mut self) -> Result<&mut [u8]> {
161 unsafe {
164 let ptr = (self.lib.tensor_data)(self.raw) as *mut u8;
165 if ptr.is_null() {
166 return Err(Error::NullData);
167 }
168 let len = (self.lib.tensor_byte_size)(self.raw);
169 Ok(std::slice::from_raw_parts_mut(ptr, len))
170 }
171 }
172}
173
174fn common_dims(raw: *const TfLiteTensor, lib: &TfLiteLibrary) -> Vec<i32> {
177 unsafe {
179 let n = (lib.tensor_num_dims)(raw);
180 if n <= 0 {
181 return Vec::new();
182 }
183 (0..n).map(|i| (lib.tensor_dim)(raw, i as c_int)).collect()
184 }
185}
186
187fn common_dtype(raw: *const TfLiteTensor, lib: &TfLiteLibrary) -> TfLiteType {
188 unsafe { (lib.tensor_type)(raw) }
190}
191
192fn common_name<'a>(raw: *const TfLiteTensor, lib: &TfLiteLibrary) -> Option<&'a str> {
193 unsafe {
198 let ptr = (lib.tensor_name)(raw);
199 if ptr.is_null() {
200 return None;
201 }
202 CStr::from_ptr(ptr).to_str().ok()
203 }
204}
205
206fn common_byte_size(raw: *const TfLiteTensor, lib: &TfLiteLibrary) -> usize {
207 unsafe { (lib.tensor_byte_size)(raw) }
209}
210
211fn common_quantization(
212 raw: *const TfLiteTensor,
213 lib: &TfLiteLibrary,
214) -> TfLiteQuantizationParams {
215 unsafe { (lib.tensor_quantization_params)(raw) }
217}
218
219fn common_data<'a>(raw: *const TfLiteTensor, lib: &TfLiteLibrary) -> &'a [u8] {
220 unsafe {
222 let ptr = (lib.tensor_data)(raw) as *const u8;
223 if ptr.is_null() {
224 return &[];
225 }
226 let len = (lib.tensor_byte_size)(raw);
227 std::slice::from_raw_parts(ptr, len)
228 }
229}
230
231fn typed_slice<'a, T: Copy>(
232 raw: *const TfLiteTensor,
233 lib: &TfLiteLibrary,
234 expected: TfLiteType,
235) -> Result<&'a [T]> {
236 let actual = common_dtype(raw, lib);
237 if actual != expected {
238 return Err(Error::DtypeMismatch { expected, actual });
239 }
240 unsafe {
243 let ptr = (lib.tensor_data)(raw) as *const T;
244 if ptr.is_null() {
245 return Err(Error::NullData);
246 }
247 let bytes = (lib.tensor_byte_size)(raw);
248 let len = bytes / std::mem::size_of::<T>();
249 Ok(std::slice::from_raw_parts(ptr, len))
250 }
251}
252
253pub(crate) fn dequantize_i8(raw: &[i8], params: TfLiteQuantizationParams) -> Vec<f32> {
258 let zp = params.zero_point as f32;
259 let scale = params.scale;
260 raw.iter().map(|&q| (q as f32 - zp) * scale).collect()
261}
262
263pub(crate) fn dequantize_u8(raw: &[u8], params: TfLiteQuantizationParams) -> Vec<f32> {
265 let zp = params.zero_point as f32;
266 let scale = params.scale;
267 raw.iter().map(|&q| (q as f32 - zp) * scale).collect()
268}
269
270#[cfg(test)]
271mod tests {
272 use super::*;
273
274 #[test]
275 fn dequantize_i8_matches_formula() {
276 let params = TfLiteQuantizationParams { scale: 0.5, zero_point: -10 };
278 let raw: Vec<i8> = vec![-10, 0, 10, 20];
279 let got = dequantize_i8(&raw, params);
280 assert_eq!(got, vec![0.0, 5.0, 10.0, 15.0]);
281 }
282
283 #[test]
284 fn dequantize_i8_with_zero_scale_is_all_zero() {
285 let params = TfLiteQuantizationParams { scale: 0.0, zero_point: 42 };
286 let got = dequantize_i8(&[1, 2, 3, 4], params);
287 assert_eq!(got, vec![0.0, 0.0, 0.0, 0.0]);
288 }
289
290 #[test]
291 fn dequantize_u8_matches_formula() {
292 let params = TfLiteQuantizationParams { scale: 0.25, zero_point: 128 };
294 let raw: Vec<u8> = vec![128, 132, 124, 0];
295 let got = dequantize_u8(&raw, params);
296 assert_eq!(got, vec![0.0, 1.0, -1.0, -32.0]);
297 }
298
299 #[test]
300 fn dequantize_handles_empty_input() {
301 let params = TfLiteQuantizationParams { scale: 1.0, zero_point: 0 };
302 assert!(dequantize_i8(&[], params).is_empty());
303 assert!(dequantize_u8(&[], params).is_empty());
304 }
305}