From e6dd7353fbe3ed7d8e789b095212fe20e9598ea9 Mon Sep 17 00:00:00 2001 From: axunonb Date: Thu, 29 Jan 2026 18:04:33 +0100 Subject: [PATCH 1/3] Refactor `ZCharArray` - clear array on disposal Refactored `ZCharArray` to use a non-nullable buffer array and introduced an explicit `_isDisposed` flag for disposal tracking. Updated `IsDisposed` and `Dispose()` logic to use the new flag, removed all null checks and null-forgiveness operators for `_bufferArray`, and ensured the buffer is always returned to the pool with `clearArray: true`. --- src/SmartFormat/ZString/ZCharArray.cs | 28 ++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/SmartFormat/ZString/ZCharArray.cs b/src/SmartFormat/ZString/ZCharArray.cs index 2406a956..ccb87cec 100644 --- a/src/SmartFormat/ZString/ZCharArray.cs +++ b/src/SmartFormat/ZString/ZCharArray.cs @@ -20,8 +20,9 @@ public struct ZCharArray : IDisposable private static readonly ArrayPool Pool = ArrayPool.Create(MaxBufferCapacity, 100); - private char[]? _bufferArray; + private char[] _bufferArray; private int _currentLength; + private bool _isDisposed; /// /// The default capacity of the array. @@ -48,6 +49,7 @@ public ZCharArray(int length) { _bufferArray = Pool.Rent(length); _currentLength = 0; + _isDisposed = false; } /// @@ -96,7 +98,7 @@ public int Capacity get { ThrowIfDisposed(); - return _bufferArray!.Length; + return _bufferArray.Length; } } @@ -119,7 +121,7 @@ public void Reset() private void Grow(int length) { var newArray = Pool.Rent(length); - Array.Copy(_bufferArray!, newArray, Math.Min(_bufferArray!.Length, length)); + Array.Copy(_bufferArray, newArray, Math.Min(_bufferArray.Length, length)); Pool.Return(_bufferArray); _bufferArray = newArray; } @@ -133,7 +135,7 @@ public void Write(Span data) { ThrowIfDisposed(); GrowBufferIfNeeded(data.Length); - data.CopyTo(_bufferArray!.AsSpan(_currentLength, data.Length)); + data.CopyTo(_bufferArray.AsSpan(_currentLength, data.Length)); _currentLength += data.Length; } @@ -146,7 +148,7 @@ public void Write(ReadOnlySpan data) { ThrowIfDisposed(); GrowBufferIfNeeded(data.Length); - data.CopyTo(_bufferArray!.AsSpan(_currentLength, data.Length)); + data.CopyTo(_bufferArray.AsSpan(_currentLength, data.Length)); _currentLength += data.Length; } @@ -159,7 +161,7 @@ public void Write(string data) { ThrowIfDisposed(); GrowBufferIfNeeded(data.Length); - data.AsSpan().CopyTo(_bufferArray!.AsSpan(_currentLength, data.Length)); + data.AsSpan().CopyTo(_bufferArray.AsSpan(_currentLength, data.Length)); _currentLength += data.Length; } @@ -172,7 +174,7 @@ public void Write(char c) { ThrowIfDisposed(); GrowBufferIfNeeded(1); - _bufferArray![_currentLength++] = c; + _bufferArray[_currentLength++] = c; } /// @@ -188,7 +190,7 @@ public void Write(char c, int count) for (var i = 0; i < count; i++) { - _bufferArray![_currentLength++] = c; + _bufferArray[_currentLength++] = c; } } @@ -246,7 +248,7 @@ private void GrowBufferIfNeeded(int dataLength) /// /// Returns if the array has been disposed. /// - public bool IsDisposed => _bufferArray is null; + public bool IsDisposed => _isDisposed; private void ThrowIfDisposed() { @@ -261,7 +263,7 @@ private void ThrowIfDisposed() public override string ToString() { ThrowIfDisposed(); - return new string(_bufferArray!, 0, _currentLength); + return new string(_bufferArray, 0, _currentLength); } /// @@ -269,9 +271,9 @@ public override string ToString() /// public void Dispose() { - if (IsDisposed) return; + if (_isDisposed) return; - Pool.Return(_bufferArray!); - _bufferArray = null; + Pool.Return(_bufferArray, clearArray: true); + _isDisposed = true; } } From ebbe32b02d118783a1b589fac1cf5d5fea65f492 Mon Sep 17 00:00:00 2001 From: axunonb Date: Thu, 29 Jan 2026 18:04:33 +0100 Subject: [PATCH 2/3] Refactor `ZCharArray` - clear array on disposal `ZCharArray` now ensures the buffer is always returned to the pool with `clearArray: true`. --- src/SmartFormat/ZString/ZCharArray.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/SmartFormat/ZString/ZCharArray.cs b/src/SmartFormat/ZString/ZCharArray.cs index ccb87cec..a13db1d8 100644 --- a/src/SmartFormat/ZString/ZCharArray.cs +++ b/src/SmartFormat/ZString/ZCharArray.cs @@ -273,7 +273,7 @@ public void Dispose() { if (_isDisposed) return; - Pool.Return(_bufferArray, clearArray: true); - _isDisposed = true; + Pool.Return(_bufferArray!, clearArray: true); + _bufferArray = null; } } From dbeab9e325e8026303f6e268b8725b6da23d69f1 Mon Sep 17 00:00:00 2001 From: axunonb Date: Thu, 29 Jan 2026 18:26:34 +0100 Subject: [PATCH 3/3] Update ZCharArray.cs --- src/SmartFormat/ZString/ZCharArray.cs | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/SmartFormat/ZString/ZCharArray.cs b/src/SmartFormat/ZString/ZCharArray.cs index a13db1d8..92279ce5 100644 --- a/src/SmartFormat/ZString/ZCharArray.cs +++ b/src/SmartFormat/ZString/ZCharArray.cs @@ -20,9 +20,8 @@ public struct ZCharArray : IDisposable private static readonly ArrayPool Pool = ArrayPool.Create(MaxBufferCapacity, 100); - private char[] _bufferArray; + private char[]? _bufferArray; private int _currentLength; - private bool _isDisposed; /// /// The default capacity of the array. @@ -49,7 +48,6 @@ public ZCharArray(int length) { _bufferArray = Pool.Rent(length); _currentLength = 0; - _isDisposed = false; } /// @@ -98,7 +96,7 @@ public int Capacity get { ThrowIfDisposed(); - return _bufferArray.Length; + return _bufferArray!.Length; } } @@ -121,8 +119,8 @@ public void Reset() private void Grow(int length) { var newArray = Pool.Rent(length); - Array.Copy(_bufferArray, newArray, Math.Min(_bufferArray.Length, length)); - Pool.Return(_bufferArray); + Array.Copy(_bufferArray!, newArray, Math.Min(_bufferArray!.Length, length)); + Pool.Return(_bufferArray, clearArray: true); _bufferArray = newArray; } @@ -135,7 +133,7 @@ public void Write(Span data) { ThrowIfDisposed(); GrowBufferIfNeeded(data.Length); - data.CopyTo(_bufferArray.AsSpan(_currentLength, data.Length)); + data.CopyTo(_bufferArray!.AsSpan(_currentLength, data.Length)); _currentLength += data.Length; } @@ -148,7 +146,7 @@ public void Write(ReadOnlySpan data) { ThrowIfDisposed(); GrowBufferIfNeeded(data.Length); - data.CopyTo(_bufferArray.AsSpan(_currentLength, data.Length)); + data.CopyTo(_bufferArray!.AsSpan(_currentLength, data.Length)); _currentLength += data.Length; } @@ -161,7 +159,7 @@ public void Write(string data) { ThrowIfDisposed(); GrowBufferIfNeeded(data.Length); - data.AsSpan().CopyTo(_bufferArray.AsSpan(_currentLength, data.Length)); + data.AsSpan().CopyTo(_bufferArray!.AsSpan(_currentLength, data.Length)); _currentLength += data.Length; } @@ -174,7 +172,7 @@ public void Write(char c) { ThrowIfDisposed(); GrowBufferIfNeeded(1); - _bufferArray[_currentLength++] = c; + _bufferArray![_currentLength++] = c; } /// @@ -190,7 +188,7 @@ public void Write(char c, int count) for (var i = 0; i < count; i++) { - _bufferArray[_currentLength++] = c; + _bufferArray![_currentLength++] = c; } } @@ -248,7 +246,7 @@ private void GrowBufferIfNeeded(int dataLength) /// /// Returns if the array has been disposed. /// - public bool IsDisposed => _isDisposed; + public bool IsDisposed => _bufferArray is null; private void ThrowIfDisposed() { @@ -263,7 +261,7 @@ private void ThrowIfDisposed() public override string ToString() { ThrowIfDisposed(); - return new string(_bufferArray, 0, _currentLength); + return new string(_bufferArray!, 0, _currentLength); } /// @@ -271,7 +269,7 @@ public override string ToString() /// public void Dispose() { - if (_isDisposed) return; + if (IsDisposed) return; Pool.Return(_bufferArray!, clearArray: true); _bufferArray = null;