Skip to content

StringViewArray: enable nullable reference types and optimize encoding with stackalloc/ArrayPool - #400

Open
kronic wants to merge 2 commits into
apache:mainfrom
kronic:stringviewarray-nullable-optimize-encoding
Open

StringViewArray: enable nullable reference types and optimize encoding with stackalloc/ArrayPool#400
kronic wants to merge 2 commits into
apache:mainfrom
kronic:stringviewarray-nullable-optimize-encoding

Conversation

@kronic

@kronic kronic commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

StringViewArray: Enable nullable reference types and improve encoding performance

  • Enable #nullable enable and update interfaces to use nullable string (IReadOnlyList<string?>)
  • Change DefaultEncoding to UTF8Encoding(false) to explicitly disable BOM
  • In Builder.Append, use GetMaxByteCount and stackalloc (for <=1024 bytes) or ArrayPool
    to avoid per‑string byte array allocation and reduce GC pressure
  • Add using System.Buffers for ArrayPool
  • Update Append and AppendRange to accept nullable strings and optional encoding
  • Update GetString to return nullable string and accept optional encoding
  • Use primary constructor for StringViewArray and Builder
  • Adjust overloads and parameter defaults accordingly

public class StringViewArray(ArrayData data) : BinaryViewArray(ArrowTypeId.StringView, data), IReadOnlyList<string?>
{
public static readonly Encoding DefaultEncoding = Encoding.UTF8;
public static Encoding DefaultEncoding { get; } = new UTF8Encoding(false);

@adamreeve adamreeve Aug 5, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a little concerned that this could be a breaking behaviour change, but I checked that decoding is forgiving of whether or not a BOM is present, regardless of the encoderShouldEmitUTF8Identifier parameter. So I think this is OK.

I notice that we disable testing the StringView type in our C Data Interface Python integration tests:

// TODO: Enable these once this the version of pyarrow referenced during testing supports them
HashSet<ArrowTypeId> unsupported = new HashSet<ArrowTypeId> { ArrowTypeId.ListView, ArrowTypeId.BinaryView, ArrowTypeId.StringView, ArrowTypeId.Decimal32, ArrowTypeId.Decimal64 };

Did you find that emitting the BOM was causing a problem when integrating with another Arrow implementation?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@adamreeve Yes, BOM causes problems with DataFusion.

Comment thread src/Apache.Arrow/Arrays/StringViewArray.cs Outdated
…arger ones

- Ensure ArrayPool buffers are returned in finally blocks
- Fix variable naming and remove duplicate code
@kronic

kronic commented Aug 5, 2026

Copy link
Copy Markdown
Contributor Author

In general, I think the idea of ​​temporary buffers is redundant. You can write directly to the ArrowBuffer, which will eliminate unnecessary memory copying and temporary buffer allocation.

#if NETCOREAPP
byte[]? buffer = null;

Span<byte> span = maxByteCount <= 1024

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've been using 256 as a conservative upper bound for stackalloc in libraries; see VariantValueWriter.StackAllocThreshold.

namespace Apache.Arrow
{
public class StringViewArray : BinaryViewArray, IReadOnlyList<string>
public class StringViewArray(ArrayData data) : BinaryViewArray(ArrowTypeId.StringView, data), IReadOnlyList<string?>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a codebase is easier to read when it's consistent in its use of syntax and personally found this new form jarring. I'm curious what other people think.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also found it a bit jarring but I haven't been doing as much work in C# lately so maybe I'm just not used to this. I noticed Rider always suggests refactoring to this form.


encoding ??= DefaultEncoding;
int maxByteCount = encoding.GetMaxByteCount(value.Length);
#if NETCOREAPP

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please put preprocessor directives in flush-left like the rest of the codebase.

@CurtHagenlocher

Copy link
Copy Markdown
Contributor

In general and for a variety of reasons I think the existing builders kind of suck. I've often been skipping them and building buffers directly for better performance. For your own use cases, do you find that you typically have a known set of .NET strings that you want to convert into an Arrow array or are you also building them more incrementally?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants