From e794898262ad88560b9dc8481363cf4172a24bb3 Mon Sep 17 00:00:00 2001 From: kronic Date: Tue, 4 Aug 2026 10:22:20 +0300 Subject: [PATCH 1/2] update StringViewArray --- src/Apache.Arrow/Arrays/StringViewArray.cs | 88 +++++++++++++++------- 1 file changed, 62 insertions(+), 26 deletions(-) diff --git a/src/Apache.Arrow/Arrays/StringViewArray.cs b/src/Apache.Arrow/Arrays/StringViewArray.cs index 5411d6b6..7f751ea9 100644 --- a/src/Apache.Arrow/Arrays/StringViewArray.cs +++ b/src/Apache.Arrow/Arrays/StringViewArray.cs @@ -13,7 +13,10 @@ // See the License for the specific language governing permissions and // limitations under the License. +#nullable enable + using System; +using System.Buffers; using System.Collections; using System.Collections.Generic; using System.Runtime.InteropServices; @@ -22,33 +25,56 @@ namespace Apache.Arrow { - public class StringViewArray : BinaryViewArray, IReadOnlyList + public class StringViewArray(ArrayData data) : BinaryViewArray(ArrowTypeId.StringView, data), IReadOnlyList { - public static readonly Encoding DefaultEncoding = Encoding.UTF8; + public static Encoding DefaultEncoding { get; } = new UTF8Encoding(false); - public new class Builder : BuilderBase + public new class Builder() : BuilderBase(StringViewType.Default) { - public Builder() : base(StringViewType.Default) { } - protected override StringViewArray Build(ArrayData data) { return new StringViewArray(data); } - public Builder Append(string value, Encoding encoding = null) + public Builder Append(string? value, Encoding? encoding = null) { - if (value == null) + if (value is null) { return AppendNull(); } - encoding = encoding ?? DefaultEncoding; - byte[] span = encoding.GetBytes(value); - return Append(span.AsSpan()); + + encoding ??= DefaultEncoding; + int maxByteCount = encoding.GetMaxByteCount(value.Length); + #if NETCOREAPP + byte[]? buffer = null; + + Span span = maxByteCount <= 1024 + ? stackalloc byte[maxByteCount] + : buffer = ArrayPool.Shared.Rent(maxByteCount); + + int encodeBbytes = encoding.GetBytes(value, span); + span = span.Slice(0, encodeBbytes); + #else + byte[] buffer = ArrayPool.Shared.Rent(maxByteCount); + int encodeBbytes = encoding.GetBytes(value, 0, value.Length, buffer, 0); + Span span = buffer.AsSpan(0, encodeBbytes); + #endif + try + { + return Append(span); + } + finally + { + if (buffer != null) + { + ArrayPool.Shared.Return(buffer); + } + } } - public Builder AppendRange(IEnumerable values, Encoding encoding = null) + public Builder AppendRange(IEnumerable values, Encoding? encoding = null) { - foreach (string value in values) + foreach (string? value in values) { Append(value, encoding); } @@ -57,31 +83,41 @@ public Builder AppendRange(IEnumerable values, Encoding encoding = null) } } - public StringViewArray(ArrayData data) - : base(ArrowTypeId.StringView, data) { } - - public StringViewArray(int length, + public StringViewArray + ( + int length, ArrowBuffer valueOffsetsBuffer, ArrowBuffer dataBuffer, ArrowBuffer nullBitmapBuffer, - int nullCount = 0, int offset = 0) - : this(new ArrayData(StringViewType.Default, length, nullCount, offset, - new[] { nullBitmapBuffer, valueOffsetsBuffer, dataBuffer })) - { } + int nullCount = 0, + int offset = 0 + ) : this + ( + new ArrayData + ( + StringViewType.Default, + length, + nullCount, + offset, + [nullBitmapBuffer, valueOffsetsBuffer, dataBuffer] + ) + ) + { + } public override void Accept(IArrowArrayVisitor visitor) => Accept(this, visitor); - public string GetString(int index, Encoding encoding = default) + public string? GetString(int index, Encoding? encoding = null) { encoding ??= DefaultEncoding; - ReadOnlySpan bytes = GetBytes(index, out bool isNull); if (isNull) { return null; } - if (bytes.Length == 0) + + if (bytes.Length is 0) { return string.Empty; } @@ -93,11 +129,11 @@ public string GetString(int index, Encoding encoding = default) } } - int IReadOnlyCollection.Count => Length; + int IReadOnlyCollection.Count => Length; - string IReadOnlyList.this[int index] => GetString(index); + string? IReadOnlyList.this[int index] => GetString(index); - IEnumerator IEnumerable.GetEnumerator() + IEnumerator IEnumerable.GetEnumerator() { for (int index = 0; index < Length; index++) { From a4286b00a1d76c0fafebb1e57e722b98ae2229d7 Mon Sep 17 00:00:00 2001 From: kronic Date: Wed, 5 Aug 2026 08:27:37 +0300 Subject: [PATCH 2/2] - Use stackalloc for small strings (<=1024 bytes) and ArrayPool for larger ones - Ensure ArrayPool buffers are returned in finally blocks - Fix variable naming and remove duplicate code --- src/Apache.Arrow/Arrays/StringViewArray.cs | 25 ++++++++++++++-------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/Apache.Arrow/Arrays/StringViewArray.cs b/src/Apache.Arrow/Arrays/StringViewArray.cs index 7f751ea9..9a9d67ff 100644 --- a/src/Apache.Arrow/Arrays/StringViewArray.cs +++ b/src/Apache.Arrow/Arrays/StringViewArray.cs @@ -52,24 +52,31 @@ public Builder Append(string? value, Encoding? encoding = null) ? stackalloc byte[maxByteCount] : buffer = ArrayPool.Shared.Rent(maxByteCount); - int encodeBbytes = encoding.GetBytes(value, span); - span = span.Slice(0, encodeBbytes); - #else - byte[] buffer = ArrayPool.Shared.Rent(maxByteCount); - int encodeBbytes = encoding.GetBytes(value, 0, value.Length, buffer, 0); - Span span = buffer.AsSpan(0, encodeBbytes); - #endif try { - return Append(span); + int encodeBytes = encoding.GetBytes(value, span); + return Append(span.Slice(0, encodeBytes)); } finally { - if (buffer != null) + if (buffer is not null) { ArrayPool.Shared.Return(buffer); } } + #else + byte[] buffer = ArrayPool.Shared.Rent(maxByteCount); + + try + { + int encodeBytes = encoding.GetBytes(value, 0, value.Length, buffer, 0); + return Append(buffer.AsSpan(0, encodeBytes)); + } + finally + { + ArrayPool.Shared.Return(buffer); + } + #endif } public Builder AppendRange(IEnumerable values, Encoding? encoding = null)