Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 28 additions & 11 deletions ruby/red-arrow-format/lib/arrow-format/array.rb
Original file line number Diff line number Diff line change
Expand Up @@ -309,40 +309,57 @@ def to_a
end

class DecimalArray < FixedSizeBinaryArray
end

class Decimal128Array < DecimalArray
def to_a
byte_width = @type.byte_width
buffer_types = [:u64, :s64] # TODO: big endian support
buffer_types = [:u64] * (byte_width / 8 - 1) + [:s64]
values = 0.step(@size * byte_width - 1, byte_width).collect do |offset|
@values_buffer.get_values(buffer_types, offset)
end
apply_validity(values).collect do |value|
if value.nil?
nil
else
low, high = value
BigDecimal(format_value(low, high))
BigDecimal(format_value(value))
end
end
end

private
def format_value(low, high)
def format_value(components)
highest = components.last
width = @type.precision
width += 1 if high < 0
value = (high << 64) + low
string = value.to_s.ljust(width, "0")
width += 1 if highest < 0
value = 0
components.reverse_each do |component|
value = (value << 64) + component
end
string = value.to_s
if @type.scale < 0
string << ("0" * [email protected])
Copy link
Collaborator

@hiroyuki-sato hiroyuki-sato Jan 2, 2026

Choose a reason for hiding this comment

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

(This part is the out of scope in this PR). It seems storange.

"0" * -4
ArgumentError: negative argument (ArgumentError)
from (pry):3:in 'String#*'

scale: -3 and value 123 formats to 123000?

string << ("0" * @type.scale)

?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes. 123 + scale: -3 is 123000.

BTW, why did you try "0" * -4? In this context, @type.scale is negative. So "0" * --4 should be tried for the case.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Thank you for your comment. It was misunderstanding.

elsif @type.scale > 0
string[[email protected], 0] = "."
n_digits = string.bytesize
n_digits -= 1 if value < 0
if n_digits < @type.scale
prefix = "0." + ("0" * (@type.scale - n_digits - 1))
if value < 0
string[1, 0] = prefix
else
string[0, 0] = prefix
end
else
string[[email protected], 0] = "."
end
end
string
end
end

class Decimal128Array < DecimalArray
end

class Decimal256Array < DecimalArray
end

class VariableSizeListArray < Array
def initialize(type, size, validity_buffer, offsets_buffer, child)
super(type, size, validity_buffer)
Expand Down
2 changes: 2 additions & 0 deletions ruby/red-arrow-format/lib/arrow-format/readable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,8 @@ def read_field(fb_field)
case fb_type.bit_width
when 128
type = Decimal128Type.new(fb_type.precision, fb_type.scale)
when 256
type = Decimal256Type.new(fb_type.precision, fb_type.scale)
end
end
Field.new(fb_field.name, type, fb_field.nullable?)
Expand Down
14 changes: 14 additions & 0 deletions ruby/red-arrow-format/lib/arrow-format/type.rb
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,20 @@ def build_array(size, validity_buffer, values_buffer)
end
end

class Decimal256Type < DecimalType
def initialize(precision, scale)
super(32, precision, scale)
end

def name
"Decimal256"
end

def build_array(size, validity_buffer, values_buffer)
Decimal256Array.new(self, size, validity_buffer, values_buffer)
end
end

class VariableSizeListType < Type
attr_reader :child
def initialize(child)
Expand Down
52 changes: 47 additions & 5 deletions ruby/red-arrow-format/test/test-reader.rb
Original file line number Diff line number Diff line change
Expand Up @@ -676,19 +676,61 @@ def test_read

sub_test_case("Decimal128") do
def build_array
@positive = "12345678901234567890123456789012345.678"
@negative = "-12345678901234567890123456789012345.67"
@positive_small = "1.200"
@positive_large = ("1234567890" * 3) + "12345.678"
@negative_small = "-1.200"
@negative_large = "-" + ("1234567890" * 3) + "12345.678"
Arrow::Decimal128Array.new({precision: 38, scale: 3},
[@positive, nil, @negative])
[
@positive_large,
@positive_small,
nil,
@negative_small,
@negative_large,
])
end

def test_read
assert_equal([
{
"value" => [
BigDecimal(@positive),
BigDecimal(@positive_large),
BigDecimal(@positive_small),
nil,
BigDecimal(@negative),
BigDecimal(@negative_small),
BigDecimal(@negative_large),
],
},
],
read)
end
end

sub_test_case("Decimal256") do
def build_array
@positive_small = "1.200"
@positive_large = ("1234567890" * 7) + "123.456"
@negative_small = "-1.200"
@negative_large = "-" + ("1234567890" * 7) + "123.456"
Arrow::Decimal256Array.new({precision: 76, scale: 3},
[
@positive_large,
@positive_small,
nil,
@negative_small,
@negative_large,
])
end

def test_read
assert_equal([
{
"value" => [
BigDecimal(@positive_large),
BigDecimal(@positive_small),
nil,
BigDecimal(@negative_small),
BigDecimal(@negative_large),
],
},
],
Expand Down
Loading