1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
using System;
using System.Runtime.InteropServices;
using TrustformersMobile.Native;
namespace TrustformersMobile
{
/// <summary>
/// TrustformeRS Mobile inference engine for running ML models on mobile devices
/// </summary>
public class MobileInferenceEngine : IDisposable
{
private UIntPtr _handle;
private bool _disposed = false;
/// <summary>
/// Creates a new inference engine with the specified configuration and model
/// </summary>
/// <param name="config">Mobile configuration</param>
/// <param name="modelPath">Path to the model file</param>
/// <exception cref="ArgumentNullException">Thrown if config or modelPath is null</exception>
/// <exception cref="TrustformersMobileException">Thrown if engine creation fails</exception>
public MobileInferenceEngine(MobileConfig config, string modelPath)
{
if (config == null)
throw new ArgumentNullException(nameof(config));
if (string.IsNullOrEmpty(modelPath))
throw new ArgumentNullException(nameof(modelPath));
// Validate configuration before creating engine
config.Validate();
var error = TrustformersMobileNative.trustformers_mobile_engine_create(
config.Handle,
modelPath,
out _handle);
ThrowIfError(error);
}
/// <summary>
/// Runs inference with float32 input data
/// </summary>
/// <param name="inputData">Input data as float array</param>
/// <returns>Output data as float array</returns>
/// <exception cref="ArgumentNullException">Thrown if inputData is null</exception>
/// <exception cref="TrustformersMobileException">Thrown if inference fails</exception>
public unsafe float[] InferenceF32(float[] inputData)
{
if (inputData == null)
throw new ArgumentNullException(nameof(inputData));
ThrowIfDisposed();
// Estimate output size (this could be made more sophisticated)
var estimatedOutputSize = inputData.Length * 2; // Conservative estimate
var outputData = new float[estimatedOutputSize];
fixed (float* inputPtr = inputData)
fixed (float* outputPtr = outputData)
{
var error = TrustformersMobileNative.trustformers_mobile_engine_inference_f32(
_handle,
(IntPtr)inputPtr,
(UIntPtr)inputData.Length,
(IntPtr)outputPtr,
(UIntPtr)outputData.Length,
out UIntPtr actualOutputSize);
ThrowIfError(error);
// Return only the actual output data
var actualSize = (int)actualOutputSize;
if (actualSize <= outputData.Length)
{
Array.Resize(ref outputData, actualSize);
return outputData;
}
else
{
// Need to reallocate with larger buffer
var largerOutput = new float[actualSize];
fixed (float* largerOutputPtr = largerOutput)
{
error = TrustformersMobileNative.trustformers_mobile_engine_inference_f32(
_handle,
(IntPtr)inputPtr,
(UIntPtr)inputData.Length,
(IntPtr)largerOutputPtr,
(UIntPtr)largerOutput.Length,
out actualOutputSize);
ThrowIfError(error);
return largerOutput;
}
}
}
}
/// <summary>
/// Runs inference with float32 input data and writes output to provided buffer
/// </summary>
/// <param name="inputData">Input data as float array</param>
/// <param name="outputData">Output buffer to write results to</param>
/// <returns>Actual number of output elements written</returns>
/// <exception cref="ArgumentNullException">Thrown if inputData or outputData is null</exception>
/// <exception cref="TrustformersMobileException">Thrown if inference fails</exception>
public unsafe int InferenceF32(float[] inputData, float[] outputData)
{
if (inputData == null)
throw new ArgumentNullException(nameof(inputData));
if (outputData == null)
throw new ArgumentNullException(nameof(outputData));
ThrowIfDisposed();
fixed (float* inputPtr = inputData)
fixed (float* outputPtr = outputData)
{
var error = TrustformersMobileNative.trustformers_mobile_engine_inference_f32(
_handle,
(IntPtr)inputPtr,
(UIntPtr)inputData.Length,
(IntPtr)outputPtr,
(UIntPtr)outputData.Length,
out UIntPtr actualOutputSize);
ThrowIfError(error);
return (int)actualOutputSize;
}
}
/// <summary>
/// Runs inference with float32 input data using Span<T> for zero-copy operations
/// </summary>
/// <param name="inputData">Input data as ReadOnlySpan<float></param>
/// <param name="outputData">Output buffer as Span<float></param>
/// <returns>Actual number of output elements written</returns>
/// <exception cref="TrustformersMobileException">Thrown if inference fails</exception>
public unsafe int InferenceF32(ReadOnlySpan<float> inputData, Span<float> outputData)
{
ThrowIfDisposed();
fixed (float* inputPtr = inputData)
fixed (float* outputPtr = outputData)
{
var error = TrustformersMobileNative.trustformers_mobile_engine_inference_f32(
_handle,
(IntPtr)inputPtr,
(UIntPtr)inputData.Length,
(IntPtr)outputPtr,
(UIntPtr)outputData.Length,
out UIntPtr actualOutputSize);
ThrowIfError(error);
return (int)actualOutputSize;
}
}
private void ThrowIfDisposed()
{
if (_disposed)
throw new ObjectDisposedException(nameof(MobileInferenceEngine));
}
private static void ThrowIfError(TrustformersMobileError error)
{
if (error != TrustformersMobileError.Success)
throw new TrustformersMobileException(error);
}
/// <summary>
/// Disposes the inference engine and frees native resources
/// </summary>
public void Dispose()
{
if (!_disposed && _handle != UIntPtr.Zero)
{
TrustformersMobileNative.trustformers_mobile_engine_free(_handle);
_handle = UIntPtr.Zero;
_disposed = true;
}
GC.SuppressFinalize(this);
}
/// <summary>
/// Finalizer
/// </summary>
~MobileInferenceEngine()
{
Dispose();
}
}
}