red-flatbuffers 0.0.3 → 0.0.4
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.
- checksums.yaml +4 -4
- data/doc/text/news.md +6 -0
- data/lib/flatbuffers/alignable.rb +42 -0
- data/lib/flatbuffers/append_as_bytes.rb +26 -0
- data/lib/flatbuffers/data_definable.rb +35 -0
- data/lib/flatbuffers/error.rb +21 -0
- data/lib/flatbuffers/field.rb +57 -0
- data/lib/flatbuffers/generator.rb +97 -10
- data/lib/flatbuffers/root_table.rb +57 -0
- data/lib/flatbuffers/serializer.rb +348 -0
- data/lib/flatbuffers/struct.rb +21 -2
- data/lib/flatbuffers/table.rb +48 -10
- data/lib/flatbuffers/union.rb +8 -2
- data/lib/flatbuffers/verifier.rb +54 -0
- data/lib/flatbuffers/version.rb +1 -1
- data/lib/flatbuffers/view.rb +77 -40
- data/lib/flatbuffers.rb +5 -2
- data/red-flatbuffers.gemspec +1 -0
- metadata +12 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 39624c3160429b2d1492aa6bcf66b0d4e8028cb11c2e5e8538a40014bbfaea58
|
|
4
|
+
data.tar.gz: 296cce04665d4cd2095b60e72e052dc650aede2215e9fba0a9dfd0082f64d242
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bc547bfdc66275abebf0160367c293ff5a3ab328fe6d191fc1ab00952fa6f578330dbe987cce19fba954d145a0881f757012b0e34d80ffeeb69c43cbfa341df1
|
|
7
|
+
data.tar.gz: 7436bbce98bb2fc5400a4392eef2cd7d1438c4585e97ec26f193b0aabd7410f0fda7241fd6be84a34712cbe59941e8b65d98de76b5532dedda9b134de551b2a8
|
data/doc/text/news.md
CHANGED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Copyright 2026 Sutou Kouhei <kou@clear-code.com>
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
require_relative "append_as_bytes"
|
|
16
|
+
|
|
17
|
+
module FlatBuffers
|
|
18
|
+
using AppendAsBytes if const_defined?(:AppendAsBytes)
|
|
19
|
+
|
|
20
|
+
module Alignable
|
|
21
|
+
LARGEST_ALIGNMENT_SIZE = 8 # IO::Buffer.size_of(:u64)
|
|
22
|
+
LARGEST_PADDING = "\x00" * 7
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
def compute_padding_size(size, alignment_byte)
|
|
26
|
+
(alignment_byte - (size & (alignment_byte - 1))) & (alignment_byte - 1)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def padding(padding_size)
|
|
30
|
+
LARGEST_PADDING[0, padding_size]
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def pad!(data, padding_size)
|
|
34
|
+
return if padding_size.zero?
|
|
35
|
+
data.append_as_bytes(padding(padding_size))
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def align!(data, alignment_byte)
|
|
39
|
+
pad!(data, compute_padding_size(data.bytesize, alignment_byte))
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Copyright 2026 Sutou Kouhei <kou@clear-code.com>
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
# This is for Ruby < 3.4
|
|
16
|
+
unless String.method_defined?(:append_as_bytes)
|
|
17
|
+
module FlatBuffers
|
|
18
|
+
module AppendAsBytes
|
|
19
|
+
refine String do
|
|
20
|
+
def append_as_bytes(*objects)
|
|
21
|
+
concat(*objects)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Copyright 2026 Sutou Kouhei <kou@clear-code.com>
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
module FlatBuffers
|
|
16
|
+
module DataDefinable
|
|
17
|
+
def define_data_class
|
|
18
|
+
if self::FIELDS.empty?
|
|
19
|
+
klass = Class.new
|
|
20
|
+
else
|
|
21
|
+
klass = ::Struct.new(*self::FIELDS.keys) do
|
|
22
|
+
members.each do |member|
|
|
23
|
+
next unless member.end_with?("?")
|
|
24
|
+
alias_method :"#{member.to_s.delete_suffix("?")}=", :"#{member}="
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
table_class = self
|
|
29
|
+
klass.singleton_class.define_method(:table_class) do
|
|
30
|
+
table_class
|
|
31
|
+
end
|
|
32
|
+
klass
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Copyright 2026 Sutou Kouhei <kou@clear-code.com>
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
module FlatBuffers
|
|
16
|
+
class Error < StandardError
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
class VerificationError < Error
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Copyright 2025-2026 Sutou Kouhei <kou@clear-code.com>
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
require_relative "view"
|
|
16
|
+
|
|
17
|
+
module FlatBuffers
|
|
18
|
+
class Field
|
|
19
|
+
attr_reader :name
|
|
20
|
+
attr_reader :index
|
|
21
|
+
attr_reader :offset
|
|
22
|
+
attr_reader :base_type
|
|
23
|
+
attr_reader :padding
|
|
24
|
+
def initialize(name, index, offset, base_type, padding)
|
|
25
|
+
@name = name
|
|
26
|
+
@index = index
|
|
27
|
+
@offset = offset
|
|
28
|
+
@base_type = base_type
|
|
29
|
+
@padding = padding
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def alignment_size
|
|
33
|
+
case @base_type
|
|
34
|
+
when :utype
|
|
35
|
+
4
|
|
36
|
+
when :bool, :byte, :ubyte
|
|
37
|
+
1
|
|
38
|
+
when :short, :ushort
|
|
39
|
+
2
|
|
40
|
+
when :int, :uint
|
|
41
|
+
4
|
|
42
|
+
when :long, :ulong
|
|
43
|
+
8
|
|
44
|
+
when :float
|
|
45
|
+
4
|
|
46
|
+
when :double
|
|
47
|
+
8
|
|
48
|
+
when :string
|
|
49
|
+
4
|
|
50
|
+
when String
|
|
51
|
+
4
|
|
52
|
+
when Array
|
|
53
|
+
4
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright 2025 Sutou Kouhei <kou@clear-code.com>
|
|
1
|
+
# Copyright 2025-2026 Sutou Kouhei <kou@clear-code.com>
|
|
2
2
|
#
|
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
# you may not use this file except in compliance with the License.
|
|
@@ -338,20 +338,113 @@ module FlatBuffers
|
|
|
338
338
|
start_modules(writer, namespaces)
|
|
339
339
|
|
|
340
340
|
if object.struct?
|
|
341
|
+
is_root_table = false
|
|
341
342
|
parent = "::FlatBuffers::Struct"
|
|
342
343
|
else
|
|
343
|
-
|
|
344
|
+
is_root_table = (object.name == @schema.root_table&.name)
|
|
345
|
+
if is_root_table
|
|
346
|
+
parent = "::FlatBuffers::RootTable"
|
|
347
|
+
else
|
|
348
|
+
parent = "::FlatBuffers::Table"
|
|
349
|
+
end
|
|
344
350
|
end
|
|
345
351
|
generate_documentation(writer, object.documentation)
|
|
346
352
|
writer << "class #{to_class_name(name)} < #{parent}"
|
|
347
353
|
writer.indent
|
|
348
354
|
|
|
349
|
-
|
|
355
|
+
if is_root_table
|
|
356
|
+
writer << "class << self"
|
|
357
|
+
writer.indent
|
|
358
|
+
|
|
359
|
+
writer << "def file_identifier"
|
|
360
|
+
writer.indent
|
|
361
|
+
writer << to_ruby_code(@schema.file_ident)
|
|
362
|
+
writer.end
|
|
363
|
+
writer << ""
|
|
364
|
+
writer << "def file_extension"
|
|
365
|
+
writer.indent
|
|
366
|
+
writer << to_ruby_code(@schema.file_ext)
|
|
367
|
+
writer.end
|
|
368
|
+
|
|
369
|
+
writer.end
|
|
370
|
+
writer << ""
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
generate_object_fields(writer, object)
|
|
374
|
+
|
|
375
|
+
writer << "Data = define_data_class"
|
|
376
|
+
|
|
377
|
+
generate_object_methods(writer, object, namespaces)
|
|
378
|
+
|
|
379
|
+
writer.end # class
|
|
380
|
+
|
|
381
|
+
end_modules(writer, namespaces)
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
def generate_object_fields(writer, object)
|
|
385
|
+
writer << "FIELDS = {"
|
|
386
|
+
writer.indent
|
|
387
|
+
|
|
388
|
+
offset_sorted_fields = object.fields&.sort_by(&:offset)
|
|
389
|
+
offset_sorted_fields&.each_with_index do |field, index|
|
|
390
|
+
# Skip writing deprecated fields altogether.
|
|
391
|
+
next if field.deprecated?
|
|
392
|
+
|
|
393
|
+
method_name = to_method_name(field.name)
|
|
394
|
+
type = field.type
|
|
395
|
+
base_type = type.base_type
|
|
396
|
+
if base_type == Reflection::BaseType::BOOL
|
|
397
|
+
method_name = "#{method_name}?".delete_prefix("is_")
|
|
398
|
+
end
|
|
399
|
+
ruby_index = to_ruby_code(index)
|
|
400
|
+
ruby_offset = to_ruby_code(field.offset)
|
|
401
|
+
case base_type
|
|
402
|
+
when Reflection::BaseType::OBJ
|
|
403
|
+
object = @schema.objects[type.index]
|
|
404
|
+
*object_namespaces, object_name = denamespace(object.name)
|
|
405
|
+
klass = to_absolute_class_name(@outer_namespaces,
|
|
406
|
+
object_namespaces,
|
|
407
|
+
object_name)
|
|
408
|
+
ruby_base_type = to_ruby_code(klass)
|
|
409
|
+
when Reflection::BaseType::UNION
|
|
410
|
+
union = @schema.enums[type.index]
|
|
411
|
+
*union_namespaces, union_name = denamespace(union.name)
|
|
412
|
+
klass = to_absolute_class_name(@outer_namespaces,
|
|
413
|
+
union_namespaces,
|
|
414
|
+
union_name)
|
|
415
|
+
ruby_base_type = to_ruby_code(klass)
|
|
416
|
+
when Reflection::BaseType::VECTOR
|
|
417
|
+
element_base_type = type.element
|
|
418
|
+
if element_base_type == Reflection::BaseType::OBJ
|
|
419
|
+
object = @schema.objects[type.index]
|
|
420
|
+
*object_namespaces, object_name = denamespace(object.name)
|
|
421
|
+
klass = to_absolute_class_name(@outer_namespaces,
|
|
422
|
+
object_namespaces,
|
|
423
|
+
object_name)
|
|
424
|
+
ruby_base_type = "[#{to_ruby_code(klass)}]"
|
|
425
|
+
else
|
|
426
|
+
ruby_base_type = "[:#{to_lower_snake_case(element_base_type.name)}]"
|
|
427
|
+
end
|
|
428
|
+
else
|
|
429
|
+
ruby_base_type = ":#{to_lower_snake_case(base_type.name)}"
|
|
430
|
+
end
|
|
431
|
+
ruby_padding = to_ruby_code(field.padding)
|
|
432
|
+
writer << ("#{method_name}: ::FlatBuffers::Field.new(" +
|
|
433
|
+
":#{method_name}, #{ruby_index}, " +
|
|
434
|
+
"#{ruby_offset}, #{ruby_base_type}, #{ruby_padding}),")
|
|
435
|
+
end
|
|
436
|
+
|
|
437
|
+
writer.unindent
|
|
438
|
+
writer << "}"
|
|
439
|
+
writer << ""
|
|
440
|
+
end
|
|
441
|
+
|
|
442
|
+
def generate_object_methods(writer, object, namespaces)
|
|
350
443
|
object.fields&.each do |field|
|
|
351
444
|
# Skip writing deprecated fields altogether.
|
|
352
445
|
next if field.deprecated?
|
|
353
446
|
|
|
354
|
-
writer << ""
|
|
447
|
+
writer << ""
|
|
355
448
|
|
|
356
449
|
method_name = to_method_name(field.name)
|
|
357
450
|
type = field.type
|
|
@@ -471,13 +564,7 @@ module FlatBuffers
|
|
|
471
564
|
writer.end
|
|
472
565
|
end
|
|
473
566
|
writer.end
|
|
474
|
-
|
|
475
|
-
n_processed_fields += 1
|
|
476
567
|
end
|
|
477
|
-
|
|
478
|
-
writer.end # class
|
|
479
|
-
|
|
480
|
-
end_modules(writer, namespaces)
|
|
481
568
|
end
|
|
482
569
|
|
|
483
570
|
def generate_documentation(writer, documentation)
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Copyright 2025-2026 Sutou Kouhei <kou@clear-code.com>
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
require_relative "serializer"
|
|
16
|
+
require_relative "table"
|
|
17
|
+
require_relative "view"
|
|
18
|
+
|
|
19
|
+
module FlatBuffers
|
|
20
|
+
class RootTable < Table
|
|
21
|
+
class << self
|
|
22
|
+
def save(base_name, data)
|
|
23
|
+
name = "#{base_name}.#{file_extension}"
|
|
24
|
+
File.open(name, "wb") do |output|
|
|
25
|
+
output.print(serialize(data))
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def serialize(data, table_serializer=nil)
|
|
30
|
+
if table_serializer
|
|
31
|
+
# Referenced table
|
|
32
|
+
super(data, table_serializer)
|
|
33
|
+
else
|
|
34
|
+
# Root table
|
|
35
|
+
serializer = Serializer.new(file_identifier)
|
|
36
|
+
serializer.start_table do |table_serializer|
|
|
37
|
+
super(data, table_serializer)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def initialize(input)
|
|
44
|
+
if input.is_a?(View)
|
|
45
|
+
# Referenced table
|
|
46
|
+
super
|
|
47
|
+
else
|
|
48
|
+
# Root table
|
|
49
|
+
if input.is_a?(String)
|
|
50
|
+
input = IO::Buffer.for(input)
|
|
51
|
+
end
|
|
52
|
+
offset = input.get_value(:u32, 0)
|
|
53
|
+
super(View.new(input, offset, have_vtable: true))
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
# Copyright 2026 Sutou Kouhei <kou@clear-code.com>
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
require_relative "alignable"
|
|
16
|
+
|
|
17
|
+
module FlatBuffers
|
|
18
|
+
using AppendAsBytes if const_defined?(:AppendAsBytes)
|
|
19
|
+
|
|
20
|
+
class Serializer
|
|
21
|
+
module Packable
|
|
22
|
+
private
|
|
23
|
+
def pack_value(base_type, value)
|
|
24
|
+
case base_type
|
|
25
|
+
when :bool
|
|
26
|
+
[value ? 1 : 0].pack("c")
|
|
27
|
+
when :utype
|
|
28
|
+
[value || 0].pack("C")
|
|
29
|
+
when :byte
|
|
30
|
+
[value || 0].pack("c")
|
|
31
|
+
when :ubyte
|
|
32
|
+
[value || 0].pack("C")
|
|
33
|
+
when :short
|
|
34
|
+
[value || 0].pack("s<")
|
|
35
|
+
when :ushort
|
|
36
|
+
[value || 0].pack("S<")
|
|
37
|
+
when :int
|
|
38
|
+
[value || 0].pack("l<")
|
|
39
|
+
when :uint
|
|
40
|
+
[value || 0].pack("L<")
|
|
41
|
+
when :long
|
|
42
|
+
[value || 0].pack("q<")
|
|
43
|
+
when :ulong
|
|
44
|
+
[value || 0].pack("Q<")
|
|
45
|
+
when :float
|
|
46
|
+
[value || 0.0].pack("e")
|
|
47
|
+
when :double
|
|
48
|
+
[value || 0.0].pack("E")
|
|
49
|
+
when :string
|
|
50
|
+
value ||= ""
|
|
51
|
+
packed_value = [value.bytesize].pack("L<")
|
|
52
|
+
packed_value.append_as_bytes(value)
|
|
53
|
+
packed_value.append_as_bytes("\x00")
|
|
54
|
+
packed_value
|
|
55
|
+
when String
|
|
56
|
+
klass = Object.const_get(base_type)
|
|
57
|
+
if klass < Struct
|
|
58
|
+
sub_struct_serializer = StructSerializer.new(+"".b)
|
|
59
|
+
klass.serialize(value, sub_struct_serializer)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
class StructSerializer
|
|
66
|
+
include Alignable
|
|
67
|
+
include Packable
|
|
68
|
+
|
|
69
|
+
def initialize(buffer)
|
|
70
|
+
@buffer = buffer
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def start
|
|
74
|
+
yield
|
|
75
|
+
finish
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def add_field(field, value)
|
|
79
|
+
packed_value = pack_value(field.base_type, value)
|
|
80
|
+
@buffer.append_as_bytes(packed_value)
|
|
81
|
+
unless field.padding.zero?
|
|
82
|
+
pad!(@buffer, field.padding)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def finish
|
|
87
|
+
@buffer
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
class TableSerializer
|
|
92
|
+
include Alignable
|
|
93
|
+
include Packable
|
|
94
|
+
|
|
95
|
+
DUMMY_OFFSET = [0].pack("L<")
|
|
96
|
+
|
|
97
|
+
def initialize
|
|
98
|
+
@field_metadata = {}
|
|
99
|
+
# vtable_offset. This is replaced later.
|
|
100
|
+
@table = DUMMY_OFFSET.dup
|
|
101
|
+
@values = +"".b
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def start
|
|
105
|
+
yield
|
|
106
|
+
finish
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def add_field(field, value)
|
|
110
|
+
if value.nil?
|
|
111
|
+
@field_metadata[field] = {
|
|
112
|
+
inline: true,
|
|
113
|
+
offset: nil,
|
|
114
|
+
}
|
|
115
|
+
else
|
|
116
|
+
case field.base_type
|
|
117
|
+
when String
|
|
118
|
+
add_field_object(field, value)
|
|
119
|
+
when Array
|
|
120
|
+
add_field_array(field, value)
|
|
121
|
+
when :string
|
|
122
|
+
align!(@table, View::OFFSET_SIZE)
|
|
123
|
+
@field_metadata[field] = {
|
|
124
|
+
inline: false,
|
|
125
|
+
table_offset: @table.bytesize,
|
|
126
|
+
value_offset: @values.bytesize,
|
|
127
|
+
}
|
|
128
|
+
@values.append_as_bytes(pack_value(field.base_type, value))
|
|
129
|
+
@table.append_as_bytes(DUMMY_OFFSET) # Replaced later
|
|
130
|
+
else
|
|
131
|
+
align!(@table, field.alignment_size)
|
|
132
|
+
@field_metadata[field] = {
|
|
133
|
+
inline: true,
|
|
134
|
+
offset: @table.bytesize,
|
|
135
|
+
}
|
|
136
|
+
@table.append_as_bytes(pack_value(field.base_type, value))
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def finish
|
|
142
|
+
vtable_size = View::VTable.compute_size(@field_metadata.size)
|
|
143
|
+
align!(@table, LARGEST_ALIGNMENT_SIZE)
|
|
144
|
+
table_size = @table.bytesize
|
|
145
|
+
|
|
146
|
+
field_offsets = []
|
|
147
|
+
@field_metadata.each do |field, metadata|
|
|
148
|
+
if metadata[:inline] # Scalar or struct
|
|
149
|
+
offset = metadata[:offset]
|
|
150
|
+
if offset.nil?
|
|
151
|
+
field_offsets[field.index] = 0
|
|
152
|
+
else
|
|
153
|
+
field_offsets[field.index] = offset
|
|
154
|
+
end
|
|
155
|
+
else # Table, string or vector.
|
|
156
|
+
# |vtable|@table|@values|
|
|
157
|
+
#
|
|
158
|
+
# `vtable:` |vtable_size|table_size|field_offsets|
|
|
159
|
+
# `field_offsets` uses relative offset from the start of
|
|
160
|
+
# `vtable`.
|
|
161
|
+
#
|
|
162
|
+
# `@table`: |vtable_offset|fields|
|
|
163
|
+
# `fields` uses relative offset from itself.
|
|
164
|
+
|
|
165
|
+
# The offset in `fields`. This is relative from the start
|
|
166
|
+
# of `@table`.
|
|
167
|
+
table_offset = metadata[:table_offset]
|
|
168
|
+
# The offset in `@values`. This is relative from the start
|
|
169
|
+
# of `@table`.
|
|
170
|
+
value_offset = metadata[:value_offset]
|
|
171
|
+
# `@values` is placed just after `@table`.
|
|
172
|
+
# `offset` is relative from `table_offset`.
|
|
173
|
+
offset = table_size - table_offset + value_offset
|
|
174
|
+
@table[table_offset, View::Table::VTABLE_OFFSET_SIZE] =
|
|
175
|
+
[offset].pack("L<")
|
|
176
|
+
field_offsets[field.index] = table_offset
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
field_offsets.each_with_index do |offset, i|
|
|
180
|
+
field_offsets[i] = 0 if offset.nil?
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
data = +"".b
|
|
184
|
+
vtable = View::VTable.serialize(vtable_size,
|
|
185
|
+
table_size,
|
|
186
|
+
field_offsets)
|
|
187
|
+
data.append_as_bytes(vtable)
|
|
188
|
+
# We don't need "-" here because vtable_offset is subtracted
|
|
189
|
+
# (not added).
|
|
190
|
+
vtable_offset = vtable_size
|
|
191
|
+
@table[0, View::Table::VTABLE_OFFSET_SIZE] = [vtable_offset].pack("l<")
|
|
192
|
+
data.append_as_bytes(@table)
|
|
193
|
+
align!(@values, LARGEST_ALIGNMENT_SIZE)
|
|
194
|
+
data.append_as_bytes(@values)
|
|
195
|
+
table_offset = vtable_size
|
|
196
|
+
[data, table_offset]
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
private
|
|
200
|
+
# Struct, union or table
|
|
201
|
+
def add_field_object(field, value)
|
|
202
|
+
align!(@table, View::OFFSET_SIZE)
|
|
203
|
+
klass = Object.const_get(field.base_type)
|
|
204
|
+
if klass < Struct
|
|
205
|
+
@field_metadata[field] = {
|
|
206
|
+
inline: true,
|
|
207
|
+
offset: @table.bytesize,
|
|
208
|
+
}
|
|
209
|
+
sub_struct_serializer = StructSerializer.new(@table)
|
|
210
|
+
klass.serialize(value, sub_struct_serializer)
|
|
211
|
+
elsif klass < Union
|
|
212
|
+
align!(@values, LARGEST_ALIGNMENT_SIZE)
|
|
213
|
+
sub_table_serializer = TableSerializer.new
|
|
214
|
+
sub_table_data, sub_table_offset =
|
|
215
|
+
value.class.table_class.serialize(value, sub_table_serializer)
|
|
216
|
+
@field_metadata[field] = {
|
|
217
|
+
inline: false,
|
|
218
|
+
table_offset: @table.bytesize,
|
|
219
|
+
value_offset: @values.bytesize + sub_table_offset,
|
|
220
|
+
}
|
|
221
|
+
@values.append_as_bytes(sub_table_data)
|
|
222
|
+
@table.append_as_bytes(DUMMY_OFFSET) # Replaced later
|
|
223
|
+
else
|
|
224
|
+
align!(@values, LARGEST_ALIGNMENT_SIZE)
|
|
225
|
+
sub_table_serializer = TableSerializer.new
|
|
226
|
+
sub_table_data, sub_table_offset =
|
|
227
|
+
klass.serialize(value, sub_table_serializer)
|
|
228
|
+
@field_metadata[field] = {
|
|
229
|
+
inline: false,
|
|
230
|
+
table_offset: @table.bytesize,
|
|
231
|
+
value_offset: @values.bytesize + sub_table_offset,
|
|
232
|
+
}
|
|
233
|
+
@values.append_as_bytes(sub_table_data)
|
|
234
|
+
@table.append_as_bytes(DUMMY_OFFSET) # Replaced later
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def add_field_array(field, value)
|
|
239
|
+
align!(@table, View::OFFSET_SIZE)
|
|
240
|
+
align!(@values, View::OFFSET_SIZE)
|
|
241
|
+
value_offset = @values.bytesize
|
|
242
|
+
# The number of elements.
|
|
243
|
+
@values.append_as_bytes([value.size].pack("L<"))
|
|
244
|
+
element_base_type = field.base_type[0]
|
|
245
|
+
case element_base_type
|
|
246
|
+
when String
|
|
247
|
+
klass = Object.const_get(element_base_type)
|
|
248
|
+
if klass < Struct
|
|
249
|
+
value.each do |v|
|
|
250
|
+
sub_struct_serializer = StructSerializer.new(@values)
|
|
251
|
+
klass.serialize(v, sub_struct_serializer)
|
|
252
|
+
end
|
|
253
|
+
else
|
|
254
|
+
offset_base = @values.bytesize
|
|
255
|
+
# Placeholder for offsets.
|
|
256
|
+
value.size.times do
|
|
257
|
+
@values.append_as_bytes(DUMMY_OFFSET) # Replaced later
|
|
258
|
+
end
|
|
259
|
+
align!(@values, LARGEST_ALIGNMENT_SIZE)
|
|
260
|
+
value.each do |v|
|
|
261
|
+
sub_table_serializer = TableSerializer.new
|
|
262
|
+
sub_table_data, sub_table_offset =
|
|
263
|
+
klass.serialize(v, sub_table_serializer)
|
|
264
|
+
|
|
265
|
+
element_offset = @values.bytesize + sub_table_offset
|
|
266
|
+
# Update offset placeholder.
|
|
267
|
+
relative_element_offset = element_offset - offset_base
|
|
268
|
+
@values[offset_base, View::OFFSET_SIZE] =
|
|
269
|
+
[relative_element_offset].pack("L<")
|
|
270
|
+
offset_base += View::OFFSET_SIZE
|
|
271
|
+
|
|
272
|
+
@values.append_as_bytes(sub_table_data)
|
|
273
|
+
end
|
|
274
|
+
end
|
|
275
|
+
@field_metadata[field] = {
|
|
276
|
+
inline: false,
|
|
277
|
+
table_offset: @table.bytesize,
|
|
278
|
+
value_offset: value_offset,
|
|
279
|
+
}
|
|
280
|
+
when :string
|
|
281
|
+
offset_base = @values.bytesize
|
|
282
|
+
# Placeholder for offsets.
|
|
283
|
+
value.size.times do
|
|
284
|
+
@values.append_as_bytes(DUMMY_OFFSET) # Replaced later
|
|
285
|
+
end
|
|
286
|
+
value.each do |v|
|
|
287
|
+
packed_value = pack_value(element_base_type, v)
|
|
288
|
+
|
|
289
|
+
element_offset = @values.bytesize
|
|
290
|
+
# Update offset placeholder.
|
|
291
|
+
relative_element_offset = element_offset - offset_base
|
|
292
|
+
@values[offset_base, View::OFFSET_SIZE] =
|
|
293
|
+
[relative_element_offset].pack("L<")
|
|
294
|
+
offset_base += View::OFFSET_SIZE
|
|
295
|
+
|
|
296
|
+
@values.append_as_bytes(packed_value)
|
|
297
|
+
end
|
|
298
|
+
@field_metadata[field] = {
|
|
299
|
+
inline: false,
|
|
300
|
+
table_offset: @table.bytesize,
|
|
301
|
+
value_offset: value_offset,
|
|
302
|
+
}
|
|
303
|
+
else
|
|
304
|
+
value.each do |v|
|
|
305
|
+
packed_value = pack_value(element_base_type, v)
|
|
306
|
+
@values.append_as_bytes(packed_value)
|
|
307
|
+
end
|
|
308
|
+
@field_metadata[field] = {
|
|
309
|
+
inline: false,
|
|
310
|
+
table_offset: @table.bytesize,
|
|
311
|
+
value_offset: value_offset,
|
|
312
|
+
}
|
|
313
|
+
end
|
|
314
|
+
@table.append_as_bytes(DUMMY_OFFSET) # Replaced later
|
|
315
|
+
end
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
include Alignable
|
|
319
|
+
|
|
320
|
+
def initialize(identifier)
|
|
321
|
+
identifier = nil if identifier.is_a?(String) and identifier.empty?
|
|
322
|
+
validate_identifier(identifier)
|
|
323
|
+
@identifier = identifier
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
def start_table
|
|
327
|
+
table_serializer = TableSerializer.new
|
|
328
|
+
table, table_offset = yield(table_serializer)
|
|
329
|
+
header_size = View::OFFSET_SIZE
|
|
330
|
+
header_size += View::IDENTIFIER_SIZE if @identifier
|
|
331
|
+
header_size += compute_padding_size(header_size, LARGEST_ALIGNMENT_SIZE)
|
|
332
|
+
root_table_offset = header_size + table_offset
|
|
333
|
+
data = [root_table_offset].pack("L<")
|
|
334
|
+
data.append_as_bytes(@identifier) if @identifier
|
|
335
|
+
align!(data, LARGEST_ALIGNMENT_SIZE)
|
|
336
|
+
data.append_as_bytes(table)
|
|
337
|
+
data
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
private
|
|
341
|
+
def validate_identifier(identifier)
|
|
342
|
+
return if identifier.nil?
|
|
343
|
+
return if identifier.bytesize == View::IDENTIFIER_SIZE
|
|
344
|
+
raise ArgumentError,
|
|
345
|
+
"Identifier must be nil or 4 bytes string: #{identifier.inspect}"
|
|
346
|
+
end
|
|
347
|
+
end
|
|
348
|
+
end
|
data/lib/flatbuffers/struct.rb
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright 2025 Sutou Kouhei <kou@clear-code.com>
|
|
1
|
+
# Copyright 2025-2026 Sutou Kouhei <kou@clear-code.com>
|
|
2
2
|
#
|
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
# you may not use this file except in compliance with the License.
|
|
@@ -12,15 +12,34 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
|
+
require_relative "data_definable"
|
|
15
16
|
require_relative "inspectable"
|
|
16
|
-
require_relative "view"
|
|
17
17
|
|
|
18
18
|
module FlatBuffers
|
|
19
19
|
class Struct
|
|
20
|
+
extend DataDefinable
|
|
20
21
|
include Inspectable
|
|
21
22
|
|
|
23
|
+
class << self
|
|
24
|
+
def serialize(data, struct_serializer)
|
|
25
|
+
struct_serializer.start do
|
|
26
|
+
self::FIELDS.each do |name, field|
|
|
27
|
+
value = data&.public_send(name)
|
|
28
|
+
struct_serializer.add_field(field, value)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
22
34
|
def initialize(view)
|
|
23
35
|
@view = view
|
|
24
36
|
end
|
|
37
|
+
|
|
38
|
+
def ==(other)
|
|
39
|
+
return false unless other.is_a?(self.class)
|
|
40
|
+
self.class::FIELDS.keys.all? do |name|
|
|
41
|
+
public_send(name) == other.public_send(name)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
25
44
|
end
|
|
26
45
|
end
|
data/lib/flatbuffers/table.rb
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright 2025 Sutou Kouhei <kou@clear-code.com>
|
|
1
|
+
# Copyright 2025-2026 Sutou Kouhei <kou@clear-code.com>
|
|
2
2
|
#
|
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
# you may not use this file except in compliance with the License.
|
|
@@ -12,22 +12,60 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
|
+
require_relative "data_definable"
|
|
16
|
+
require_relative "field"
|
|
15
17
|
require_relative "inspectable"
|
|
16
|
-
require_relative "view"
|
|
17
18
|
|
|
18
19
|
module FlatBuffers
|
|
19
20
|
class Table
|
|
20
21
|
include Inspectable
|
|
22
|
+
extend DataDefinable
|
|
21
23
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
class << self
|
|
25
|
+
def table_class
|
|
26
|
+
self
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def serialize(data, table_serializer)
|
|
30
|
+
table_serializer.start do
|
|
31
|
+
self::FIELDS.each do |name, field|
|
|
32
|
+
if field.base_type == :utype
|
|
33
|
+
union_field_name = :"#{name.to_s.delete_suffix("_type")}"
|
|
34
|
+
union_field = self::FIELDS[union_field_name]
|
|
35
|
+
union_value = data.public_send(union_field_name)
|
|
36
|
+
if union_value.nil?
|
|
37
|
+
value = nil
|
|
38
|
+
else
|
|
39
|
+
union_class = Object.const_get(union_field.base_type)
|
|
40
|
+
value = union_class.try_convert(union_value.class.table_class)
|
|
41
|
+
end
|
|
42
|
+
else
|
|
43
|
+
value = data.public_send(name)
|
|
44
|
+
end
|
|
45
|
+
table_serializer.add_field(field, value)
|
|
46
|
+
end
|
|
28
47
|
end
|
|
29
|
-
|
|
30
|
-
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def initialize(view)
|
|
52
|
+
unless view.is_a?(View)
|
|
53
|
+
# For backward compatibility
|
|
54
|
+
if view.is_a?(IO::Buffer)
|
|
55
|
+
buffer = view
|
|
56
|
+
else
|
|
57
|
+
buffer = IO::Buffer.for(view)
|
|
58
|
+
end
|
|
59
|
+
offset = buffer.get_value(:u32, 0)
|
|
60
|
+
view = View.new(buffer, offset, have_vtable: true)
|
|
61
|
+
end
|
|
62
|
+
@view = view
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def ==(other)
|
|
66
|
+
return false unless other.is_a?(self.class)
|
|
67
|
+
self.class::FIELDS.keys.all? do |name|
|
|
68
|
+
public_send(name) == other.public_send(name)
|
|
31
69
|
end
|
|
32
70
|
end
|
|
33
71
|
end
|
data/lib/flatbuffers/union.rb
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright 2025 Sutou Kouhei <kou@clear-code.com>
|
|
1
|
+
# Copyright 2025-2026 Sutou Kouhei <kou@clear-code.com>
|
|
2
2
|
#
|
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
# you may not use this file except in compliance with the License.
|
|
@@ -24,7 +24,13 @@ module FlatBuffers
|
|
|
24
24
|
when self
|
|
25
25
|
value
|
|
26
26
|
else
|
|
27
|
-
|
|
27
|
+
if value < Table
|
|
28
|
+
@name_to_union.values.find do |union|
|
|
29
|
+
union.table_class == value
|
|
30
|
+
end
|
|
31
|
+
else
|
|
32
|
+
nil
|
|
33
|
+
end
|
|
28
34
|
end
|
|
29
35
|
end
|
|
30
36
|
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Copyright 2026 Sutou Kouhei <kou@clear-code.com>
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
require_relative "append_as_bytes"
|
|
16
|
+
|
|
17
|
+
module FlatBuffers
|
|
18
|
+
class Verifier
|
|
19
|
+
def initialize(target)
|
|
20
|
+
@target = target
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def verify
|
|
24
|
+
view = @target.instance_variable_get(:@view)
|
|
25
|
+
@target.class::FIELDS.each_value do |field|
|
|
26
|
+
offset = view.offset + view.unpack_virtual_offset(field.offset)
|
|
27
|
+
verify_alignment(field, offset)
|
|
28
|
+
case field.base_type
|
|
29
|
+
when String
|
|
30
|
+
sub_target = @target.public_send(field.name)
|
|
31
|
+
next if sub_target.nil?
|
|
32
|
+
next if sub_target.is_a?(Struct)
|
|
33
|
+
Verifier.new(sub_target).verify
|
|
34
|
+
when Array
|
|
35
|
+
next unless field.base_type[0].is_a?(String)
|
|
36
|
+
@target.public_send(field.name)&.each do |value|
|
|
37
|
+
next if value.is_a?(Struct)
|
|
38
|
+
Verifier.new(value).verify
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
def verify_alignment(field, offset)
|
|
46
|
+
unless (offset % field.alignment_size).zero?
|
|
47
|
+
message = "#{@target.class.name}'s #{field.name} " +
|
|
48
|
+
"(#{field.base_type}) value isn't aligned: #{offset}: " +
|
|
49
|
+
"#{field.alignment_size}"
|
|
50
|
+
raise VerificationError.new(message)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
data/lib/flatbuffers/version.rb
CHANGED
data/lib/flatbuffers/view.rb
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright 2025 Sutou Kouhei <kou@clear-code.com>
|
|
1
|
+
# Copyright 2025-2026 Sutou Kouhei <kou@clear-code.com>
|
|
2
2
|
#
|
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
# you may not use this file except in compliance with the License.
|
|
@@ -12,11 +12,78 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
|
+
require_relative "alignable"
|
|
16
|
+
|
|
15
17
|
module FlatBuffers
|
|
18
|
+
using AppendAsBytes if const_defined?(:AppendAsBytes)
|
|
19
|
+
|
|
16
20
|
class View
|
|
17
|
-
|
|
18
|
-
|
|
21
|
+
OFFSET_SIZE = 4 # IO::Buffer.size_of(:u32)
|
|
22
|
+
VIRTUAL_OFFSET_SIZE = 2 # IO::Buffer.size_of(:u16)
|
|
23
|
+
IDENTIFIER_SIZE = 4
|
|
24
|
+
|
|
25
|
+
module VTable
|
|
26
|
+
# https://flatbuffers.dev/internals/#tables
|
|
27
|
+
#
|
|
28
|
+
# vtable_size: voffset_t (== uint16_t)
|
|
29
|
+
# table_size: voffset_t (== uint16_t)
|
|
30
|
+
# field_offsets: [voffset_t (== uint16_t)]
|
|
31
|
+
|
|
32
|
+
VTABLE_SIZE_SIZE = VIRTUAL_OFFSET_SIZE
|
|
33
|
+
TABLE_SIZE_SIZE = VIRTUAL_OFFSET_SIZE
|
|
34
|
+
|
|
35
|
+
class << self
|
|
36
|
+
include Alignable
|
|
37
|
+
|
|
38
|
+
def compute_size(n_fields)
|
|
39
|
+
size = VTABLE_SIZE_SIZE +
|
|
40
|
+
TABLE_SIZE_SIZE +
|
|
41
|
+
(VIRTUAL_OFFSET_SIZE * n_fields)
|
|
42
|
+
size += compute_padding_size(size, LARGEST_ALIGNMENT_SIZE)
|
|
43
|
+
size
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def compute_field_index(offset)
|
|
47
|
+
# offset includes vtable_size and table_size.
|
|
48
|
+
field_offset = (offset - VTABLE_SIZE_SIZE - TABLE_SIZE_SIZE)
|
|
49
|
+
field_offset / VIRTUAL_OFFSET_SIZE
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def serialize(vtable_size, table_size, field_offsets)
|
|
53
|
+
data = [vtable_size, table_size, *field_offsets].pack("S<*")
|
|
54
|
+
align!(data, LARGEST_ALIGNMENT_SIZE)
|
|
55
|
+
data
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
module Table
|
|
61
|
+
# https://flatbuffers.dev/internals/#tables
|
|
62
|
+
#
|
|
63
|
+
# vtable_offset: soffset_t (== int32_t)
|
|
64
|
+
# fields: []
|
|
65
|
+
|
|
66
|
+
VTABLE_OFFSET_SIZE = OFFSET_SIZE
|
|
67
|
+
|
|
68
|
+
class << self
|
|
69
|
+
include Alignable
|
|
19
70
|
|
|
71
|
+
def compute_size(fields_size)
|
|
72
|
+
size = VTABLE_OFFSET_SIZE + fields_size
|
|
73
|
+
size += compute_padding_size(size, LARGEST_ALIGNMENT_SIZE)
|
|
74
|
+
size
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def serialize(vtable_offset, fields)
|
|
78
|
+
data = [vtable_offset].pack("l<")
|
|
79
|
+
data.append_as_bytes(fields)
|
|
80
|
+
align!(data, LARGEST_ALIGNMENT_SIZE)
|
|
81
|
+
data
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
attr_reader :offset
|
|
20
87
|
def initialize(data, offset, have_vtable: false)
|
|
21
88
|
@data = data
|
|
22
89
|
@offset = offset
|
|
@@ -24,15 +91,15 @@ module FlatBuffers
|
|
|
24
91
|
vtable_offset = unpack_signed_offset(0)
|
|
25
92
|
@vtable_start = @offset - vtable_offset
|
|
26
93
|
# We assume vtable must have length.
|
|
27
|
-
@vtable_max_offset =
|
|
28
|
-
@
|
|
29
|
-
@vtable_max_offset = @
|
|
30
|
-
@
|
|
94
|
+
@vtable_max_offset = VTable::VTABLE_SIZE_SIZE
|
|
95
|
+
@vtable_size = unpack_virtual_offset(0)
|
|
96
|
+
@vtable_max_offset = @vtable_size
|
|
97
|
+
@table_size = unpack_virtual_offset(VTable::TABLE_SIZE_SIZE)
|
|
31
98
|
end
|
|
32
99
|
end
|
|
33
100
|
|
|
34
101
|
def unpack_virtual_offset(vtable_offset)
|
|
35
|
-
return 0 if vtable_offset > @vtable_max_offset
|
|
102
|
+
return 0 if (vtable_offset + VIRTUAL_OFFSET_SIZE > @vtable_max_offset)
|
|
36
103
|
@data.get_value(:u16, @vtable_start + vtable_offset)
|
|
37
104
|
end
|
|
38
105
|
|
|
@@ -103,7 +170,7 @@ module FlatBuffers
|
|
|
103
170
|
def unpack_string(offset)
|
|
104
171
|
value_offset = resolve_indirect(offset)
|
|
105
172
|
length = unpack_offset_raw(value_offset)
|
|
106
|
-
@data.get_string(value_offset +
|
|
173
|
+
@data.get_string(value_offset + OFFSET_SIZE,
|
|
107
174
|
length)
|
|
108
175
|
end
|
|
109
176
|
|
|
@@ -135,42 +202,12 @@ module FlatBuffers
|
|
|
135
202
|
length = unpack_offset(relative_vector_offset)
|
|
136
203
|
return nil if length.zero?
|
|
137
204
|
|
|
138
|
-
relative_vector_body_offset = relative_vector_offset +
|
|
205
|
+
relative_vector_body_offset = relative_vector_offset + OFFSET_SIZE
|
|
139
206
|
length.times.collect do |i|
|
|
140
207
|
relative_element_offset =
|
|
141
208
|
relative_vector_body_offset + (element_size * i)
|
|
142
209
|
yield(relative_element_offset)
|
|
143
210
|
end
|
|
144
211
|
end
|
|
145
|
-
|
|
146
|
-
def unpack_bool_vector(offset, element_size)
|
|
147
|
-
unpack_vector(offset, element_size) do |relative_element_offset|
|
|
148
|
-
unpack_bool(relative_element_offset)
|
|
149
|
-
end
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
def unpack_ubyte_vector(offset, element_size)
|
|
153
|
-
unpack_vector(offset, element_size) do |relative_element_offset|
|
|
154
|
-
unpack_ubyte(relative_element_offset)
|
|
155
|
-
end
|
|
156
|
-
end
|
|
157
|
-
|
|
158
|
-
def unpack_string_vector(offset, element_size)
|
|
159
|
-
unpack_vector(offset, element_size) do |relative_element_offset|
|
|
160
|
-
unpack_string(relative_element_offset)
|
|
161
|
-
end
|
|
162
|
-
end
|
|
163
|
-
|
|
164
|
-
def unpack_table_vector(offset, element_size, klass)
|
|
165
|
-
unpack_vector(offset, element_size) do |relative_element_offset|
|
|
166
|
-
unpack_table(klass, relative_element_offset)
|
|
167
|
-
end
|
|
168
|
-
end
|
|
169
|
-
|
|
170
|
-
def unpack_struct_vector(offset, element_size, klass)
|
|
171
|
-
unpack_vector(offset, element_size) do |relative_element_offset|
|
|
172
|
-
unpack_struct(klass, relative_element_offset)
|
|
173
|
-
end
|
|
174
|
-
end
|
|
175
212
|
end
|
|
176
213
|
end
|
data/lib/flatbuffers.rb
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright 2025
|
|
1
|
+
# Copyright 2025-2026 Sutou Kouhei <kou@clear-code.com>
|
|
2
2
|
#
|
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
4
|
# you may not use this file except in compliance with the License.
|
|
@@ -12,9 +12,12 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
|
+
require_relative "flatbuffers/serializer"
|
|
15
16
|
require_relative "flatbuffers/enum"
|
|
17
|
+
require_relative "flatbuffers/error"
|
|
16
18
|
require_relative "flatbuffers/flags"
|
|
17
19
|
require_relative "flatbuffers/struct"
|
|
18
|
-
require_relative "flatbuffers/
|
|
20
|
+
require_relative "flatbuffers/root_table"
|
|
19
21
|
require_relative "flatbuffers/union"
|
|
22
|
+
require_relative "flatbuffers/verifier"
|
|
20
23
|
require_relative "flatbuffers/version"
|
data/red-flatbuffers.gemspec
CHANGED
|
@@ -33,6 +33,7 @@ Gem::Specification.new do |spec|
|
|
|
33
33
|
description = clean_white_space.call(entries[2])
|
|
34
34
|
spec.summary, spec.description, = description.split(/\n\n+/, 3)
|
|
35
35
|
spec.license = "Apache-2.0"
|
|
36
|
+
spec.required_ruby_version = ">= 3.1.0"
|
|
36
37
|
spec.files = ["README.md", "#{spec.name}.gemspec"]
|
|
37
38
|
spec.files += Dir.glob("lib/**/*.rb")
|
|
38
39
|
spec.files += Dir.glob("doc/text/*")
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: red-flatbuffers
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sutou Kouhei
|
|
@@ -27,8 +27,13 @@ files:
|
|
|
27
27
|
- doc/text/apache-2.0.txt
|
|
28
28
|
- doc/text/news.md
|
|
29
29
|
- lib/flatbuffers.rb
|
|
30
|
+
- lib/flatbuffers/alignable.rb
|
|
31
|
+
- lib/flatbuffers/append_as_bytes.rb
|
|
30
32
|
- lib/flatbuffers/command/rbflatc.rb
|
|
33
|
+
- lib/flatbuffers/data_definable.rb
|
|
31
34
|
- lib/flatbuffers/enum.rb
|
|
35
|
+
- lib/flatbuffers/error.rb
|
|
36
|
+
- lib/flatbuffers/field.rb
|
|
32
37
|
- lib/flatbuffers/flags.rb
|
|
33
38
|
- lib/flatbuffers/generator.rb
|
|
34
39
|
- lib/flatbuffers/inspectable.rb
|
|
@@ -44,9 +49,12 @@ files:
|
|
|
44
49
|
- lib/flatbuffers/reflection/schema_file.rb
|
|
45
50
|
- lib/flatbuffers/reflection/service.rb
|
|
46
51
|
- lib/flatbuffers/reflection/type.rb
|
|
52
|
+
- lib/flatbuffers/root_table.rb
|
|
53
|
+
- lib/flatbuffers/serializer.rb
|
|
47
54
|
- lib/flatbuffers/struct.rb
|
|
48
55
|
- lib/flatbuffers/table.rb
|
|
49
56
|
- lib/flatbuffers/union.rb
|
|
57
|
+
- lib/flatbuffers/verifier.rb
|
|
50
58
|
- lib/flatbuffers/version.rb
|
|
51
59
|
- lib/flatbuffers/view.rb
|
|
52
60
|
- red-flatbuffers.gemspec
|
|
@@ -55,7 +63,7 @@ licenses:
|
|
|
55
63
|
- Apache-2.0
|
|
56
64
|
metadata:
|
|
57
65
|
bug_tracker_uri: https://github.com/red-data-tools/red-flatbuffers/issues
|
|
58
|
-
changelog_uri: https://github.com/red-data-tools/red-flatbuffers/releases/tag/0.0.
|
|
66
|
+
changelog_uri: https://github.com/red-data-tools/red-flatbuffers/releases/tag/0.0.4
|
|
59
67
|
source_code_uri: https://github.com/red-data-tools/red-flatbuffers
|
|
60
68
|
rdoc_options: []
|
|
61
69
|
require_paths:
|
|
@@ -64,14 +72,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
64
72
|
requirements:
|
|
65
73
|
- - ">="
|
|
66
74
|
- !ruby/object:Gem::Version
|
|
67
|
-
version:
|
|
75
|
+
version: 3.1.0
|
|
68
76
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
69
77
|
requirements:
|
|
70
78
|
- - ">="
|
|
71
79
|
- !ruby/object:Gem::Version
|
|
72
80
|
version: '0'
|
|
73
81
|
requirements: []
|
|
74
|
-
rubygems_version:
|
|
82
|
+
rubygems_version: 4.0.3
|
|
75
83
|
specification_version: 4
|
|
76
84
|
summary: Red FlatBuffers is a pure Ruby FlatBuffers implementation.
|
|
77
85
|
test_files: []
|