@@ -8,7 +8,7 @@ namespace LLama.Native;
88/// underlying native pointer (when created via <see cref="Copy"/>) or act as non-owning views
99/// produced by the tokenizer.
1010/// </summary>
11- public sealed class SafeMtmdInputChunk : IDisposable
11+ public sealed class SafeMtmdInputChunk : SafeLLamaHandleBase
1212{
1313 /// <summary>
1414 /// Chunk modality returned by the native tokenizer.
@@ -23,15 +23,18 @@ public enum SafeMtmdInputChunkType
2323 /// <summary>
2424 /// Raw pointer to the native chunk structure.
2525 /// </summary>
26- public IntPtr NativePtr { get ; private set ; }
27-
28- private bool _ownsPtr ;
29- private bool _disposed ;
26+ public IntPtr NativePtr
27+ {
28+ get
29+ {
30+ EnsureNotDisposed ( ) ;
31+ return DangerousGetHandle ( ) ;
32+ }
33+ }
3034
31- private SafeMtmdInputChunk ( IntPtr ptr , bool owns )
35+ private SafeMtmdInputChunk ( IntPtr handle , bool ownsHandle )
36+ : base ( handle , ownsHandle )
3237 {
33- NativePtr = ptr ;
34- _ownsPtr = owns ;
3538 }
3639
3740 /// <summary>
@@ -40,7 +43,7 @@ private SafeMtmdInputChunk(IntPtr ptr, bool owns)
4043 /// <param name="ptr">Pointer returned by the native tokenizer.</param>
4144 /// <returns>Managed wrapper, or <c>null</c> when the pointer is null.</returns>
4245 public static SafeMtmdInputChunk Wrap ( IntPtr ptr )
43- => ptr == IntPtr . Zero ? null : new SafeMtmdInputChunk ( ptr , false ) ;
46+ => ptr == IntPtr . Zero ? null : new SafeMtmdInputChunk ( ptr , ownsHandle : false ) ;
4447
4548 /// <summary>
4649 /// Create an owning copy of the current chunk. The caller becomes responsible for disposal.
@@ -49,10 +52,11 @@ public static SafeMtmdInputChunk Wrap(IntPtr ptr)
4952 /// <exception cref="ObjectDisposedException">Thrown when the current wrapper has been disposed.</exception>
5053 public SafeMtmdInputChunk Copy ( )
5154 {
52- EnsureNotDisposed ( ) ;
53-
54- var p = NativeApi . mtmd_input_chunk_copy ( NativePtr ) ;
55- return p == IntPtr . Zero ? null : new SafeMtmdInputChunk ( p , true ) ;
55+ return WithHandle ( ptr =>
56+ {
57+ var clone = NativeApi . mtmd_input_chunk_copy ( ptr ) ;
58+ return clone == IntPtr . Zero ? null : new SafeMtmdInputChunk ( clone , ownsHandle : true ) ;
59+ } ) ;
5660 }
5761
5862 /// <summary>
@@ -62,8 +66,7 @@ public SafeMtmdInputChunkType Type
6266 {
6367 get
6468 {
65- EnsureNotDisposed ( ) ;
66- return ( SafeMtmdInputChunkType ) NativeApi . mtmd_input_chunk_get_type ( NativePtr ) ;
69+ return WithHandle ( ptr => ( SafeMtmdInputChunkType ) NativeApi . mtmd_input_chunk_get_type ( ptr ) ) ;
6770 }
6871 }
6972
@@ -74,8 +77,7 @@ public ulong NTokens
7477 {
7578 get
7679 {
77- EnsureNotDisposed ( ) ;
78- return NativeApi . mtmd_input_chunk_get_n_tokens ( NativePtr ) . ToUInt64 ( ) ;
80+ return WithHandle ( ptr => NativeApi . mtmd_input_chunk_get_n_tokens ( ptr ) . ToUInt64 ( ) ) ;
7981 }
8082 }
8183
@@ -86,8 +88,11 @@ public string Id
8688 {
8789 get
8890 {
89- EnsureNotDisposed ( ) ;
90- return Marshal . PtrToStringAnsi ( NativeApi . mtmd_input_chunk_get_id ( NativePtr ) ) ?? string . Empty ;
91+ return WithHandle ( ptr =>
92+ {
93+ var idPtr = NativeApi . mtmd_input_chunk_get_id ( ptr ) ;
94+ return Marshal . PtrToStringAnsi ( idPtr ) ?? string . Empty ;
95+ } ) ;
9196 }
9297 }
9398
@@ -98,8 +103,7 @@ public long NPos
98103 {
99104 get
100105 {
101- EnsureNotDisposed ( ) ;
102- return NativeApi . mtmd_input_chunk_get_n_pos ( NativePtr ) ;
106+ return WithHandle ( ptr => NativeApi . mtmd_input_chunk_get_n_pos ( ptr ) ) ;
103107 }
104108 }
105109
@@ -112,39 +116,63 @@ public unsafe ReadOnlySpan<uint> GetTextTokensSpan()
112116 {
113117 EnsureNotDisposed ( ) ;
114118
115- UIntPtr n ;
116- var p = ( uint * ) NativeApi . mtmd_input_chunk_get_tokens_text ( NativePtr , out n ) ;
117- return p == null ? ReadOnlySpan < uint > . Empty : new ReadOnlySpan < uint > ( p , checked ( ( int ) n . ToUInt64 ( ) ) ) ;
119+ bool added = false ;
120+ try
121+ {
122+ DangerousAddRef ( ref added ) ;
123+ UIntPtr nTokens ;
124+ var tokensPtr = ( uint * ) NativeApi . mtmd_input_chunk_get_tokens_text ( DangerousGetHandle ( ) , out nTokens ) ;
125+ if ( tokensPtr == null )
126+ return ReadOnlySpan < uint > . Empty ;
127+
128+ var length = checked ( ( int ) nTokens . ToUInt64 ( ) ) ;
129+ return new ReadOnlySpan < uint > ( tokensPtr , length ) ;
130+ }
131+ finally
132+ {
133+ if ( added )
134+ DangerousRelease ( ) ;
135+ }
118136 }
119137
120138 /// <summary>
121- /// Release the underlying native resources if this instance owns them .
139+ /// Releases the native chunk when ownership is held by this instance .
122140 /// </summary>
123- public void Dispose ( )
141+ protected override bool ReleaseHandle ( )
124142 {
125- if ( _disposed )
126- return ;
127-
128- if ( _ownsPtr && NativePtr != IntPtr . Zero )
143+ if ( handle != IntPtr . Zero )
129144 {
130- NativeApi . mtmd_input_chunk_free ( NativePtr ) ;
145+ NativeApi . mtmd_input_chunk_free ( handle ) ;
146+ SetHandle ( IntPtr . Zero ) ;
131147 }
132148
133- NativePtr = IntPtr . Zero ;
134- _ownsPtr = false ;
135- _disposed = true ;
136-
137- GC . SuppressFinalize ( this ) ;
149+ return true ;
138150 }
139151
140- /// <summary>
141- /// Finalizer to ensure native memory is reclaimed when Dispose is not called by owners.
142- /// </summary>
143- ~ SafeMtmdInputChunk ( ) => Dispose ( ) ;
144-
145152 private void EnsureNotDisposed ( )
146153 {
147- if ( _disposed || NativePtr == IntPtr . Zero )
154+ if ( IsClosed || IsInvalid )
148155 throw new ObjectDisposedException ( nameof ( SafeMtmdInputChunk ) ) ;
149156 }
157+
158+ private T WithHandle < T > ( Func < IntPtr , T > action )
159+ {
160+ EnsureNotDisposed ( ) ;
161+
162+ bool added = false ;
163+ try
164+ {
165+ DangerousAddRef ( ref added ) ;
166+ var ptr = DangerousGetHandle ( ) ;
167+ if ( ptr == IntPtr . Zero )
168+ throw new ObjectDisposedException ( nameof ( SafeMtmdInputChunk ) ) ;
169+
170+ return action ( ptr ) ;
171+ }
172+ finally
173+ {
174+ if ( added )
175+ DangerousRelease ( ) ;
176+ }
177+ }
150178}
0 commit comments