diff --git a/src/Apache.Arrow/Arrays/StringViewArray.cs b/src/Apache.Arrow/Arrays/StringViewArray.cs index 5411d6b6..9a9d67ff 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,63 @@ 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); + + try + { + int encodeBytes = encoding.GetBytes(value, span); + return Append(span.Slice(0, encodeBytes)); + } + finally + { + 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) + public Builder AppendRange(IEnumerable values, Encoding? encoding = null) { - foreach (string value in values) + foreach (string? value in values) { Append(value, encoding); } @@ -57,31 +90,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 +136,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++) {