Skip to content

Commit 29586f4

Browse files
authored
GH-48703: [Ruby] Add support for reading decimal256 array (#48704)
### Rationale for this change It's a 256 bit variant of decimal array. ### What changes are included in this PR? * Add `ArrowFormat::Decimal256Type` * Add `ArrowFormat::Decimal256Array` ### Are these changes tested? Yes. ### Are there any user-facing changes? Yes. * GitHub Issue: #48703 Authored-by: Sutou Kouhei <[email protected]> Signed-off-by: Sutou Kouhei <[email protected]>
1 parent 8fc54a3 commit 29586f4

File tree

4 files changed

+91
-16
lines changed

4 files changed

+91
-16
lines changed

ruby/red-arrow-format/lib/arrow-format/array.rb

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -309,40 +309,57 @@ def to_a
309309
end
310310

311311
class DecimalArray < FixedSizeBinaryArray
312-
end
313-
314-
class Decimal128Array < DecimalArray
315312
def to_a
316313
byte_width = @type.byte_width
317-
buffer_types = [:u64, :s64] # TODO: big endian support
314+
buffer_types = [:u64] * (byte_width / 8 - 1) + [:s64]
318315
values = 0.step(@size * byte_width - 1, byte_width).collect do |offset|
319316
@values_buffer.get_values(buffer_types, offset)
320317
end
321318
apply_validity(values).collect do |value|
322319
if value.nil?
323320
nil
324321
else
325-
low, high = value
326-
BigDecimal(format_value(low, high))
322+
BigDecimal(format_value(value))
327323
end
328324
end
329325
end
330326

331327
private
332-
def format_value(low, high)
328+
def format_value(components)
329+
highest = components.last
333330
width = @type.precision
334-
width += 1 if high < 0
335-
value = (high << 64) + low
336-
string = value.to_s.ljust(width, "0")
331+
width += 1 if highest < 0
332+
value = 0
333+
components.reverse_each do |component|
334+
value = (value << 64) + component
335+
end
336+
string = value.to_s
337337
if @type.scale < 0
338338
string << ("0" * -@type.scale)
339339
elsif @type.scale > 0
340-
string[-@type.scale, 0] = "."
340+
n_digits = string.bytesize
341+
n_digits -= 1 if value < 0
342+
if n_digits < @type.scale
343+
prefix = "0." + ("0" * (@type.scale - n_digits - 1))
344+
if value < 0
345+
string[1, 0] = prefix
346+
else
347+
string[0, 0] = prefix
348+
end
349+
else
350+
string[-@type.scale, 0] = "."
351+
end
341352
end
342353
string
343354
end
344355
end
345356

357+
class Decimal128Array < DecimalArray
358+
end
359+
360+
class Decimal256Array < DecimalArray
361+
end
362+
346363
class VariableSizeListArray < Array
347364
def initialize(type, size, validity_buffer, offsets_buffer, child)
348365
super(type, size, validity_buffer)

ruby/red-arrow-format/lib/arrow-format/readable.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,8 @@ def read_field(fb_field)
171171
case fb_type.bit_width
172172
when 128
173173
type = Decimal128Type.new(fb_type.precision, fb_type.scale)
174+
when 256
175+
type = Decimal256Type.new(fb_type.precision, fb_type.scale)
174176
end
175177
end
176178
Field.new(fb_field.name, type, fb_field.nullable?)

ruby/red-arrow-format/lib/arrow-format/type.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -522,6 +522,20 @@ def build_array(size, validity_buffer, values_buffer)
522522
end
523523
end
524524

525+
class Decimal256Type < DecimalType
526+
def initialize(precision, scale)
527+
super(32, precision, scale)
528+
end
529+
530+
def name
531+
"Decimal256"
532+
end
533+
534+
def build_array(size, validity_buffer, values_buffer)
535+
Decimal256Array.new(self, size, validity_buffer, values_buffer)
536+
end
537+
end
538+
525539
class VariableSizeListType < Type
526540
attr_reader :child
527541
def initialize(child)

ruby/red-arrow-format/test/test-reader.rb

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -676,19 +676,61 @@ def test_read
676676

677677
sub_test_case("Decimal128") do
678678
def build_array
679-
@positive = "12345678901234567890123456789012345.678"
680-
@negative = "-12345678901234567890123456789012345.67"
679+
@positive_small = "1.200"
680+
@positive_large = ("1234567890" * 3) + "12345.678"
681+
@negative_small = "-1.200"
682+
@negative_large = "-" + ("1234567890" * 3) + "12345.678"
681683
Arrow::Decimal128Array.new({precision: 38, scale: 3},
682-
[@positive, nil, @negative])
684+
[
685+
@positive_large,
686+
@positive_small,
687+
nil,
688+
@negative_small,
689+
@negative_large,
690+
])
683691
end
684692

685693
def test_read
686694
assert_equal([
687695
{
688696
"value" => [
689-
BigDecimal(@positive),
697+
BigDecimal(@positive_large),
698+
BigDecimal(@positive_small),
690699
nil,
691-
BigDecimal(@negative),
700+
BigDecimal(@negative_small),
701+
BigDecimal(@negative_large),
702+
],
703+
},
704+
],
705+
read)
706+
end
707+
end
708+
709+
sub_test_case("Decimal256") do
710+
def build_array
711+
@positive_small = "1.200"
712+
@positive_large = ("1234567890" * 7) + "123.456"
713+
@negative_small = "-1.200"
714+
@negative_large = "-" + ("1234567890" * 7) + "123.456"
715+
Arrow::Decimal256Array.new({precision: 76, scale: 3},
716+
[
717+
@positive_large,
718+
@positive_small,
719+
nil,
720+
@negative_small,
721+
@negative_large,
722+
])
723+
end
724+
725+
def test_read
726+
assert_equal([
727+
{
728+
"value" => [
729+
BigDecimal(@positive_large),
730+
BigDecimal(@positive_small),
731+
nil,
732+
BigDecimal(@negative_small),
733+
BigDecimal(@negative_large),
692734
],
693735
},
694736
],

0 commit comments

Comments
 (0)