red-arrow-format 23.0.1 → 24.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +7 -2
  3. data/Rakefile +23 -1
  4. data/lib/arrow-format/array.rb +486 -117
  5. data/lib/arrow-format/bitmap.rb +25 -6
  6. data/lib/arrow-format/buffer-alignable.rb +35 -0
  7. data/lib/arrow-format/dictionary.rb +29 -0
  8. data/lib/arrow-format/error.rb +1 -1
  9. data/lib/arrow-format/field.rb +24 -3
  10. data/lib/arrow-format/file-reader.rb +47 -25
  11. data/lib/arrow-format/file-writer.rb +56 -0
  12. data/lib/arrow-format/flatbuffers.rb +67 -0
  13. data/lib/arrow-format/org/apache/arrow/flatbuf/binary.rb +5 -1
  14. data/lib/arrow-format/org/apache/arrow/flatbuf/binary_view.rb +5 -1
  15. data/lib/arrow-format/org/apache/arrow/flatbuf/block.rb +10 -2
  16. data/lib/arrow-format/org/apache/arrow/flatbuf/body_compression.rb +8 -1
  17. data/lib/arrow-format/org/apache/arrow/flatbuf/body_compression_method.rb +3 -1
  18. data/lib/arrow-format/org/apache/arrow/flatbuf/bool.rb +5 -1
  19. data/lib/arrow-format/org/apache/arrow/flatbuf/buffer.rb +8 -1
  20. data/lib/arrow-format/org/apache/arrow/flatbuf/compression_type.rb +1 -1
  21. data/lib/arrow-format/org/apache/arrow/flatbuf/date.rb +7 -1
  22. data/lib/arrow-format/org/apache/arrow/flatbuf/date_unit.rb +1 -1
  23. data/lib/arrow-format/org/apache/arrow/flatbuf/decimal.rb +9 -1
  24. data/lib/arrow-format/org/apache/arrow/flatbuf/dictionary_batch.rb +9 -1
  25. data/lib/arrow-format/org/apache/arrow/flatbuf/dictionary_encoding.rb +11 -2
  26. data/lib/arrow-format/org/apache/arrow/flatbuf/dictionary_kind.rb +1 -1
  27. data/lib/arrow-format/org/apache/arrow/flatbuf/duration.rb +7 -1
  28. data/lib/arrow-format/org/apache/arrow/flatbuf/endianness.rb +1 -1
  29. data/lib/arrow-format/org/apache/arrow/flatbuf/feature.rb +1 -1
  30. data/lib/arrow-format/org/apache/arrow/flatbuf/field.rb +13 -1
  31. data/lib/arrow-format/org/apache/arrow/flatbuf/field_node.rb +8 -1
  32. data/lib/arrow-format/org/apache/arrow/flatbuf/fixed_size_binary.rb +7 -1
  33. data/lib/arrow-format/org/apache/arrow/flatbuf/fixed_size_list.rb +7 -1
  34. data/lib/arrow-format/org/apache/arrow/flatbuf/floating_point.rb +7 -1
  35. data/lib/arrow-format/org/apache/arrow/flatbuf/footer.rb +22 -2
  36. data/lib/arrow-format/org/apache/arrow/flatbuf/int.rb +8 -1
  37. data/lib/arrow-format/org/apache/arrow/flatbuf/interval.rb +7 -1
  38. data/lib/arrow-format/org/apache/arrow/flatbuf/interval_unit.rb +1 -1
  39. data/lib/arrow-format/org/apache/arrow/flatbuf/key_value.rb +8 -1
  40. data/lib/arrow-format/org/apache/arrow/flatbuf/large_binary.rb +5 -1
  41. data/lib/arrow-format/org/apache/arrow/flatbuf/large_list.rb +5 -1
  42. data/lib/arrow-format/org/apache/arrow/flatbuf/large_list_view.rb +5 -1
  43. data/lib/arrow-format/org/apache/arrow/flatbuf/large_utf8.rb +5 -1
  44. data/lib/arrow-format/org/apache/arrow/flatbuf/list.rb +5 -1
  45. data/lib/arrow-format/org/apache/arrow/flatbuf/list_view.rb +5 -1
  46. data/lib/arrow-format/org/apache/arrow/flatbuf/map.rb +8 -2
  47. data/lib/arrow-format/org/apache/arrow/flatbuf/message.rb +22 -2
  48. data/lib/arrow-format/org/apache/arrow/flatbuf/message_header.rb +1 -1
  49. data/lib/arrow-format/org/apache/arrow/flatbuf/metadata_version.rb +1 -1
  50. data/lib/arrow-format/org/apache/arrow/flatbuf/null.rb +5 -1
  51. data/lib/arrow-format/org/apache/arrow/flatbuf/precision.rb +1 -1
  52. data/lib/arrow-format/org/apache/arrow/flatbuf/record_batch.rb +12 -2
  53. data/lib/arrow-format/org/apache/arrow/flatbuf/run_end_encoded.rb +5 -1
  54. data/lib/arrow-format/org/apache/arrow/flatbuf/schema.rb +10 -1
  55. data/lib/arrow-format/org/apache/arrow/flatbuf/sparse_matrix_compressed_axis.rb +1 -1
  56. data/lib/arrow-format/org/apache/arrow/flatbuf/sparse_matrix_index_csx.rb +12 -2
  57. data/lib/arrow-format/org/apache/arrow/flatbuf/sparse_tensor.rb +13 -1
  58. data/lib/arrow-format/org/apache/arrow/flatbuf/sparse_tensor_index.rb +1 -1
  59. data/lib/arrow-format/org/apache/arrow/flatbuf/sparse_tensor_index_coo.rb +13 -4
  60. data/lib/arrow-format/org/apache/arrow/flatbuf/sparse_tensor_index_csf.rb +14 -4
  61. data/lib/arrow-format/org/apache/arrow/flatbuf/struct_.rb +5 -1
  62. data/lib/arrow-format/org/apache/arrow/flatbuf/tensor.rb +11 -1
  63. data/lib/arrow-format/org/apache/arrow/flatbuf/tensor_dim.rb +8 -1
  64. data/lib/arrow-format/org/apache/arrow/flatbuf/time.rb +8 -1
  65. data/lib/arrow-format/org/apache/arrow/flatbuf/time_unit.rb +1 -1
  66. data/lib/arrow-format/org/apache/arrow/flatbuf/timestamp.rb +8 -1
  67. data/lib/arrow-format/org/apache/arrow/flatbuf/type.rb +1 -1
  68. data/lib/arrow-format/org/apache/arrow/flatbuf/union.rb +8 -1
  69. data/lib/arrow-format/org/apache/arrow/flatbuf/union_mode.rb +1 -1
  70. data/lib/arrow-format/org/apache/arrow/flatbuf/utf8.rb +5 -1
  71. data/lib/arrow-format/org/apache/arrow/flatbuf/utf8view.rb +5 -1
  72. data/lib/arrow-format/readable.rb +151 -96
  73. data/lib/arrow-format/record-batch.rb +70 -1
  74. data/lib/arrow-format/schema.rb +14 -1
  75. data/lib/arrow-format/streaming-pull-reader.rb +49 -23
  76. data/lib/arrow-format/streaming-reader.rb +51 -14
  77. data/lib/arrow-format/streaming-writer.rb +156 -0
  78. data/lib/arrow-format/type.rb +397 -34
  79. data/lib/arrow-format/version.rb +1 -1
  80. data/lib/arrow-format.rb +2 -0
  81. data/red-arrow-format.gemspec +2 -1
  82. metadata +9 -4
@@ -17,152 +17,186 @@
17
17
 
18
18
  require_relative "array"
19
19
  require_relative "field"
20
+ require_relative "flatbuffers"
20
21
  require_relative "record-batch"
21
22
  require_relative "schema"
22
23
  require_relative "type"
23
24
 
24
- require_relative "org/apache/arrow/flatbuf/binary"
25
- require_relative "org/apache/arrow/flatbuf/bool"
26
- require_relative "org/apache/arrow/flatbuf/date"
27
- require_relative "org/apache/arrow/flatbuf/date_unit"
28
- require_relative "org/apache/arrow/flatbuf/decimal"
29
- require_relative "org/apache/arrow/flatbuf/dictionary_encoding"
30
- require_relative "org/apache/arrow/flatbuf/dictionary_batch"
31
- require_relative "org/apache/arrow/flatbuf/duration"
32
- require_relative "org/apache/arrow/flatbuf/fixed_size_binary"
33
- require_relative "org/apache/arrow/flatbuf/floating_point"
34
- require_relative "org/apache/arrow/flatbuf/int"
35
- require_relative "org/apache/arrow/flatbuf/interval"
36
- require_relative "org/apache/arrow/flatbuf/interval_unit"
37
- require_relative "org/apache/arrow/flatbuf/large_binary"
38
- require_relative "org/apache/arrow/flatbuf/large_list"
39
- require_relative "org/apache/arrow/flatbuf/large_utf8"
40
- require_relative "org/apache/arrow/flatbuf/list"
41
- require_relative "org/apache/arrow/flatbuf/map"
42
- require_relative "org/apache/arrow/flatbuf/message"
43
- require_relative "org/apache/arrow/flatbuf/null"
44
- require_relative "org/apache/arrow/flatbuf/precision"
45
- require_relative "org/apache/arrow/flatbuf/record_batch"
46
- require_relative "org/apache/arrow/flatbuf/schema"
47
- require_relative "org/apache/arrow/flatbuf/struct_"
48
- require_relative "org/apache/arrow/flatbuf/time"
49
- require_relative "org/apache/arrow/flatbuf/time_unit"
50
- require_relative "org/apache/arrow/flatbuf/timestamp"
51
- require_relative "org/apache/arrow/flatbuf/union"
52
- require_relative "org/apache/arrow/flatbuf/union_mode"
53
- require_relative "org/apache/arrow/flatbuf/utf8"
54
-
55
25
  module ArrowFormat
56
26
  module Readable
57
27
  private
58
- def read_schema(fb_schema)
28
+ def read_custom_metadata(fb_custom_metadata)
29
+ return nil if fb_custom_metadata.nil?
30
+ metadata = {}
31
+ fb_custom_metadata.each do |key_value|
32
+ metadata[key_value.key] = key_value.value
33
+ end
34
+ metadata
35
+ end
36
+
37
+ def read_schema(fb_schema, fb_message_custom_metadata=nil)
59
38
  fields = fb_schema.fields.collect do |fb_field|
60
39
  read_field(fb_field)
61
40
  end
62
- Schema.new(fields)
41
+ message_metadata = read_custom_metadata(fb_message_custom_metadata)
42
+ Schema.new(fields,
43
+ metadata: read_custom_metadata(fb_schema.custom_metadata),
44
+ message_metadata: message_metadata)
63
45
  end
64
46
 
65
- def read_field(fb_field)
47
+ def read_field(fb_field,
48
+ map_entries: false,
49
+ map_key: false,
50
+ map_value: false)
66
51
  fb_type = fb_field.type
67
52
  case fb_type
68
- when Org::Apache::Arrow::Flatbuf::Null
53
+ when FB::Null
69
54
  type = NullType.singleton
70
- when Org::Apache::Arrow::Flatbuf::Bool
55
+ when FB::Bool
71
56
  type = BooleanType.singleton
72
- when Org::Apache::Arrow::Flatbuf::Int
57
+ when FB::Int
73
58
  type = read_type_int(fb_type)
74
- when Org::Apache::Arrow::Flatbuf::FloatingPoint
59
+ when FB::FloatingPoint
75
60
  case fb_type.precision
76
- when Org::Apache::Arrow::Flatbuf::Precision::SINGLE
61
+ when FB::Precision::SINGLE
77
62
  type = Float32Type.singleton
78
- when Org::Apache::Arrow::Flatbuf::Precision::DOUBLE
63
+ when FB::Precision::DOUBLE
79
64
  type = Float64Type.singleton
65
+ else
66
+ raise ReadError.new("Unsupported type: #{fb_type.inspect}")
80
67
  end
81
- when Org::Apache::Arrow::Flatbuf::Date
68
+ when FB::Date
82
69
  case fb_type.unit
83
- when Org::Apache::Arrow::Flatbuf::DateUnit::DAY
70
+ when FB::DateUnit::DAY
84
71
  type = Date32Type.singleton
85
- when Org::Apache::Arrow::Flatbuf::DateUnit::MILLISECOND
72
+ when FB::DateUnit::MILLISECOND
86
73
  type = Date64Type.singleton
74
+ else
75
+ raise ReadError.new("Unsupported type: #{fb_type.inspect}")
87
76
  end
88
- when Org::Apache::Arrow::Flatbuf::Time
77
+ when FB::Time
89
78
  case fb_type.bit_width
90
79
  when 32
91
80
  case fb_type.unit
92
- when Org::Apache::Arrow::Flatbuf::TimeUnit::SECOND
81
+ when FB::TimeUnit::SECOND
93
82
  type = Time32Type.new(:second)
94
- when Org::Apache::Arrow::Flatbuf::TimeUnit::MILLISECOND
83
+ when FB::TimeUnit::MILLISECOND
95
84
  type = Time32Type.new(:millisecond)
85
+ else
86
+ raise ReadError.new("Unsupported type: #{fb_type.inspect}")
96
87
  end
97
88
  when 64
98
89
  case fb_type.unit
99
- when Org::Apache::Arrow::Flatbuf::TimeUnit::MICROSECOND
90
+ when FB::TimeUnit::MICROSECOND
100
91
  type = Time64Type.new(:microsecond)
101
- when Org::Apache::Arrow::Flatbuf::TimeUnit::NANOSECOND
92
+ when FB::TimeUnit::NANOSECOND
102
93
  type = Time64Type.new(:nanosecond)
94
+ else
95
+ raise ReadError.new("Unsupported type: #{fb_type.inspect}")
103
96
  end
104
97
  end
105
- when Org::Apache::Arrow::Flatbuf::Timestamp
98
+ when FB::Timestamp
106
99
  unit = fb_type.unit.name.downcase.to_sym
107
100
  type = TimestampType.new(unit, fb_type.timezone)
108
- when Org::Apache::Arrow::Flatbuf::Interval
101
+ when FB::Interval
109
102
  case fb_type.unit
110
- when Org::Apache::Arrow::Flatbuf::IntervalUnit::YEAR_MONTH
111
- type = YearMonthIntervalType.new
112
- when Org::Apache::Arrow::Flatbuf::IntervalUnit::DAY_TIME
113
- type = DayTimeIntervalType.new
114
- when Org::Apache::Arrow::Flatbuf::IntervalUnit::MONTH_DAY_NANO
115
- type = MonthDayNanoIntervalType.new
103
+ when FB::IntervalUnit::YEAR_MONTH
104
+ type = YearMonthIntervalType.singleton
105
+ when FB::IntervalUnit::DAY_TIME
106
+ type = DayTimeIntervalType.singleton
107
+ when FB::IntervalUnit::MONTH_DAY_NANO
108
+ type = MonthDayNanoIntervalType.singleton
109
+ else
110
+ raise ReadError.new("Unsupported type: #{fb_type.inspect}")
116
111
  end
117
- when Org::Apache::Arrow::Flatbuf::Duration
112
+ when FB::Duration
118
113
  unit = fb_type.unit.name.downcase.to_sym
119
114
  type = DurationType.new(unit)
120
- when Org::Apache::Arrow::Flatbuf::List
115
+ when FB::List
121
116
  type = ListType.new(read_field(fb_field.children[0]))
122
- when Org::Apache::Arrow::Flatbuf::LargeList
117
+ when FB::LargeList
123
118
  type = LargeListType.new(read_field(fb_field.children[0]))
124
- when Org::Apache::Arrow::Flatbuf::Struct
125
- children = fb_field.children.collect {|child| read_field(child)}
119
+ when FB::FixedSizeList
120
+ type = FixedSizeListType.new(read_field(fb_field.children[0]),
121
+ fb_type.list_size)
122
+ when FB::Struct
123
+ if map_entries
124
+ fb_children = fb_field.children
125
+ children = [
126
+ read_field(fb_children[0], map_key: true),
127
+ read_field(fb_children[1], map_value: true),
128
+ ]
129
+ else
130
+ children = fb_field.children.collect {|child| read_field(child)}
131
+ end
126
132
  type = StructType.new(children)
127
- when Org::Apache::Arrow::Flatbuf::Union
133
+ when FB::Union
128
134
  children = fb_field.children.collect {|child| read_field(child)}
129
135
  type_ids = fb_type.type_ids
130
136
  case fb_type.mode
131
- when Org::Apache::Arrow::Flatbuf::UnionMode::DENSE
137
+ when FB::UnionMode::DENSE
132
138
  type = DenseUnionType.new(children, type_ids)
133
- when Org::Apache::Arrow::Flatbuf::UnionMode::SPARSE
139
+ when FB::UnionMode::SPARSE
134
140
  type = SparseUnionType.new(children, type_ids)
141
+ else
142
+ raise ReadError.new("Unsupported type: #{fb_type.inspect}")
135
143
  end
136
- when Org::Apache::Arrow::Flatbuf::Map
137
- type = MapType.new(read_field(fb_field.children[0]))
138
- when Org::Apache::Arrow::Flatbuf::Binary
144
+ when FB::Map
145
+ type = MapType.new(read_field(fb_field.children[0], map_entries: true),
146
+ fb_type.keys_sorted?)
147
+ when FB::Binary
139
148
  type = BinaryType.singleton
140
- when Org::Apache::Arrow::Flatbuf::LargeBinary
149
+ when FB::LargeBinary
141
150
  type = LargeBinaryType.singleton
142
- when Org::Apache::Arrow::Flatbuf::Utf8
151
+ when FB::Utf8
143
152
  type = UTF8Type.singleton
144
- when Org::Apache::Arrow::Flatbuf::LargeUtf8
153
+ when FB::LargeUtf8
145
154
  type = LargeUTF8Type.singleton
146
- when Org::Apache::Arrow::Flatbuf::FixedSizeBinary
155
+ when FB::FixedSizeBinary
147
156
  type = FixedSizeBinaryType.new(fb_type.byte_width)
148
- when Org::Apache::Arrow::Flatbuf::Decimal
157
+ when FB::Decimal
149
158
  case fb_type.bit_width
150
159
  when 128
151
160
  type = Decimal128Type.new(fb_type.precision, fb_type.scale)
152
161
  when 256
153
162
  type = Decimal256Type.new(fb_type.precision, fb_type.scale)
163
+ else
164
+ raise ReadError.new("Unsupported type: #{fb_type.inspect}")
154
165
  end
166
+ else
167
+ raise ReadError.new("Unsupported type: #{fb_type.inspect}")
155
168
  end
156
169
 
157
170
  dictionary = fb_field.dictionary
158
171
  if dictionary
159
172
  dictionary_id = dictionary.id
160
173
  index_type = read_type_int(dictionary.index_type)
161
- type = DictionaryType.new(index_type, type, dictionary.ordered?)
174
+ value_type = type
175
+ type = DictionaryType.new(dictionary_id,
176
+ index_type,
177
+ value_type,
178
+ dictionary.ordered?)
179
+ end
180
+
181
+ # Map type uses static "entries"/"key"/"value" as field names
182
+ # instead of field names in FlatBuffers. It's based on the
183
+ # specification:
184
+ #
185
+ # The names of the child fields may be respectively "entries",
186
+ # "key", and "value", but this is not enforced.
187
+ if map_entries
188
+ name = "entries"
189
+ elsif map_key
190
+ name = "key"
191
+ elsif map_value
192
+ name = "value"
162
193
  else
163
- dictionary_id = nil
194
+ name = fb_field.name
164
195
  end
165
- Field.new(fb_field.name, type, fb_field.nullable?, dictionary_id)
196
+ Field.new(name,
197
+ type,
198
+ nullable: fb_field.nullable?,
199
+ metadata: read_custom_metadata(fb_field.custom_metadata))
166
200
  end
167
201
 
168
202
  def read_type_int(fb_type)
@@ -194,17 +228,23 @@ module ArrowFormat
194
228
  end
195
229
  end
196
230
 
197
- def read_record_batch(fb_record_batch, schema, body)
231
+ def read_record_batch(version,
232
+ fb_record_batch,
233
+ fb_message_custom_metadata,
234
+ schema,
235
+ body)
236
+ message_metadata = read_custom_metadata(fb_message_custom_metadata)
198
237
  n_rows = fb_record_batch.length
199
238
  nodes = fb_record_batch.nodes
200
239
  buffers = fb_record_batch.buffers
201
240
  columns = schema.fields.collect do |field|
202
- read_column(field, nodes, buffers, body)
241
+ read_column(version, field, nodes, buffers, body)
203
242
  end
204
- RecordBatch.new(schema, n_rows, columns)
243
+ RecordBatch.new(schema, n_rows, columns,
244
+ message_metadata: message_metadata)
205
245
  end
206
246
 
207
- def read_column(field, nodes, buffers, body)
247
+ def read_column(version, field, nodes, buffers, body)
208
248
  node = nodes.shift
209
249
  length = node.length
210
250
 
@@ -222,49 +262,64 @@ module ArrowFormat
222
262
  NumberType,
223
263
  TemporalType
224
264
  values_buffer = buffers.shift
225
- values = body.slice(values_buffer.offset, values_buffer.length)
265
+ values = body&.slice(values_buffer.offset, values_buffer.length)
226
266
  field.type.build_array(length, validity, values)
227
267
  when VariableSizeBinaryType
228
268
  offsets_buffer = buffers.shift
229
269
  values_buffer = buffers.shift
230
- offsets = body.slice(offsets_buffer.offset, offsets_buffer.length)
231
- values = body.slice(values_buffer.offset, values_buffer.length)
270
+ offsets = body&.slice(offsets_buffer.offset, offsets_buffer.length)
271
+ values = body&.slice(values_buffer.offset, values_buffer.length)
232
272
  field.type.build_array(length, validity, offsets, values)
233
273
  when FixedSizeBinaryType
234
274
  values_buffer = buffers.shift
235
- values = body.slice(values_buffer.offset, values_buffer.length)
275
+ values = body&.slice(values_buffer.offset, values_buffer.length)
236
276
  field.type.build_array(length, validity, values)
237
277
  when VariableSizeListType
238
278
  offsets_buffer = buffers.shift
239
- offsets = body.slice(offsets_buffer.offset, offsets_buffer.length)
240
- child = read_column(field.type.child, nodes, buffers, body)
279
+ offsets = body&.slice(offsets_buffer.offset, offsets_buffer.length)
280
+ child = read_column(version, field.type.child, nodes, buffers, body)
241
281
  field.type.build_array(length, validity, offsets, child)
282
+ when FixedSizeListType
283
+ child = read_column(version, field.type.child, nodes, buffers, body)
284
+ field.type.build_array(length, validity, child)
242
285
  when StructType
243
286
  children = field.type.children.collect do |child|
244
- read_column(child, nodes, buffers, body)
287
+ read_column(version, child, nodes, buffers, body)
245
288
  end
246
289
  field.type.build_array(length, validity, children)
247
290
  when DenseUnionType
248
- # dense union type doesn't have validity.
249
- types = validity
291
+ if version == FB::MetadataVersion::V4
292
+ # Dense union type has validity with V4.
293
+ types_buffer = buffers.shift
294
+ types = body&.slice(types_buffer.offset, types_buffer.length)
295
+ else
296
+ # Dense union type doesn't have validity.
297
+ types = validity
298
+ end
250
299
  offsets_buffer = buffers.shift
251
- offsets = body.slice(offsets_buffer.offset, offsets_buffer.length)
300
+ offsets = body&.slice(offsets_buffer.offset, offsets_buffer.length)
252
301
  children = field.type.children.collect do |child|
253
- read_column(child, nodes, buffers, body)
302
+ read_column(version, child, nodes, buffers, body)
254
303
  end
255
304
  field.type.build_array(length, types, offsets, children)
256
305
  when SparseUnionType
257
- # sparse union type doesn't have validity.
258
- types = validity
306
+ if version == FB::MetadataVersion::V4
307
+ # Sparse union type has validity with V4.
308
+ types_buffer = buffers.shift
309
+ types = body&.slice(types_buffer.offset, types_buffer.length)
310
+ else
311
+ # Sparse union type doesn't have validity.
312
+ types = validity
313
+ end
259
314
  children = field.type.children.collect do |child|
260
- read_column(child, nodes, buffers, body)
315
+ read_column(version, child, nodes, buffers, body)
261
316
  end
262
317
  field.type.build_array(length, types, children)
263
318
  when DictionaryType
264
319
  indices_buffer = buffers.shift
265
- indices = body.slice(indices_buffer.offset, indices_buffer.length)
266
- dictionary = find_dictionary(field.dictionary_id)
267
- field.type.build_array(length, validity, indices, dictionary)
320
+ indices = body&.slice(indices_buffer.offset, indices_buffer.length)
321
+ dictionaries = find_dictionaries(field.type.id)
322
+ field.type.build_array(length, validity, indices, dictionaries)
268
323
  end
269
324
  end
270
325
  end
@@ -14,15 +14,27 @@
14
14
  # specific language governing permissions and limitations
15
15
  # under the License.
16
16
 
17
+ require_relative "buffer-alignable"
18
+
17
19
  module ArrowFormat
18
20
  class RecordBatch
21
+ include BufferAlignable
22
+
19
23
  attr_reader :schema
20
24
  attr_reader :n_rows
25
+ alias_method :size, :n_rows
26
+ alias_method :length, :n_rows
21
27
  attr_reader :columns
22
- def initialize(schema, n_rows, columns)
28
+ attr_reader :message_metadata
29
+ def initialize(schema, n_rows, columns, message_metadata: nil)
23
30
  @schema = schema
24
31
  @n_rows = n_rows
25
32
  @columns = columns
33
+ @message_metadata = message_metadata
34
+ end
35
+
36
+ def empty?
37
+ @n_rows.zero?
26
38
  end
27
39
 
28
40
  def to_h
@@ -32,5 +44,62 @@ module ArrowFormat
32
44
  end
33
45
  hash
34
46
  end
47
+
48
+ def to_flatbuffers
49
+ fb_record_batch = FB::RecordBatch::Data.new
50
+ fb_record_batch.length = @n_rows
51
+ fb_record_batch.nodes = all_columns_enumerator.collect do |array|
52
+ field_node = FB::FieldNode::Data.new
53
+ field_node.length = array.size
54
+ field_node.null_count = array.n_nulls
55
+ field_node
56
+ end
57
+ offset = 0
58
+ fb_record_batch.buffers = all_buffers_enumerator.collect do |buffer|
59
+ fb_buffer = FB::Buffer::Data.new
60
+ fb_buffer.offset = offset
61
+ if buffer
62
+ aligned_size = aligned_buffer_size(buffer)
63
+ offset += aligned_size
64
+ fb_buffer.length = aligned_size
65
+ else
66
+ fb_buffer.length = 0
67
+ end
68
+ fb_buffer
69
+ end
70
+ # body_compression = FB::BodyCompression::Data.new
71
+ # body_compression.codec = ...
72
+ # fb_record_batch.compression = body_compression
73
+ fb_record_batch
74
+ end
75
+
76
+ # Pre-order depth-first traversal
77
+ def all_columns_enumerator
78
+ Enumerator.new do |yielder|
79
+ traverse = lambda do |array|
80
+ yielder << array
81
+ if array.respond_to?(:child)
82
+ traverse.call(array.child)
83
+ elsif array.respond_to?(:children)
84
+ array.children.each do |child_array|
85
+ traverse.call(child_array)
86
+ end
87
+ end
88
+ end
89
+ @columns.each do |array|
90
+ traverse.call(array)
91
+ end
92
+ end
93
+ end
94
+
95
+ def all_buffers_enumerator
96
+ Enumerator.new do |yielder|
97
+ all_columns_enumerator.each do |array|
98
+ array.each_buffer do |buffer|
99
+ yielder << buffer
100
+ end
101
+ end
102
+ end
103
+ end
35
104
  end
36
105
  end
@@ -17,8 +17,21 @@
17
17
  module ArrowFormat
18
18
  class Schema
19
19
  attr_reader :fields
20
- def initialize(fields)
20
+ attr_reader :metadata
21
+ attr_reader :message_metadata
22
+ def initialize(fields, metadata: nil, message_metadata: nil)
21
23
  @fields = fields
24
+ @metadata = metadata
25
+ @message_metadata = message_metadata
26
+ end
27
+
28
+ def to_flatbuffers
29
+ fb_schema = FB::Schema::Data.new
30
+ fb_schema.endianness = FB::Endianness::LITTLE
31
+ fb_schema.fields = fields.collect(&:to_flatbuffers)
32
+ fb_schema.custom_metadata = FB.build_custom_metadata(@metadata)
33
+ # fb_schema.features = @features
34
+ fb_schema
22
35
  end
23
36
  end
24
37
  end
@@ -16,6 +16,7 @@
16
16
  # under the License.
17
17
 
18
18
  require_relative "array"
19
+ require_relative "dictionary"
19
20
  require_relative "error"
20
21
  require_relative "field"
21
22
  require_relative "readable"
@@ -100,11 +101,23 @@ module ArrowFormat
100
101
  private
101
102
  def consume_initial(target)
102
103
  continuation = target.get_value(CONTINUATION_TYPE, 0)
103
- unless continuation == CONTINUATION_INT32
104
+ if continuation == CONTINUATION_INT32
105
+ @state = :metadata_length
106
+ elsif continuation < 0
104
107
  raise ReadError.new("Invalid continuation token: " +
105
108
  continuation.inspect)
109
+ else
110
+ # For backward compatibility of data produced prior to version
111
+ # 0.15.0. It doesn't have continuation token. Ignore it and
112
+ # re-read it as metadata length.
113
+ metadata_length = continuation
114
+ if metadata_length == 0
115
+ @state = :eos
116
+ else
117
+ @metadata_length = metadata_length
118
+ @state = :metadata
119
+ end
106
120
  end
107
- @state = :metadata_length
108
121
  end
109
122
 
110
123
  def consume_metadata_length(target)
@@ -123,7 +136,7 @@ module ArrowFormat
123
136
 
124
137
  def consume_metadata(target)
125
138
  metadata_buffer = target.slice(0, @metadata_length)
126
- @message = Org::Apache::Arrow::Flatbuf::Message.new(metadata_buffer)
139
+ @message = FB::Message.new(metadata_buffer)
127
140
  @body_length = @message.body_length
128
141
  if @body_length < 0
129
142
  raise ReadError.new("Negative body length: " +
@@ -174,7 +187,7 @@ module ArrowFormat
174
187
  process_schema_message(message, body)
175
188
  when :initial_dictionaries
176
189
  header = message.header
177
- unless header.is_a?(Org::Apache::Arrow::Flatbuf::DictionaryBatch)
190
+ unless header.is_a?(FB::DictionaryBatch)
178
191
  raise ReadError.new("Not a dictionary batch message: " +
179
192
  header.inspect)
180
193
  end
@@ -184,9 +197,9 @@ module ArrowFormat
184
197
  end
185
198
  when :data
186
199
  case message.header
187
- when Org::Apache::Arrow::Flatbuf::DictionaryBatch
200
+ when FB::DictionaryBatch
188
201
  process_dictionary_batch_message(message, body)
189
- when Org::Apache::Arrow::Flatbuf::RecordBatch
202
+ when FB::RecordBatch
190
203
  process_record_batch_message(message, body)
191
204
  end
192
205
  end
@@ -194,17 +207,17 @@ module ArrowFormat
194
207
 
195
208
  def process_schema_message(message, body)
196
209
  header = message.header
197
- unless header.is_a?(Org::Apache::Arrow::Flatbuf::Schema)
210
+ unless header.is_a?(FB::Schema)
198
211
  raise ReadError.new("Not a schema message: " +
199
212
  header.inspect)
200
213
  end
201
214
 
202
- @schema = read_schema(header)
215
+ @schema = read_schema(header, message.custom_metadata)
203
216
  @dictionaries = {}
204
217
  @dictionary_fields = {}
205
218
  @schema.fields.each do |field|
206
219
  next unless field.type.is_a?(DictionaryType)
207
- @dictionary_fields[field.dictionary_id] = field
220
+ @dictionary_fields[field.type.id] = field
208
221
  end
209
222
  if @dictionaries.size < @dictionary_fields.size
210
223
  @state = :initial_dictionaries
@@ -213,31 +226,44 @@ module ArrowFormat
213
226
  end
214
227
  end
215
228
 
216
- def process_dictionary_batch_message(message, body)
217
- header = message.header
218
- if @state == :initial_dictionaries and header.delta?
229
+ def process_dictionary_batch_message(fb_message, body)
230
+ fb_header = fb_message.header
231
+ if @state == :initial_dictionaries and fb_header.delta?
219
232
  raise ReadError.new("An initial dictionary batch message must be " +
220
233
  "a non delta dictionary batch message: " +
221
- header.inspect)
234
+ fb_header.inspect)
222
235
  end
223
- field = @dictionary_fields[header.id]
236
+ field = @dictionary_fields[fb_header.id]
224
237
  value_type = field.type.value_type
225
- schema = Schema.new([Field.new("dummy", value_type, true, nil)])
226
- record_batch = read_record_batch(header.data, schema, body)
227
- if header.delta?
228
- @dictionaries[header.id] << record_batch.columns[0]
238
+ schema = Schema.new([Field.new("dummy", value_type)])
239
+ record_batch = read_record_batch(fb_message.version,
240
+ fb_header.data,
241
+ nil,
242
+ schema,
243
+ body)
244
+ message_metadata = read_custom_metadata(fb_message.custom_metadata)
245
+ dictionary = Dictionary.new(fb_header.id,
246
+ record_batch.columns[0],
247
+ message_metadata: message_metadata)
248
+ if fb_header.delta?
249
+ @dictionaries[fb_header.id] << dictionary
229
250
  else
230
- @dictionaries[header.id] = [record_batch.columns[0]]
251
+ @dictionaries[fb_header.id] = [dictionary]
231
252
  end
232
253
  end
233
254
 
234
- def find_dictionary(id)
255
+ def find_dictionaries(id)
235
256
  @dictionaries[id]
236
257
  end
237
258
 
238
- def process_record_batch_message(message, body)
239
- header = message.header
240
- @on_read.call(read_record_batch(header, @schema, body))
259
+ def process_record_batch_message(fb_message, body)
260
+ fb_header = fb_message.header
261
+ record_batch = read_record_batch(fb_message.version,
262
+ fb_header,
263
+ fb_message.custom_metadata,
264
+ @schema,
265
+ body)
266
+ @on_read.call(record_batch)
241
267
  end
242
268
  end
243
269
  end