Skip to content

Commit 755bf24

Browse files
committed
Add support for serialization of basic features
reflection.fbs is only supported for now. monster_test.fbs isn't supported yet.
1 parent 10f4c5c commit 755bf24

File tree

12 files changed

+741
-56
lines changed

12 files changed

+741
-56
lines changed

lib/flatbuffers.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
require_relative "flatbuffers/serializer"
1516
require_relative "flatbuffers/enum"
1617
require_relative "flatbuffers/flags"
1718
require_relative "flatbuffers/struct"
18-
require_relative "flatbuffers/table"
19+
require_relative "flatbuffers/root_table"
1920
require_relative "flatbuffers/union"
2021
require_relative "flatbuffers/version"

lib/flatbuffers/data_definable.rb

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Copyright 2025 Sutou Kouhei <[email protected]>
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
18+
::Struct.new(*self::FIELDS.collect(&:name)) do
19+
members.each do |member|
20+
next unless member.end_with?("?")
21+
alias_method :"#{member.to_s.delete_suffix("?")}=", :"#{member}="
22+
end
23+
end
24+
end
25+
end
26+
end

lib/flatbuffers/field.rb

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Copyright 2025 Sutou Kouhei <[email protected]>
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 :offset
21+
attr_reader :base_type
22+
attr_reader :index
23+
def initialize(name, offset, base_type)
24+
@name = name
25+
@offset = offset
26+
@base_type = base_type
27+
@index = View::VTable.compute_field_index(@offset)
28+
end
29+
end
30+
end

lib/flatbuffers/generator.rb

Lines changed: 86 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -338,14 +338,99 @@ def generate_object(writer, object)
338338
start_modules(writer, namespaces)
339339

340340
if object.struct?
341+
is_root_table = false
341342
parent = "::FlatBuffers::Struct"
342343
else
343-
parent = "::FlatBuffers::Table"
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
344350
end
345351
generate_documentation(writer, object.documentation)
346352
writer << "class #{to_class_name(name)} < #{parent}"
347353
writer.indent
348354

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"
376+
writer << ""
377+
378+
generate_object_methods(writer, object, namespaces)
379+
380+
writer.end # class
381+
382+
end_modules(writer, namespaces)
383+
end
384+
385+
def generate_object_fields(writer, object)
386+
writer << "FIELDS = ["
387+
writer.indent
388+
389+
offset_sorted_fields = object.fields&.sort_by(&:offset)
390+
offset_sorted_fields&.each do |field|
391+
# Skip writing deprecated fields altogether.
392+
next if field.deprecated?
393+
394+
method_name = to_method_name(field.name)
395+
type = field.type
396+
base_type = type.base_type
397+
if base_type == Reflection::BaseType::BOOL
398+
method_name = "#{method_name}?".delete_prefix("is_")
399+
end
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::VECTOR
410+
element_base_type = type.element
411+
if element_base_type == Reflection::BaseType::OBJ
412+
object = @schema.objects[type.index]
413+
*object_namespaces, object_name = denamespace(object.name)
414+
klass = to_absolute_class_name(@outer_namespaces,
415+
object_namespaces,
416+
object_name)
417+
ruby_base_type = "[#{to_ruby_code(klass)}]"
418+
else
419+
ruby_base_type = "[:#{to_lower_snake_case(element_base_type.name)}]"
420+
end
421+
else
422+
ruby_base_type = ":#{to_lower_snake_case(base_type.name)}"
423+
end
424+
writer << ("::FlatBuffers::Field.new(" +
425+
":#{method_name}, #{ruby_offset}, #{ruby_base_type}),")
426+
end
427+
428+
writer.unindent
429+
writer << "]"
430+
writer << ""
431+
end
432+
433+
def generate_object_methods(writer, object, namespaces)
349434
n_processed_fields = 0
350435
object.fields&.each do |field|
351436
# Skip writing deprecated fields altogether.
@@ -474,10 +559,6 @@ def generate_object(writer, object)
474559

475560
n_processed_fields += 1
476561
end
477-
478-
writer.end # class
479-
480-
end_modules(writer, namespaces)
481562
end
482563

483564
def generate_documentation(writer, documentation)

lib/flatbuffers/reflection/schema.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
module FlatBuffers
1515
module Reflection
16-
class Schema < ::FlatBuffers::Table
16+
class Schema < ::FlatBuffers::RootTable
1717
def advanced_features
1818
field_offset = @view.unpack_virtual_offset(16)
1919
if field_offset.zero?

lib/flatbuffers/root_table.rb

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Copyright 2025 Sutou Kouhei <[email protected]>
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 commit comments

Comments
 (0)