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

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


The following commit(s) were added to refs/heads/main by this push:
     new 5d286d47dc4 GH-50832: [Ruby] Add 
`ArrowFormat::FixedSizeBinaryArray.new(byte_width, values)` (#50854)
5d286d47dc4 is described below

commit 5d286d47dc45e934879d7432fe2b390b98f99427
Author: takuya kodama <[email protected]>
AuthorDate: Mon Aug 24 16:47:20 2026 +0800

    GH-50832: [Ruby] Add `ArrowFormat::FixedSizeBinaryArray.new(byte_width, 
values)` (#50854)
    
    ### Rationale for this change
    
    Building a fixed size binary Arrow array from Ruby objects is convenient.
    
    ### What changes are included in this PR?
    
    Accept:
    
    * `ArrowFormat::FixedSizeBinaryArray.new(byte_width, values)`
    * `ArrowFormat::FixedSizeBinaryArray.new(type, values)`
    
    `ArrowFormat::FixedSizeBinaryType.try_convert` is added for the byte width 
shorthand, in the same way as `ArrowFormat::Time32Array.new(unit, values)`. The 
existing 4 arguments form is kept.
    
    Decimal arrays are out of scope. `ArrowFormat::DecimalArray#to_a` returns 
`BigDecimal`, so building one from Ruby objects needs a conversion between 
`BigDecimal` and two's complement. I would like to open a separate issue for it.
    
    ### Are these changes tested?
    
    Yes.
    
    ### Are there any user-facing changes?
    
    Yes.
    
    * GitHub Issue: #50832
    
    Authored-by: otegami <[email protected]>
    Signed-off-by: Sutou Kouhei <[email protected]>
---
 ruby/red-arrow-format/lib/arrow-format/array.rb    | 54 ++++++++++++++-
 ruby/red-arrow-format/lib/arrow-format/type.rb     | 14 ++++
 .../test/test-fixed-size-binary-array.rb           | 79 ++++++++++++++++++++++
 3 files changed, 146 insertions(+), 1 deletion(-)

diff --git a/ruby/red-arrow-format/lib/arrow-format/array.rb 
b/ruby/red-arrow-format/lib/arrow-format/array.rb
index d190cc29923..20929493e90 100644
--- a/ruby/red-arrow-format/lib/arrow-format/array.rb
+++ b/ruby/red-arrow-format/lib/arrow-format/array.rb
@@ -852,7 +852,21 @@ module ArrowFormat
   end
 
   class FixedSizeBinaryArray < Array
-    def initialize(type, size, validity_buffer, values_buffer)
+    include BufferAlignable
+
+    def initialize(type, *args)
+      unless type.is_a?(Type)
+        type = FixedSizeBinaryType.try_convert(type) || type
+      end
+      if args.size == 1
+        args = build_data(args.first, type)
+      elsif args.size != 3
+        raise ArgumentError,
+              "wrong number of arguments (given #{args.size + 1}, expected 2 
or 4)"
+      end
+
+      size, validity_buffer, values_buffer = args
+
       super(type, size, validity_buffer)
       @values_buffer = values_buffer
     end
@@ -875,6 +889,44 @@ module ArrowFormat
       end
       apply_validity(values)
     end
+
+    private
+    def build_data(data, type)
+      n = 0
+      validity_buffer_builder = nil
+
+      values = +"".b
+      byte_width = type.byte_width
+      null_value = "\x00" * byte_width
+
+      data.each_with_index do |value, i|
+        if value.nil?
+          validity_buffer_builder ||= SparseBitmapBuilder.new
+          validity_buffer_builder.unset(i)
+          values.append_as_bytes(null_value)
+        else
+          unless value.bytesize == byte_width
+            message = "value size must be #{byte_width}: #{value.inspect}"
+            raise ArgumentError, message
+          end
+          values.append_as_bytes(value)
+        end
+
+        n += 1
+      end
+
+      validity_buffer = validity_buffer_builder&.finish(n)
+
+      pad!(values, buffer_padding_size(values))
+      values.freeze
+      values_buffer = IO::Buffer.for(values)
+
+      [
+        n,
+        validity_buffer,
+        values_buffer,
+      ]
+    end
   end
 
   class DecimalArray < FixedSizeBinaryArray
diff --git a/ruby/red-arrow-format/lib/arrow-format/type.rb 
b/ruby/red-arrow-format/lib/arrow-format/type.rb
index b52136769d4..9a6d1f3ed06 100644
--- a/ruby/red-arrow-format/lib/arrow-format/type.rb
+++ b/ruby/red-arrow-format/lib/arrow-format/type.rb
@@ -911,6 +911,20 @@ module ArrowFormat
   end
 
   class FixedSizeBinaryType < Type
+    class << self
+      def try_convert(value)
+        case value
+        when Integer
+          byte_width = value
+          new(byte_width)
+        when self
+          value
+        else
+          nil
+        end
+      end
+    end
+
     attr_reader :byte_width
     def initialize(byte_width)
       super()
diff --git a/ruby/red-arrow-format/test/test-fixed-size-binary-array.rb 
b/ruby/red-arrow-format/test/test-fixed-size-binary-array.rb
new file mode 100644
index 00000000000..bee0ae1f8ee
--- /dev/null
+++ b/ruby/red-arrow-format/test/test-fixed-size-binary-array.rb
@@ -0,0 +1,79 @@
+# 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.
+
+class TestFixedSizeBinaryArray < Test::Unit::TestCase
+  sub_test_case("#initialize") do
+    def test_no_null
+      values = ["0124".b, "abcd".b]
+      assert_equal(values,
+                   ArrowFormat::FixedSizeBinaryArray.new(4, values).to_a)
+    end
+
+    def test_mixed
+      values = ["0124".b, nil, "abcd".b]
+      assert_equal(values,
+                   ArrowFormat::FixedSizeBinaryArray.new(4, values).to_a)
+    end
+
+    def test_type
+      type = ArrowFormat::FixedSizeBinaryType.new(4)
+      values = ["0124".b, nil, "abcd".b]
+      assert_equal(values,
+                   ArrowFormat::FixedSizeBinaryArray.new(type, values).to_a)
+    end
+
+    def test_too_small_value_size
+      error = ArgumentError.new("value size must be 4: \"012\"")
+      assert_raise(error) do
+        ArrowFormat::FixedSizeBinaryArray.new(4, ["012".b])
+      end
+    end
+
+    def test_too_large_value_size
+      error = ArgumentError.new("value size must be 4: \"01245\"")
+      assert_raise(error) do
+        ArrowFormat::FixedSizeBinaryArray.new(4, ["01245".b])
+      end
+    end
+  end
+
+  sub_test_case("#==") do
+    def test_no_slice
+      values = ["0124".b, nil, "abcd".b]
+      array1 = ArrowFormat::FixedSizeBinaryArray.new(4, values)
+      array2 = ArrowFormat::FixedSizeBinaryArray.new(4, values)
+      assert_equal(array1, array2)
+    end
+
+    def test_sliced
+      pad = "0000".b
+      values = ["0124".b, nil, "abcd".b]
+      array1 = ArrowFormat::FixedSizeBinaryArray.new(4, values)
+      array2 = ArrowFormat::FixedSizeBinaryArray.new(4, [pad, *values, pad])
+      assert_equal(array1, array2.slice(1, 3))
+    end
+
+    def test_sliced_different_content
+      pad = "0000".b
+      values = ["0124".b, nil, "abcd".b]
+      array1 = ArrowFormat::FixedSizeBinaryArray.new(4, values)
+      array2 = ArrowFormat::FixedSizeBinaryArray.new(4,
+                                                     [pad, pad, *values, pad])
+      assert_not_equal(array1, array2.slice(1, 3))
+    end
+  end
+end

Reply via email to