Copilot commented on code in PR #408:
URL: https://github.com/apache/arrow-dotnet/pull/408#discussion_r3772148151


##########
src/Apache.Arrow/Arrays/RunEndEncodedArray.Builder.cs:
##########
@@ -0,0 +1,400 @@
+// 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 _currentRunLength;
+        private TValue _lastValue;
+        private bool _lastValueIsNull;
+        private bool _hasValue;

Review Comment:
   The private field _currentRunLength actually tracks the total logical length 
(cumulative run end), not the length of the current run. This is easy to 
misread/maintain given the name and could lead to subtle bugs in future edits; 
consider renaming it to something like _length or _currentLogicalLength to 
reflect its meaning.



##########
src/Apache.Arrow/Arrays/RunEndEncodedArray.Builder.cs:
##########
@@ -0,0 +1,400 @@
+// 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 _currentRunLength;
+        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 => _currentRunLength;
+
+        /// <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.</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))
+                {
+                    _currentRunLength++;
+                }
+                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)
+                {
+                    _currentRunLength++;
+                }
+                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>
+        /// Reserves capacity in the inner builders.
+        /// </summary>
+        /// <param name="capacity">The capacity to reserve.</param>
+        /// <returns>The builder instance for method chaining.</returns>
+        public Builder<TRunEndBuilder, TValueBuilder, TRunEndArray, 
TValueArray, TValue> Reserve(int capacity)
+        {
+            return this;
+        }

Review Comment:
   Reserve currently returns without reserving any capacity (and without 
validating negative inputs), which violates the IArrowArrayBuilder contract and 
can break callers that rely on Reserve for preallocation/argument checking. 
Consider delegating Reserve to the inner builders (worst-case: one run per 
element) and throwing on negative capacity.



##########
src/Apache.Arrow/Arrays/RunEndEncodedArray.Builder.cs:
##########
@@ -0,0 +1,400 @@
+// 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 _currentRunLength;
+        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 => _currentRunLength;
+
+        /// <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.</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))
+                {
+                    _currentRunLength++;
+                }
+                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)
+                {
+                    _currentRunLength++;
+                }
+                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>
+        /// Reserves capacity in the inner builders.
+        /// </summary>
+        /// <param name="capacity">The capacity to reserve.</param>
+        /// <returns>The builder instance for method chaining.</returns>
+        public Builder<TRunEndBuilder, TValueBuilder, TRunEndArray, 
TValueArray, TValue> Reserve(int capacity)
+        {
+            return this;
+        }
+
+        /// <summary>
+        /// Resizes the 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)
+        {
+            return this;
+        }
+
+        /// <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()
+        {
+            _currentRunLength = 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;
+            _currentRunLength++;
+        }
+
+        private void FlushCurrentRun()
+        {
+            if (!_hasValue)
+            {
+                return;
+            }
+
+            AppendRunEndToRunEndsBuilder(RunEndsBuilder, _currentRunLength);
+
+            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();
+            }
+        }

Review Comment:
   ClearBuilder silently does nothing if the provided inner builder type 
doesn't match any of the known cases. That can leave 
RunEndsBuilder/ValuesBuilder with stale data while the outer builder resets its 
counters, producing corrupted arrays after Clear(). Consider throwing in the 
fallback case so unsupported builders fail fast instead of partially clearing 
state.



##########
src/Apache.Arrow/Arrays/RunEndEncodedArray.Builder.cs:
##########
@@ -0,0 +1,400 @@
+// 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 _currentRunLength;
+        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 => _currentRunLength;
+
+        /// <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.</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))
+                {
+                    _currentRunLength++;
+                }
+                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)
+                {
+                    _currentRunLength++;
+                }
+                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>
+        /// Reserves capacity in the inner builders.
+        /// </summary>
+        /// <param name="capacity">The capacity to reserve.</param>
+        /// <returns>The builder instance for method chaining.</returns>
+        public Builder<TRunEndBuilder, TValueBuilder, TRunEndArray, 
TValueArray, TValue> Reserve(int capacity)
+        {
+            return this;
+        }
+
+        /// <summary>
+        /// Resizes the 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)
+        {
+            return this;
+        }

Review Comment:
   Resize currently no-ops, leaving the builder in an unchanged state. Since 
RunEndEncodedArray.Builder also does not support Set/Swap, a silent Resize is 
particularly misleading for callers and violates IArrowArrayBuilder 
expectations; it should either be implemented or explicitly throw 
NotSupportedException.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to