This is an automated email from the ASF dual-hosted git repository.

CurtHagenlocher pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-dotnet.git


The following commit(s) were added to refs/heads/main by this push:
     new c5a82e5  feat: Implement RunEndEncodedArray.Builder (#407) (#408)
c5a82e5 is described below

commit c5a82e5ed84324b167fc320b56e8dc4e9fac52cd
Author: Abhilash S Hathwar <[email protected]>
AuthorDate: Wed Aug 19 04:06:45 2026 +0530

    feat: Implement RunEndEncodedArray.Builder (#407) (#408)
    
    ## What's Changed
    
    This PR implements `RunEndEncodedArray.Builder` to close the feature gap
    for constructing REE arrays in C#.
    
    - Designed the Builder to accept generic `TRunEndBuilder` and
    `TValueBuilder` types in the constructor (pre-configured inner
    builders).
    - Implemented implicit run-length tracking via `Append(TValue value)`,
    utilizing `EqualityComparer<TValue>.Default` to group sequential
    duplicates.
    - Added comprehensive unit tests in `RunEndEncodedArrayBuilderTests.cs`.
    
    Closes #407.
---
 .../Arrays/RunEndEncodedArray.Builder.cs           | 420 +++++++++++++++++++++
 src/Apache.Arrow/Arrays/RunEndEncodedArray.cs      |   2 +-
 .../RunEndEncodedArrayBuilderTests.cs              | 287 ++++++++++++++
 3 files changed, 708 insertions(+), 1 deletion(-)

diff --git a/src/Apache.Arrow/Arrays/RunEndEncodedArray.Builder.cs 
b/src/Apache.Arrow/Arrays/RunEndEncodedArray.Builder.cs
new file mode 100644
index 0000000..67341e4
--- /dev/null
+++ b/src/Apache.Arrow/Arrays/RunEndEncodedArray.Builder.cs
@@ -0,0 +1,420 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+using System.Collections.Generic;
+using Apache.Arrow.Memory;
+
+namespace Apache.Arrow;
+
+public partial class RunEndEncodedArray
+{
+    /// <summary>
+    /// Builder for <see cref="RunEndEncodedArray"/>.
+    /// </summary>
+    /// <typeparam name="TRunEndBuilder">The type of the run ends array 
builder.</typeparam>
+    /// <typeparam name="TValueBuilder">The type of the values array 
builder.</typeparam>
+    /// <typeparam name="TRunEndArray">The type of the run ends array (must be 
Int16Array, Int32Array, or Int64Array).</typeparam>
+    /// <typeparam name="TValueArray">The type of the values array.</typeparam>
+    /// <typeparam name="TValue">The type of values contained in the values 
array.</typeparam>
+    public class Builder<TRunEndBuilder, TValueBuilder, TRunEndArray, 
TValueArray, TValue>
+        : IArrowArrayBuilder<TValue, RunEndEncodedArray, 
Builder<TRunEndBuilder, TValueBuilder, TRunEndArray, TValueArray, TValue>>
+        where TRunEndBuilder : IArrowArrayBuilder<TRunEndArray>
+        where TValueBuilder : IArrowArrayBuilder<TValueArray>
+        where TRunEndArray : IArrowArray
+        where TValueArray : IArrowArray
+    {
+        private readonly IEqualityComparer<TValue> _comparer;
+        private int _length;
+        private TValue _lastValue;
+        private bool _lastValueIsNull;
+        private bool _hasValue;
+
+        /// <summary>
+        /// Gets the run ends builder.
+        /// </summary>
+        public TRunEndBuilder RunEndsBuilder { get; }
+
+        /// <summary>
+        /// Gets the values builder.
+        /// </summary>
+        public TValueBuilder ValuesBuilder { get; }
+
+        /// <summary>
+        /// Gets the total logical length of elements appended to this builder.
+        /// </summary>
+        public int Length => _length;
+
+        /// <summary>
+        /// Initializes a new instance of the <see 
cref="Builder{TRunEndBuilder, TValueBuilder, TRunEndArray, TValueArray, 
TValue}"/> class.
+        /// </summary>
+        /// <param name="runEndsBuilder">The builder to use for run-ends. Must 
be a builder for an Int16, Int32 or Int64 array.</param>
+        /// <param name="valuesBuilder">The builder to use for values.</param>
+        /// <param name="comparer">Optional equality comparer for value 
run-length grouping.</param>
+        public Builder(TRunEndBuilder runEndsBuilder, TValueBuilder 
valuesBuilder, IEqualityComparer<TValue> comparer = null)
+        {
+            RunEndsBuilder = runEndsBuilder ?? throw new 
ArgumentNullException(nameof(runEndsBuilder));
+            ValuesBuilder = valuesBuilder ?? throw new 
ArgumentNullException(nameof(valuesBuilder));
+            _comparer = comparer ?? EqualityComparer<TValue>.Default;
+        }
+
+        /// <summary>
+        /// Appends a single value to the builder, automatically grouping 
identical consecutive values into runs.
+        /// </summary>
+        /// <param name="value">The value to append.</param>
+        /// <returns>The builder instance for method chaining.</returns>
+        public Builder<TRunEndBuilder, TValueBuilder, TRunEndArray, 
TValueArray, TValue> Append(TValue value)
+        {
+            if (value is null)
+            {
+                return AppendNull();
+            }
+
+            if (_hasValue)
+            {
+                if (!_lastValueIsNull && _comparer.Equals(value, _lastValue))
+                {
+                    checked
+                    {
+                        _length++;
+                    }
+                }
+                else
+                {
+                    FlushCurrentRun();
+                    StartNewRun(value, isNull: false);
+                }
+            }
+            else
+            {
+                StartNewRun(value, isNull: false);
+            }
+
+            return this;
+        }
+
+        /// <summary>
+        /// Appends a null value to the builder.
+        /// </summary>
+        /// <returns>The builder instance for method chaining.</returns>
+        public Builder<TRunEndBuilder, TValueBuilder, TRunEndArray, 
TValueArray, TValue> AppendNull()
+        {
+            if (_hasValue)
+            {
+                if (_lastValueIsNull)
+                {
+                    checked
+                    {
+                        _length++;
+                    }
+                }
+                else
+                {
+                    FlushCurrentRun();
+                    StartNewRun(default, isNull: true);
+                }
+            }
+            else
+            {
+                StartNewRun(default, isNull: true);
+            }
+
+            return this;
+        }
+
+        /// <summary>
+        /// Appends a span of values to the builder.
+        /// </summary>
+        /// <param name="span">The span of values to append.</param>
+        /// <returns>The builder instance for method chaining.</returns>
+        public Builder<TRunEndBuilder, TValueBuilder, TRunEndArray, 
TValueArray, TValue> Append(ReadOnlySpan<TValue> span)
+        {
+            foreach (TValue value in span)
+            {
+                Append(value);
+            }
+            return this;
+        }
+
+        /// <summary>
+        /// Appends a sequence of values to the builder.
+        /// </summary>
+        /// <param name="values">The sequence of values to append.</param>
+        /// <returns>The builder instance for method chaining.</returns>
+        public Builder<TRunEndBuilder, TValueBuilder, TRunEndArray, 
TValueArray, TValue> AppendRange(IEnumerable<TValue> values)
+        {
+            if (values == null)
+            {
+                throw new ArgumentNullException(nameof(values));
+            }
+
+            foreach (TValue value in values)
+            {
+                Append(value);
+            }
+            return this;
+        }
+
+        /// <summary>
+        /// Validates the capacity argument. Does not preallocate inner 
builders to allow doubling growth strategy with REE compression.
+        /// </summary>
+        /// <param name="capacity">The capacity to validate.</param>
+        /// <returns>The builder instance for method chaining.</returns>
+        public Builder<TRunEndBuilder, TValueBuilder, TRunEndArray, 
TValueArray, TValue> Reserve(int capacity)
+        {
+            if (capacity < 0)
+            {
+                throw new ArgumentOutOfRangeException(nameof(capacity));
+            }
+
+            return this;
+        }
+
+        /// <summary>
+        /// Resizing is not supported for RunEndEncodedArray.Builder.
+        /// </summary>
+        /// <param name="length">The target length.</param>
+        /// <returns>The builder instance for method chaining.</returns>
+        public Builder<TRunEndBuilder, TValueBuilder, TRunEndArray, 
TValueArray, TValue> Resize(int length)
+        {
+            throw new NotSupportedException("Resize is not supported on 
RunEndEncodedArray.Builder.");
+        }
+
+        /// <summary>
+        /// Clears the state of the builder and inner builders.
+        /// </summary>
+        /// <returns>The builder instance for method chaining.</returns>
+        public Builder<TRunEndBuilder, TValueBuilder, TRunEndArray, 
TValueArray, TValue> Clear()
+        {
+            _length = 0;
+            _lastValue = default;
+            _lastValueIsNull = false;
+            _hasValue = false;
+            ClearBuilder<TRunEndArray, TRunEndBuilder>(RunEndsBuilder);
+            ClearBuilder<TValueArray, TValueBuilder>(ValuesBuilder);
+            return this;
+        }
+
+        /// <summary>
+        /// Swapping elements is not supported for RunEndEncodedArray.Builder.
+        /// </summary>
+        public Builder<TRunEndBuilder, TValueBuilder, TRunEndArray, 
TValueArray, TValue> Swap(int i, int j)
+        {
+            throw new NotSupportedException("Swap is not supported on 
RunEndEncodedArray.Builder.");
+        }
+
+        /// <summary>
+        /// Setting elements at specific indices is not supported for 
RunEndEncodedArray.Builder.
+        /// </summary>
+        public Builder<TRunEndBuilder, TValueBuilder, TRunEndArray, 
TValueArray, TValue> Set(int index, TValue value)
+        {
+            throw new NotSupportedException("Set is not supported on 
RunEndEncodedArray.Builder.");
+        }
+
+        /// <summary>
+        /// Flushes any pending run and builds the <see 
cref="RunEndEncodedArray"/>.
+        /// </summary>
+        /// <param name="allocator">Optional memory allocator.</param>
+        /// <returns>The constructed <see 
cref="RunEndEncodedArray"/>.</returns>
+        public RunEndEncodedArray Build(MemoryAllocator allocator = default)
+        {
+            if (_hasValue)
+            {
+                FlushCurrentRun();
+            }
+
+            TRunEndArray runEnds = RunEndsBuilder.Build(allocator);
+            TValueArray values = ValuesBuilder.Build(allocator);
+
+            return new RunEndEncodedArray(runEnds, values);
+        }
+
+        private void StartNewRun(TValue value, bool isNull)
+        {
+            _lastValue = value;
+            _lastValueIsNull = isNull;
+            _hasValue = true;
+            checked
+            {
+                _length++;
+            }
+        }
+
+        private void FlushCurrentRun()
+        {
+            if (!_hasValue)
+            {
+                return;
+            }
+
+            AppendRunEndToRunEndsBuilder(RunEndsBuilder, _length);
+
+            if (_lastValueIsNull)
+            {
+                AppendNullToValuesBuilder(ValuesBuilder);
+            }
+            else
+            {
+                AppendValueToValuesBuilder(ValuesBuilder, _lastValue);
+            }
+
+            _hasValue = false;
+        }
+
+        private static void AppendRunEndToRunEndsBuilder(TRunEndBuilder 
runEndsBuilder, int runEnd)
+        {
+            if (runEndsBuilder is Int32Array.Builder b32)
+            {
+                b32.Append(runEnd);
+            }
+            else if (runEndsBuilder is Int16Array.Builder b16)
+            {
+                b16.Append(checked((short)runEnd));
+            }
+            else if (runEndsBuilder is Int64Array.Builder b64)
+            {
+                b64.Append(runEnd);
+            }
+            else if (runEndsBuilder is IArrowArrayBuilder<int, TRunEndArray, 
IArrowArrayBuilder<TRunEndArray>> bInt)
+            {
+                bInt.Append(runEnd);
+            }
+            else if (runEndsBuilder is IArrowArrayBuilder<short, TRunEndArray, 
IArrowArrayBuilder<TRunEndArray>> bShort)
+            {
+                bShort.Append(checked((short)runEnd));
+            }
+            else if (runEndsBuilder is IArrowArrayBuilder<long, TRunEndArray, 
IArrowArrayBuilder<TRunEndArray>> bLong)
+            {
+                bLong.Append(runEnd);
+            }
+            else
+            {
+                throw new NotSupportedException($"Run ends builder type 
'{typeof(TRunEndBuilder).Name}' is not supported.");
+            }
+        }
+
+        private static void AppendNullToValuesBuilder(TValueBuilder 
valuesBuilder)
+        {
+            if (valuesBuilder is IArrowArrayBuilder<TValueArray, 
IArrowArrayBuilder<TValueArray>> builder)
+            {
+                builder.AppendNull();
+            }
+            else if (valuesBuilder is StringArray.Builder sb)
+            {
+                sb.AppendNull();
+            }
+            else if (valuesBuilder is LargeStringArray.Builder lsb)
+            {
+                lsb.AppendNull();
+            }
+            else if (valuesBuilder is StringViewArray.Builder svb)
+            {
+                svb.AppendNull();
+            }
+            else if (valuesBuilder is BinaryArray.Builder bb)
+            {
+                bb.AppendNull();
+            }
+            else if (valuesBuilder is LargeBinaryArray.Builder lbb)
+            {
+                lbb.AppendNull();
+            }
+            else if (valuesBuilder is BinaryViewArray.Builder bvb)
+            {
+                bvb.AppendNull();
+            }
+            else
+            {
+                throw new NotSupportedException($"Appending null to values 
builder type '{typeof(TValueBuilder).Name}' is not supported.");
+            }
+        }
+
+        private static void AppendValueToValuesBuilder(TValueBuilder 
valuesBuilder, TValue value)
+        {
+            if (valuesBuilder is IArrowArrayBuilder<TValue, TValueArray, 
IArrowArrayBuilder<TValueArray>> builder)
+            {
+                builder.Append(value);
+            }
+            else if (valuesBuilder is StringArray.Builder sb && value is 
string s)
+            {
+                sb.Append(s);
+            }
+            else if (valuesBuilder is LargeStringArray.Builder lsb && value is 
string ls)
+            {
+                lsb.Append(ls);
+            }
+            else if (valuesBuilder is StringViewArray.Builder svb && value is 
string svs)
+            {
+                svb.Append(svs);
+            }
+            else if (valuesBuilder is BinaryArray.Builder bb && value is 
byte[] b)
+            {
+                bb.Append((ReadOnlySpan<byte>)b);
+            }
+            else if (valuesBuilder is LargeBinaryArray.Builder lbb && value is 
byte[] lb)
+            {
+                lbb.Append((ReadOnlySpan<byte>)lb);
+            }
+            else if (valuesBuilder is BinaryViewArray.Builder bvb && value is 
byte[] bv)
+            {
+                bvb.Append((ReadOnlySpan<byte>)bv);
+            }
+            else
+            {
+                throw new NotSupportedException($"Appending to values builder 
type '{typeof(TValueBuilder).Name}' with value type '{typeof(TValue).Name}' is 
not supported.");
+            }
+        }
+
+
+        private static void ClearBuilder<TArray, TBuilder>(TBuilder builder)
+            where TArray : IArrowArray
+            where TBuilder : IArrowArrayBuilder<TArray>
+        {
+            if (builder is IArrowArrayBuilder<TArray, 
IArrowArrayBuilder<TArray>> b)
+            {
+                b.Clear();
+            }
+            else if (builder is StringArray.Builder sb)
+            {
+                sb.Clear();
+            }
+            else if (builder is LargeStringArray.Builder lsb)
+            {
+                lsb.Clear();
+            }
+            else if (builder is StringViewArray.Builder svb)
+            {
+                svb.Clear();
+            }
+            else if (builder is BinaryArray.Builder bb)
+            {
+                bb.Clear();
+            }
+            else if (builder is LargeBinaryArray.Builder lbb)
+            {
+                lbb.Clear();
+            }
+            else if (builder is BinaryViewArray.Builder bvb)
+            {
+                bvb.Clear();
+            }
+            else
+            {
+                throw new NotSupportedException($"Clearing builder type 
'{typeof(TBuilder).Name}' is not supported.");
+            }
+        }
+    }
+}
+
diff --git a/src/Apache.Arrow/Arrays/RunEndEncodedArray.cs 
b/src/Apache.Arrow/Arrays/RunEndEncodedArray.cs
index 2cf73ab..d0a40bb 100644
--- a/src/Apache.Arrow/Arrays/RunEndEncodedArray.cs
+++ b/src/Apache.Arrow/Arrays/RunEndEncodedArray.cs
@@ -26,7 +26,7 @@ namespace Apache.Arrow;
 /// It contains two child arrays: run_ends (Int16/Int32/Int64) and values (any 
type).
 /// The run_ends array stores the cumulative end positions of each run.
 /// </summary>
-public class RunEndEncodedArray : Array, IIndexes
+public partial class RunEndEncodedArray : Array, IIndexes
 {
     /// <summary>
     /// Gets the run ends array (Int16Array, Int32Array, or Int64Array).
diff --git a/test/Apache.Arrow.Tests/RunEndEncodedArrayBuilderTests.cs 
b/test/Apache.Arrow.Tests/RunEndEncodedArrayBuilderTests.cs
new file mode 100644
index 0000000..3d1b8f5
--- /dev/null
+++ b/test/Apache.Arrow.Tests/RunEndEncodedArrayBuilderTests.cs
@@ -0,0 +1,287 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+using Xunit;
+
+namespace Apache.Arrow.Tests;
+
+public class RunEndEncodedArrayBuilderTests
+{
+    [Fact]
+    public void TestAppendGroupingStringValues()
+    {
+        var builder = new RunEndEncodedArray.Builder<Int32Array.Builder, 
StringArray.Builder, Int32Array, StringArray, string>(
+            new Int32Array.Builder(),
+            new StringArray.Builder());
+
+        builder.Append("A")
+               .Append("A")
+               .Append("A")
+               .Append("B")
+               .Append("B")
+               .Append("C");
+
+        Assert.Equal(6, builder.Length);
+
+        RunEndEncodedArray reeArray = builder.Build();
+
+        Assert.Equal(6, reeArray.Length);
+        Assert.Equal(0, reeArray.NullCount);
+
+        var runEnds = Assert.IsType<Int32Array>(reeArray.RunEnds);
+        Assert.Equal(3, runEnds.Length);
+        Assert.Equal(3, runEnds.GetValue(0));
+        Assert.Equal(5, runEnds.GetValue(1));
+        Assert.Equal(6, runEnds.GetValue(2));
+
+        var values = Assert.IsType<StringArray>(reeArray.Values);
+        Assert.Equal(3, values.Length);
+        Assert.Equal("A", values.GetString(0));
+        Assert.Equal("B", values.GetString(1));
+        Assert.Equal("C", values.GetString(2));
+    }
+
+    [Fact]
+    public void TestAppendGroupingPrimitiveValues()
+    {
+        var builder = new RunEndEncodedArray.Builder<Int32Array.Builder, 
Int32Array.Builder, Int32Array, Int32Array, int>(
+            new Int32Array.Builder(),
+            new Int32Array.Builder());
+
+        builder.AppendRange(new[] { 10, 10, 20, 20, 20, 30 });
+
+        Assert.Equal(6, builder.Length);
+
+        RunEndEncodedArray reeArray = builder.Build();
+
+        Assert.Equal(6, reeArray.Length);
+
+        var runEnds = Assert.IsType<Int32Array>(reeArray.RunEnds);
+        Assert.Equal(3, runEnds.Length);
+        Assert.Equal(2, runEnds.GetValue(0));
+        Assert.Equal(5, runEnds.GetValue(1));
+        Assert.Equal(6, runEnds.GetValue(2));
+
+        var values = Assert.IsType<Int32Array>(reeArray.Values);
+        Assert.Equal(3, values.Length);
+        Assert.Equal(10, values.GetValue(0));
+        Assert.Equal(20, values.GetValue(1));
+        Assert.Equal(30, values.GetValue(2));
+    }
+
+    [Fact]
+    public void TestAppendGroupingInt16RunEnds()
+    {
+        var builder = new RunEndEncodedArray.Builder<Int16Array.Builder, 
DoubleArray.Builder, Int16Array, DoubleArray, double>(
+            new Int16Array.Builder(),
+            new DoubleArray.Builder());
+
+        builder.Append(1.1)
+               .Append(1.1)
+               .Append(2.2);
+
+        RunEndEncodedArray reeArray = builder.Build();
+
+        Assert.Equal(3, reeArray.Length);
+
+        var runEnds = Assert.IsType<Int16Array>(reeArray.RunEnds);
+        Assert.Equal(2, runEnds.Length);
+        Assert.Equal((short)2, runEnds.GetValue(0));
+        Assert.Equal((short)3, runEnds.GetValue(1));
+
+        var values = Assert.IsType<DoubleArray>(reeArray.Values);
+        Assert.Equal(2, values.Length);
+        Assert.Equal(1.1, values.GetValue(0));
+        Assert.Equal(2.2, values.GetValue(1));
+    }
+
+    [Fact]
+    public void TestAppendGroupingInt64RunEnds()
+    {
+        var builder = new RunEndEncodedArray.Builder<Int64Array.Builder, 
StringArray.Builder, Int64Array, StringArray, string>(
+            new Int64Array.Builder(),
+            new StringArray.Builder());
+
+        builder.AppendRange(new[] { "X", "X", "Y" });
+
+        RunEndEncodedArray reeArray = builder.Build();
+
+        Assert.Equal(3, reeArray.Length);
+
+        var runEnds = Assert.IsType<Int64Array>(reeArray.RunEnds);
+        Assert.Equal(2, runEnds.Length);
+        Assert.Equal(2L, runEnds.GetValue(0));
+        Assert.Equal(3L, runEnds.GetValue(1));
+
+        var values = Assert.IsType<StringArray>(reeArray.Values);
+        Assert.Equal(2, values.Length);
+        Assert.Equal("X", values.GetString(0));
+        Assert.Equal("Y", values.GetString(1));
+    }
+
+    [Fact]
+    public void TestAppendNullHandling()
+    {
+        var builder = new RunEndEncodedArray.Builder<Int32Array.Builder, 
StringArray.Builder, Int32Array, StringArray, string>(
+            new Int32Array.Builder(),
+            new StringArray.Builder());
+
+        builder.Append("A")
+               .Append("A")
+               .AppendNull()
+               .Append((string)null)
+               .Append("B");
+
+        Assert.Equal(5, builder.Length);
+
+        RunEndEncodedArray reeArray = builder.Build();
+
+        Assert.Equal(5, reeArray.Length);
+
+        var runEnds = Assert.IsType<Int32Array>(reeArray.RunEnds);
+        Assert.Equal(3, runEnds.Length);
+        Assert.Equal(2, runEnds.GetValue(0));
+        Assert.Equal(4, runEnds.GetValue(1));
+        Assert.Equal(5, runEnds.GetValue(2));
+
+        var values = Assert.IsType<StringArray>(reeArray.Values);
+        Assert.Equal(3, values.Length);
+        Assert.Equal("A", values.GetString(0));
+        Assert.True(values.IsNull(1));
+        Assert.Equal("B", values.GetString(2));
+    }
+
+    [Fact]
+    public void TestEmptyArrayBuilding()
+    {
+        var builder = new RunEndEncodedArray.Builder<Int32Array.Builder, 
StringArray.Builder, Int32Array, StringArray, string>(
+            new Int32Array.Builder(),
+            new StringArray.Builder());
+
+        Assert.Equal(0, builder.Length);
+
+        RunEndEncodedArray reeArray = builder.Build();
+
+        Assert.Equal(0, reeArray.Length);
+        Assert.Equal(0, reeArray.NullCount);
+        Assert.Equal(0, reeArray.RunEnds.Length);
+        Assert.Equal(0, reeArray.Values.Length);
+    }
+
+    [Fact]
+    public void TestAppendSpanAndRange()
+    {
+        var builder = new RunEndEncodedArray.Builder<Int32Array.Builder, 
Int32Array.Builder, Int32Array, Int32Array, int>(
+            new Int32Array.Builder(),
+            new Int32Array.Builder());
+
+        ReadOnlySpan<int> span = new[] { 5, 5, 5 };
+        builder.Append(span);
+        builder.AppendRange(new[] { 5, 10, 10 });
+
+        RunEndEncodedArray reeArray = builder.Build();
+
+        Assert.Equal(6, reeArray.Length);
+
+        var runEnds = Assert.IsType<Int32Array>(reeArray.RunEnds);
+        Assert.Equal(2, runEnds.Length);
+        Assert.Equal(4, runEnds.GetValue(0));
+        Assert.Equal(6, runEnds.GetValue(1));
+
+        var values = Assert.IsType<Int32Array>(reeArray.Values);
+        Assert.Equal(2, values.Length);
+        Assert.Equal(5, values.GetValue(0));
+        Assert.Equal(10, values.GetValue(1));
+    }
+
+    [Fact]
+    public void TestClear()
+    {
+        var builder = new RunEndEncodedArray.Builder<Int32Array.Builder, 
StringArray.Builder, Int32Array, StringArray, string>(
+            new Int32Array.Builder(),
+            new StringArray.Builder());
+
+        builder.Append("A").Append("A");
+        builder.Clear();
+
+        Assert.Equal(0, builder.Length);
+
+        builder.Append("B").Append("B").Append("B");
+
+        RunEndEncodedArray reeArray = builder.Build();
+
+        Assert.Equal(3, reeArray.Length);
+
+        var runEnds = Assert.IsType<Int32Array>(reeArray.RunEnds);
+        Assert.Equal(1, runEnds.Length);
+        Assert.Equal(3, runEnds.GetValue(0));
+
+        var values = Assert.IsType<StringArray>(reeArray.Values);
+        Assert.Equal(1, values.Length);
+        Assert.Equal("B", values.GetString(0));
+    }
+
+    [Fact]
+    public void TestCustomComparer()
+    {
+        var builder = new RunEndEncodedArray.Builder<Int32Array.Builder, 
StringArray.Builder, Int32Array, StringArray, string>(
+            new Int32Array.Builder(),
+            new StringArray.Builder(),
+            StringComparer.OrdinalIgnoreCase);
+
+        builder.Append("abc")
+               .Append("ABC")
+               .Append("aBc")
+               .Append("def");
+
+        RunEndEncodedArray reeArray = builder.Build();
+
+        Assert.Equal(4, reeArray.Length);
+
+        var runEnds = Assert.IsType<Int32Array>(reeArray.RunEnds);
+        Assert.Equal(2, runEnds.Length);
+        Assert.Equal(3, runEnds.GetValue(0));
+        Assert.Equal(4, runEnds.GetValue(1));
+
+        var values = Assert.IsType<StringArray>(reeArray.Values);
+        Assert.Equal(2, values.Length);
+        Assert.Equal("abc", values.GetString(0));
+        Assert.Equal("def", values.GetString(1));
+    }
+
+    [Fact]
+    public void TestReserve_ValidAndNegativeCapacity()
+    {
+        var builder = new RunEndEncodedArray.Builder<Int32Array.Builder, 
StringArray.Builder, Int32Array, StringArray, string>(
+            new Int32Array.Builder(),
+            new StringArray.Builder());
+
+        builder.Reserve(100);
+
+        Assert.Throws<ArgumentOutOfRangeException>(() => builder.Reserve(-1));
+    }
+
+    [Fact]
+    public void TestResize_ThrowsNotSupportedException()
+    {
+        var builder = new RunEndEncodedArray.Builder<Int32Array.Builder, 
StringArray.Builder, Int32Array, StringArray, string>(
+            new Int32Array.Builder(),
+            new StringArray.Builder());
+
+        Assert.Throws<NotSupportedException>(() => builder.Resize(10));
+    }
+}
+

Reply via email to