Skip to content

Commit f5cfb51

Browse files
committed
GH-50528: [Ruby] Add ArrowFormat::ArrayBuilder
1 parent 1f3f40f commit f5cfb51

4 files changed

Lines changed: 360 additions & 7 deletions

File tree

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
18+
module ArrowFormat
19+
class ArrayBuilder
20+
def initialize(values)
21+
@values = values
22+
end
23+
24+
def build
25+
target = nil
26+
@values.each do |value|
27+
target = detect_target(value, target)
28+
break if target and target[:detected]
29+
end
30+
if target
31+
array_class = target[:array_class]
32+
array_class ||= finalize_array_class(target)
33+
type = target[:type]
34+
if type
35+
array_class.new(type, @values)
36+
else
37+
array_class.new(@values)
38+
end
39+
else
40+
UTF8Array.new(@values)
41+
end
42+
end
43+
44+
private
45+
def detect_target(value, target)
46+
case value
47+
when nil
48+
target
49+
when true, false
50+
{
51+
array_class: BooleanArray,
52+
detected: true,
53+
}
54+
when String
55+
{
56+
array_class: UTF8Array,
57+
detected: true,
58+
}
59+
when Float
60+
{
61+
array_class: Float64Array,
62+
detected: true,
63+
}
64+
when Integer
65+
target ||= {}
66+
min = target[:min] || value
67+
max = target[:max] || value
68+
min = value if value < min
69+
max = value if value > max
70+
71+
if target[:value_type] == :int or value < 0
72+
{
73+
value_type: :int,
74+
min: min,
75+
max: max,
76+
}
77+
else
78+
{
79+
value_type: :uint,
80+
min: min,
81+
max: max,
82+
}
83+
end
84+
when Time
85+
{
86+
array_class: TimestampArray,
87+
type: TimestampType.new(:nanosecond),
88+
detected: true,
89+
}
90+
when Date
91+
{
92+
array_class: Date32Array,
93+
detected: true,
94+
}
95+
else
96+
{
97+
array_class: UTF8Array,
98+
detected: true,
99+
}
100+
end
101+
end
102+
103+
def finalize_array_class(target)
104+
value_type = target[:value_type]
105+
case value_type
106+
when :int
107+
min = target[:min]
108+
max = target[:max]
109+
110+
if Int8Type::MIN_VALUE <= min and max <= Int8Type::MAX_VALUE
111+
Int8Array
112+
elsif Int16Type::MIN_VALUE <= min and max <= Int16Type::MAX_VALUE
113+
Int16Array
114+
elsif Int32Type::MIN_VALUE <= min and max <= Int32Type::MAX_VALUE
115+
Int32Array
116+
elsif Int64Type::MIN_VALUE <= min and max <= Int64Type::MAX_VALUE
117+
Int64Array
118+
else
119+
UTF8Array
120+
end
121+
when :uint
122+
max = target[:max]
123+
124+
if max <= UInt8Type::MAX_VALUE
125+
UInt8Array
126+
elsif max <= UInt16Type::MAX_VALUE
127+
UInt16Array
128+
elsif max <= UInt32Type::MAX_VALUE
129+
UInt32Array
130+
elsif max <= UInt64Type::MAX_VALUE
131+
UInt64Array
132+
else
133+
UTF8Array
134+
end
135+
else
136+
UTF8Array
137+
end
138+
end
139+
end
140+
end

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

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
require "bigdecimal"
1919

20+
require_relative "array-builder"
2021
require_relative "bitmap"
2122
require_relative "bitmap-builder"
2223

@@ -26,6 +27,12 @@ module ArrowFormat
2627
class Array
2728
include Enumerable
2829

30+
class << self
31+
def build(values)
32+
ArrayBuilder.new(values).build
33+
end
34+
end
35+
2936
attr_reader :type
3037
attr_reader :size
3138
alias_method :length, :size
@@ -271,9 +278,9 @@ def build_data(data, type)
271278
if value.nil?
272279
validity_buffer_builder ||= SparseBitmapBuilder.new
273280
validity_buffer_builder.unset(i)
274-
buffer.append_as_bytes(pack_value(nil, pack_template))
281+
buffer.append_as_bytes(pack_value(nil, pack_template, type))
275282
else
276-
buffer.append_as_bytes(pack_value(value, pack_template))
283+
buffer.append_as_bytes(pack_value(value, pack_template, type))
277284
end
278285
n += 1
279286
end
@@ -283,7 +290,7 @@ def build_data(data, type)
283290
return n, validity_buffer, IO::Buffer.for(buffer)
284291
end
285292

286-
def pack_value(value, template)
293+
def pack_value(value, template, type)
287294
value = 0 if value.nil?
288295
[value].pack(template)
289296
end
@@ -455,6 +462,17 @@ def type
455462
Date32Type.singleton
456463
end
457464
end
465+
466+
private
467+
def pack_value(value, template, type)
468+
if value.nil?
469+
[0].pack(template)
470+
elsif value.is_a?(Date)
471+
[value.day].pack(template)
472+
else
473+
[value].pack(template)
474+
end
475+
end
458476
end
459477

460478
class Date64Array < DateArray
@@ -490,6 +508,27 @@ def type_class
490508
TimestampType
491509
end
492510
end
511+
512+
private
513+
def pack_value(value, template, type)
514+
if value.nil?
515+
[0, 0].pack(template)
516+
elsif value.is_a?(Time)
517+
case type.unit
518+
when :second
519+
value = value.to_i
520+
when :millisecond
521+
value = (value.to_i * 1_000) + (value.nsec / 1_000_000)
522+
when :microsecond
523+
value = (value.to_i * 1_000_000) + (value.nsec / 1_000)
524+
when :nanosecond
525+
value = (value.to_i * 1_000_000_000) + value.nsec
526+
end
527+
[value].pack(template)
528+
else
529+
value.pack(template)
530+
end
531+
end
493532
end
494533

495534
class IntervalArray < TemporalArray
@@ -552,7 +591,7 @@ def element_size
552591
super * 2
553592
end
554593

555-
def pack_value(value, template)
594+
def pack_value(value, template, type)
556595
if value.nil?
557596
[0, 0].pack(template)
558597
elsif value.is_a?(Hash)
@@ -613,7 +652,7 @@ def element_size
613652
IO::Buffer.size_of(@type.buffer_types)
614653
end
615654

616-
def pack_value(value, template)
655+
def pack_value(value, template, type)
617656
if value.nil?
618657
[0, 0, 0].pack(template)
619658
elsif value.is_a?(Hash)

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

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@ def to_flatbuffers
8888
end
8989

9090
class Int8Type < IntType
91+
MIN_VALUE = -(2 ** 7)
92+
MAX_VALUE = (2 ** 7) - 1
93+
9194
class << self
9295
def singleton
9396
@singleton ||= new
@@ -116,6 +119,9 @@ def build_array(...)
116119
end
117120

118121
class UInt8Type < IntType
122+
MIN_VALUE = 0
123+
MAX_VALUE = (2 ** 8) - 1
124+
119125
class << self
120126
def singleton
121127
@singleton ||= new
@@ -144,6 +150,9 @@ def build_array(...)
144150
end
145151

146152
class Int16Type < IntType
153+
MIN_VALUE = -(2 ** 15)
154+
MAX_VALUE = (2 ** 15) - 1
155+
147156
class << self
148157
def singleton
149158
@singleton ||= new
@@ -172,6 +181,9 @@ def build_array(...)
172181
end
173182

174183
class UInt16Type < IntType
184+
MIN_VALUE = 0
185+
MAX_VALUE = (2 ** 16) - 1
186+
175187
class << self
176188
def singleton
177189
@singleton ||= new
@@ -200,6 +212,9 @@ def build_array(...)
200212
end
201213

202214
class Int32Type < IntType
215+
MIN_VALUE = -(2 ** 31)
216+
MAX_VALUE = (2 ** 31) - 1
217+
203218
class << self
204219
def singleton
205220
@singleton ||= new
@@ -228,6 +243,9 @@ def build_array(...)
228243
end
229244

230245
class UInt32Type < IntType
246+
MIN_VALUE = 0
247+
MAX_VALUE = (2 ** 32) - 1
248+
231249
class << self
232250
def singleton
233251
@singleton ||= new
@@ -256,6 +274,9 @@ def build_array(...)
256274
end
257275

258276
class Int64Type < IntType
277+
MIN_VALUE = -(2 ** 63)
278+
MAX_VALUE = (2 ** 63) - 1
279+
259280
class << self
260281
def singleton
261282
@singleton ||= new
@@ -284,6 +305,9 @@ def build_array(...)
284305
end
285306

286307
class UInt64Type < IntType
308+
MIN_VALUE = 0
309+
MAX_VALUE = (2 ** 64) - 1
310+
287311
class << self
288312
def singleton
289313
@singleton ||= new
@@ -559,7 +583,7 @@ def try_convert(value)
559583
case value
560584
when Symbol
561585
unit = value
562-
new(unit, nil)
586+
new(unit)
563587
when ::Array
564588
unit, time_zone = value
565589
new(unit, time_zone)
@@ -573,7 +597,7 @@ def try_convert(value)
573597

574598
attr_reader :unit
575599
attr_reader :time_zone
576-
def initialize(unit, time_zone)
600+
def initialize(unit, time_zone=nil)
577601
super()
578602
@unit = unit
579603
@time_zone = time_zone

0 commit comments

Comments
 (0)