red-arrow-format 25.0.1 → 26.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.
@@ -0,0 +1,89 @@
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
+ require_relative "buffer-alignable"
19
+
20
+ module ArrowFormat
21
+ using FlatBuffers::AppendAsBytes if FlatBuffers.const_defined?(:AppendAsBytes)
22
+
23
+ class DenseBitmapBuilder
24
+ include BufferAlignable
25
+
26
+ def initialize
27
+ @buffer = +"".b
28
+ @n_bits = 0
29
+ @byte = 0
30
+ end
31
+
32
+ def append(value)
33
+ @byte |= 1 << @n_bits if value
34
+ @n_bits += 1
35
+ flush if @n_bits == 8
36
+ self
37
+ end
38
+
39
+ def finish
40
+ flush if @n_bits > 0
41
+ pad!(@buffer, buffer_padding_size(@buffer))
42
+ @buffer.freeze
43
+ IO::Buffer.for(@buffer)
44
+ end
45
+
46
+ private
47
+ def flush
48
+ @buffer.append_as_bytes([@byte].pack("C"))
49
+ @n_bits = 0
50
+ @byte = 0
51
+ end
52
+ end
53
+
54
+ class SparseBitmapBuilder
55
+ include BufferAlignable
56
+
57
+ def initialize
58
+ @unset_indexes = {}
59
+ end
60
+
61
+ def unset(index)
62
+ @unset_indexes[index] = true
63
+ end
64
+
65
+ def finish(size)
66
+ builder = DenseBitmapBuilder.new
67
+ set_value = true
68
+ unset_value = false
69
+ if @unset_indexes.empty?
70
+ size.times do
71
+ builder.append(set_value)
72
+ end
73
+ else
74
+ previous_index = 0
75
+ @unset_indexes.keys.sort.each do |index|
76
+ previous_index.upto(index - 1) do
77
+ builder.append(set_value)
78
+ end
79
+ builder.append(unset_value)
80
+ previous_index = index + 1
81
+ end
82
+ (size - previous_index).times do
83
+ builder.append(set_value)
84
+ end
85
+ end
86
+ builder.finish
87
+ end
88
+ end
89
+ end
@@ -1,3 +1,4 @@
1
+ # Licensed to the Apache Software Foundation (ASF) under one
1
2
  # or more contributor license agreements. See the NOTICE file
2
3
  # distributed with this work for additional information
3
4
  # regarding copyright ownership. The ASF licenses this file
@@ -18,23 +19,43 @@ module ArrowFormat
18
19
  class Bitmap
19
20
  include Enumerable
20
21
 
21
- def initialize(buffer, offset, n_values)
22
+ attr_reader :offset
23
+ attr_reader :size
24
+ alias_method :length, :size
25
+ def initialize(buffer, offset, size)
22
26
  @buffer = buffer
23
27
  @offset = offset
24
- @n_values = n_values
28
+ @size = size
29
+ end
30
+
31
+ def ==(other)
32
+ return false unless other.is_a?(self.class)
33
+ return false unless @size == other.size
34
+ lazy.zip(other).all? do |bit, other_bit|
35
+ bit == other_bit
36
+ end
25
37
  end
26
38
 
27
39
  def [](i)
40
+ return false if @buffer.nil?
41
+
28
42
  i += @offset
29
43
  (@buffer.get_value(:U8, i / 8) & (1 << (i % 8))) > 0
30
44
  end
31
45
 
32
46
  def each
33
- return to_enum(__method__) unless block_given?
47
+ return to_enum(__method__) {@size} unless block_given?
48
+
49
+ if @buffer.nil?
50
+ @size.times do
51
+ yield(false)
52
+ end
53
+ return
54
+ end
34
55
 
35
56
  # TODO: Optimize
36
57
  current = -1
37
- n_bytes = (@offset + @n_values) / 8
58
+ n_bytes = (@offset + @size) / 8
38
59
  @buffer.each(:U8, 0, n_bytes) do |offset, value|
39
60
  8.times do |i|
40
61
  current += 1
@@ -42,7 +63,7 @@ module ArrowFormat
42
63
  yield((value & (1 << (i % 8))) > 0)
43
64
  end
44
65
  end
45
- remained_bits = (@offset + @n_values) % 8
66
+ remained_bits = (@offset + @size) % 8
46
67
  unless remained_bits.zero?
47
68
  value = @buffer.get_value(:U8, n_bytes)
48
69
  remained_bits.times do |i|
@@ -1,3 +1,4 @@
1
+ # Licensed to the Apache Software Foundation (ASF) under one
1
2
  # or more contributor license agreements. See the NOTICE file
2
3
  # distributed with this work for additional information
3
4
  # regarding copyright ownership. The ASF licenses this file
@@ -1,3 +1,4 @@
1
+ # Licensed to the Apache Software Foundation (ASF) under one
1
2
  # or more contributor license agreements. See the NOTICE file
2
3
  # distributed with this work for additional information
3
4
  # regarding copyright ownership. The ASF licenses this file
@@ -23,7 +24,8 @@ module ArrowFormat
23
24
  type,
24
25
  nullable: true,
25
26
  metadata: nil)
26
- @name = name
27
+ name = name.to_s if name.is_a?(Symbol)
28
+ @name = name.to_str
27
29
  @type = type
28
30
  @nullable = nullable
29
31
  @metadata = metadata
@@ -1,3 +1,4 @@
1
+ # Licensed to the Apache Software Foundation (ASF) under one
1
2
  # or more contributor license agreements. See the NOTICE file
2
3
  # distributed with this work for additional information
3
4
  # regarding copyright ownership. The ASF licenses this file
@@ -15,6 +16,7 @@
15
16
  # under the License.
16
17
 
17
18
  require_relative "buffer-alignable"
19
+ require_relative "record"
18
20
 
19
21
  module ArrowFormat
20
22
  class RecordBatch
@@ -26,7 +28,19 @@ module ArrowFormat
26
28
  alias_method :length, :n_rows
27
29
  attr_reader :columns
28
30
  attr_reader :message_metadata
29
- def initialize(schema, n_rows, columns, message_metadata: nil)
31
+ def initialize(*args, message_metadata: nil)
32
+ n_args = args.size
33
+ case n_args
34
+ when 1
35
+ args = build(args[0])
36
+ when 2
37
+ args = build_with_schema(*args)
38
+ end
39
+ if args.size != 3
40
+ message = "wrong number of arguments (given #{n_args}, expected 1..3)"
41
+ raise ArgumentError, message
42
+ end
43
+ schema, n_rows, columns = args
30
44
  @schema = schema
31
45
  @n_rows = n_rows
32
46
  @columns = columns
@@ -37,12 +51,43 @@ module ArrowFormat
37
51
  @n_rows.zero?
38
52
  end
39
53
 
40
- def to_h
41
- hash = {}
42
- @schema.fields.zip(@columns) do |field, column|
43
- hash[field.name] = column
54
+ def find_column(name_or_index)
55
+ case name_or_index
56
+ when Integer
57
+ @columns[name_or_index]
58
+ when Symbol
59
+ name_to_column[name_or_index.to_s]
60
+ else
61
+ name_to_column[name_or_index.to_str]
62
+ end
63
+ end
64
+
65
+ def each_record(reuse_record: false)
66
+ unless block_given?
67
+ return to_enum(__method__, reuse_record: reuse_record)
68
+ end
69
+
70
+ if reuse_record
71
+ record = Record.new(self, nil)
72
+ @n_rows.times do |i|
73
+ record.index = i
74
+ yield(record)
75
+ end
76
+ else
77
+ @n_rows.times do |i|
78
+ yield(Record.new(self, i))
79
+ end
80
+ end
81
+ end
82
+
83
+ def records
84
+ size.times.collect do |i|
85
+ Record.new(self, i)
44
86
  end
45
- hash
87
+ end
88
+
89
+ def to_h
90
+ name_to_column.dup
46
91
  end
47
92
 
48
93
  def to_flatbuffers
@@ -101,5 +146,121 @@ module ArrowFormat
101
146
  end
102
147
  end
103
148
  end
149
+
150
+ private
151
+ def build_with_schema(schema, data)
152
+ fields = schema.fields
153
+ name_to_index = {}
154
+ fields.each_with_index do |field, i|
155
+ name_to_index[field.name] = i
156
+ end
157
+ if data.is_a?(Hash)
158
+ raw_columns = []
159
+ data.each do |name, values|
160
+ field_index = name_to_index[name.to_s]
161
+ raw_columns[field_index] = values if field_index
162
+ end
163
+ columns = fields.zip(raw_columns).collect do |field, values|
164
+ field.type.build_array(values || [])
165
+ end
166
+ all_n_rows = columns.collect(&:size)
167
+ if all_n_rows.uniq.size != 1
168
+ message = "inconsistent the number of rows: #{all_n_rows.join(", ")}"
169
+ raise ArgumentError, message
170
+ end
171
+ else
172
+ raw_columns = fields.collect { [] }
173
+ data.each_with_index do |record, nth_record|
174
+ case record
175
+ when nil
176
+ when Hash
177
+ record.each do |name, value|
178
+ field_index = name_to_index[name.to_s]
179
+ raw_columns[field_index] << value if field_index
180
+ end
181
+ else
182
+ if record.size > raw_columns.size
183
+ message = "row #{nth_record} has more values than schema fields"
184
+ raise ArgumentError, message
185
+ end
186
+ record.each_with_index do |value, field_index|
187
+ raw_columns[field_index] << value
188
+ end
189
+ end
190
+ raw_columns.each do |column|
191
+ column << nil if column.size != nth_record + 1
192
+ end
193
+ end
194
+ columns = fields.zip(raw_columns).collect do |field, values|
195
+ field.type.build_array(values)
196
+ end
197
+ end
198
+ n_rows = columns.first&.size || 0
199
+ [schema, n_rows, columns]
200
+ end
201
+
202
+ def build(data)
203
+ records = nil
204
+ fields = []
205
+ columns = []
206
+ mode = nil
207
+ i = 0
208
+ data.each do |name, column|
209
+ if i.zero?
210
+ if column.nil?
211
+ mode = :record
212
+ records = {}
213
+ else
214
+ mode = :column
215
+ end
216
+ end
217
+
218
+ if mode == :record
219
+ record = name
220
+ record.each do |n, value|
221
+ values = (records[n] ||= [])
222
+ while values.size < i
223
+ values << nil
224
+ end
225
+ values << value
226
+ end
227
+ else
228
+ fields << Field.new(name, column.type)
229
+ columns << column
230
+ end
231
+ i += 1
232
+ end
233
+
234
+ if mode == :record
235
+ records.each do |name, values|
236
+ column = Array.build(values)
237
+ fields << Field.new(name, column.type)
238
+ columns << column
239
+ end
240
+ end
241
+
242
+ raise ArgumentError, "no data" if columns.empty?
243
+ all_n_rows = columns.collect(&:size)
244
+ if all_n_rows.uniq.size != 1
245
+ message =
246
+ "inconsistent the number of rows: #{all_n_rows.join(", ")}"
247
+ raise ArgumentError, message
248
+ end
249
+
250
+ return Schema.new(fields), all_n_rows[0], columns
251
+ end
252
+
253
+ def name_to_column
254
+ @name_to_column ||= build_name_to_column
255
+ end
256
+
257
+ def build_name_to_column
258
+ name_to_column = {}
259
+ @schema.fields.zip(@columns) do |field, column|
260
+ name_to_column[field.name] = column
261
+ end
262
+ name_to_column.freeze
263
+ name_to_column
264
+ end
104
265
  end
105
266
  end
@@ -0,0 +1,60 @@
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 Record
20
+ attr_reader :record_batch
21
+ attr_accessor :index
22
+ def initialize(record_batch, index)
23
+ @record_batch = record_batch
24
+ @index = index
25
+ end
26
+
27
+ def [](name_or_index)
28
+ column = @record_batch.find_column(name_or_index)
29
+ return nil if column.nil?
30
+ column[@index]
31
+ end
32
+
33
+ def to_a
34
+ @record_batch.columns.collect do |column|
35
+ column[@index]
36
+ end
37
+ end
38
+
39
+ def to_h
40
+ attributes = {}
41
+ @record_batch.schema.fields.zip(@record_batch.columns) do |field, column|
42
+ attributes[field.name] = column[@index]
43
+ end
44
+ attributes
45
+ end
46
+
47
+ def respond_to_missing?(name, include_private)
48
+ return true if @record_batch.find_column(name)
49
+ super
50
+ end
51
+
52
+ def method_missing(name, *args, &block)
53
+ if args.empty?
54
+ column = @record_batch.find_column(name)
55
+ return column[@index] if column
56
+ end
57
+ super
58
+ end
59
+ end
60
+ end
@@ -1,3 +1,4 @@
1
+ # Licensed to the Apache Software Foundation (ASF) under one
1
2
  # or more contributor license agreements. See the NOTICE file
2
3
  # distributed with this work for additional information
3
4
  # regarding copyright ownership. The ASF licenses this file
@@ -25,6 +26,17 @@ module ArrowFormat
25
26
  @message_metadata = message_metadata
26
27
  end
27
28
 
29
+ def [](name_or_index)
30
+ case name_or_index
31
+ when Integer
32
+ @fields[name_or_index]
33
+ when Symbol
34
+ name_to_field[name_or_index.to_s]
35
+ else
36
+ name_to_field[name_or_index.to_str]
37
+ end
38
+ end
39
+
28
40
  def to_flatbuffers
29
41
  fb_schema = FB::Schema::Data.new
30
42
  fb_schema.endianness = FB::Endianness::LITTLE
@@ -33,5 +45,18 @@ module ArrowFormat
33
45
  # fb_schema.features = @features
34
46
  fb_schema
35
47
  end
48
+
49
+ private
50
+ def name_to_field
51
+ @name_to_field ||= build_name_to_field
52
+ end
53
+
54
+ def build_name_to_field
55
+ name_to_field = {}
56
+ @fields.each do |field|
57
+ name_to_field[field.name] = field
58
+ end
59
+ name_to_field
60
+ end
36
61
  end
37
62
  end