red-flatbuffers 0.0.2 → 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 +18 -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 +106 -12
- 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 -4
- metadata +13 -47
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
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# News
|
|
2
2
|
|
|
3
|
+
## 0.0.4 - 2026-01-12
|
|
4
|
+
|
|
5
|
+
### Improvements
|
|
6
|
+
|
|
7
|
+
* Added support for serializing.
|
|
8
|
+
|
|
9
|
+
## 0.0.3 - 2025-11-28
|
|
10
|
+
|
|
11
|
+
### Improvements
|
|
12
|
+
|
|
13
|
+
* Added support for outer namespaces in absolute class name.
|
|
14
|
+
|
|
15
|
+
## 0.0.2 - 2025-11-28
|
|
16
|
+
|
|
17
|
+
### Improvements
|
|
18
|
+
|
|
19
|
+
* Filled more gem metadata.
|
|
20
|
+
|
|
3
21
|
## 0.0.1 - 2025-11-28
|
|
4
22
|
|
|
5
23
|
Initial release!!!
|
|
@@ -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.
|
|
@@ -70,6 +70,11 @@ module FlatBuffers
|
|
|
70
70
|
components.join("::")
|
|
71
71
|
end
|
|
72
72
|
|
|
73
|
+
def to_absolute_class_name(outer_namespaces, namespaces, name)
|
|
74
|
+
namespaces = outer_namespaces + namespaces if outer_namespaces
|
|
75
|
+
"::#{to_namespaced_class_name(namespaces, name)}"
|
|
76
|
+
end
|
|
77
|
+
|
|
73
78
|
def to_constant_name(name)
|
|
74
79
|
to_upper_snake_case(name)
|
|
75
80
|
end
|
|
@@ -241,7 +246,9 @@ module FlatBuffers
|
|
|
241
246
|
if enum.union?
|
|
242
247
|
union = @schema.objects[value.union_type.index]
|
|
243
248
|
*union_namespaces, union_name = denamespace(union.name)
|
|
244
|
-
klass =
|
|
249
|
+
klass = to_absolute_class_name(@outer_namespaces,
|
|
250
|
+
union_namespaces,
|
|
251
|
+
union_name)
|
|
245
252
|
relative_union_namespaces =
|
|
246
253
|
resolve_namespaces(union_namespaces, namespaces)
|
|
247
254
|
path_components = relative_union_namespaces.collect do |ns|
|
|
@@ -331,20 +338,113 @@ module FlatBuffers
|
|
|
331
338
|
start_modules(writer, namespaces)
|
|
332
339
|
|
|
333
340
|
if object.struct?
|
|
341
|
+
is_root_table = false
|
|
334
342
|
parent = "::FlatBuffers::Struct"
|
|
335
343
|
else
|
|
336
|
-
|
|
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
|
|
337
350
|
end
|
|
338
351
|
generate_documentation(writer, object.documentation)
|
|
339
352
|
writer << "class #{to_class_name(name)} < #{parent}"
|
|
340
353
|
writer.indent
|
|
341
354
|
|
|
342
|
-
|
|
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)
|
|
343
443
|
object.fields&.each do |field|
|
|
344
444
|
# Skip writing deprecated fields altogether.
|
|
345
445
|
next if field.deprecated?
|
|
346
446
|
|
|
347
|
-
writer << ""
|
|
447
|
+
writer << ""
|
|
348
448
|
|
|
349
449
|
method_name = to_method_name(field.name)
|
|
350
450
|
type = field.type
|
|
@@ -464,13 +564,7 @@ module FlatBuffers
|
|
|
464
564
|
writer.end
|
|
465
565
|
end
|
|
466
566
|
writer.end
|
|
467
|
-
|
|
468
|
-
n_processed_fields += 1
|
|
469
567
|
end
|
|
470
|
-
|
|
471
|
-
writer.end # class
|
|
472
|
-
|
|
473
|
-
end_modules(writer, namespaces)
|
|
474
568
|
end
|
|
475
569
|
|
|
476
570
|
def generate_documentation(writer, documentation)
|
|
@@ -578,7 +672,7 @@ module FlatBuffers
|
|
|
578
672
|
to_namespaced_class_name(relative_namespaces, name)
|
|
579
673
|
else
|
|
580
674
|
# We need to use absolute hierarchy
|
|
581
|
-
|
|
675
|
+
to_absolute_class_name(@outer_namespaces, namespaces, name)
|
|
582
676
|
end
|
|
583
677
|
end
|
|
584
678
|
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 "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/*")
|
|
@@ -40,10 +41,6 @@ Gem::Specification.new do |spec|
|
|
|
40
41
|
spec.executables = Dir.glob("*")
|
|
41
42
|
end
|
|
42
43
|
|
|
43
|
-
spec.add_development_dependency("bundler")
|
|
44
|
-
spec.add_development_dependency("rake")
|
|
45
|
-
spec.add_development_dependency("test-unit")
|
|
46
|
-
|
|
47
44
|
spec.metadata = {
|
|
48
45
|
"bug_tracker_uri" => "#{spec.homepage}/issues",
|
|
49
46
|
"changelog_uri" => "#{spec.homepage}/releases/tag/#{spec.version}",
|
metadata
CHANGED
|
@@ -1,56 +1,14 @@
|
|
|
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
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
-
dependencies:
|
|
12
|
-
- !ruby/object:Gem::Dependency
|
|
13
|
-
name: bundler
|
|
14
|
-
requirement: !ruby/object:Gem::Requirement
|
|
15
|
-
requirements:
|
|
16
|
-
- - ">="
|
|
17
|
-
- !ruby/object:Gem::Version
|
|
18
|
-
version: '0'
|
|
19
|
-
type: :development
|
|
20
|
-
prerelease: false
|
|
21
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
-
requirements:
|
|
23
|
-
- - ">="
|
|
24
|
-
- !ruby/object:Gem::Version
|
|
25
|
-
version: '0'
|
|
26
|
-
- !ruby/object:Gem::Dependency
|
|
27
|
-
name: rake
|
|
28
|
-
requirement: !ruby/object:Gem::Requirement
|
|
29
|
-
requirements:
|
|
30
|
-
- - ">="
|
|
31
|
-
- !ruby/object:Gem::Version
|
|
32
|
-
version: '0'
|
|
33
|
-
type: :development
|
|
34
|
-
prerelease: false
|
|
35
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
-
requirements:
|
|
37
|
-
- - ">="
|
|
38
|
-
- !ruby/object:Gem::Version
|
|
39
|
-
version: '0'
|
|
40
|
-
- !ruby/object:Gem::Dependency
|
|
41
|
-
name: test-unit
|
|
42
|
-
requirement: !ruby/object:Gem::Requirement
|
|
43
|
-
requirements:
|
|
44
|
-
- - ">="
|
|
45
|
-
- !ruby/object:Gem::Version
|
|
46
|
-
version: '0'
|
|
47
|
-
type: :development
|
|
48
|
-
prerelease: false
|
|
49
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
-
requirements:
|
|
51
|
-
- - ">="
|
|
52
|
-
- !ruby/object:Gem::Version
|
|
53
|
-
version: '0'
|
|
11
|
+
dependencies: []
|
|
54
12
|
description: 'Red FlatBuffers can generate Ruby code that reads and writes (not implemented
|
|
55
13
|
yet) FlatBuffers from `.fbfs` (binary FlatBuffers schema). Red FlatBuffers can''t
|
|
56
14
|
compile `.fbs` (FlatBuffers schema) to `.fbfs`. You need to use FlatBuffers to compile
|
|
@@ -69,8 +27,13 @@ files:
|
|
|
69
27
|
- doc/text/apache-2.0.txt
|
|
70
28
|
- doc/text/news.md
|
|
71
29
|
- lib/flatbuffers.rb
|
|
30
|
+
- lib/flatbuffers/alignable.rb
|
|
31
|
+
- lib/flatbuffers/append_as_bytes.rb
|
|
72
32
|
- lib/flatbuffers/command/rbflatc.rb
|
|
33
|
+
- lib/flatbuffers/data_definable.rb
|
|
73
34
|
- lib/flatbuffers/enum.rb
|
|
35
|
+
- lib/flatbuffers/error.rb
|
|
36
|
+
- lib/flatbuffers/field.rb
|
|
74
37
|
- lib/flatbuffers/flags.rb
|
|
75
38
|
- lib/flatbuffers/generator.rb
|
|
76
39
|
- lib/flatbuffers/inspectable.rb
|
|
@@ -86,9 +49,12 @@ files:
|
|
|
86
49
|
- lib/flatbuffers/reflection/schema_file.rb
|
|
87
50
|
- lib/flatbuffers/reflection/service.rb
|
|
88
51
|
- lib/flatbuffers/reflection/type.rb
|
|
52
|
+
- lib/flatbuffers/root_table.rb
|
|
53
|
+
- lib/flatbuffers/serializer.rb
|
|
89
54
|
- lib/flatbuffers/struct.rb
|
|
90
55
|
- lib/flatbuffers/table.rb
|
|
91
56
|
- lib/flatbuffers/union.rb
|
|
57
|
+
- lib/flatbuffers/verifier.rb
|
|
92
58
|
- lib/flatbuffers/version.rb
|
|
93
59
|
- lib/flatbuffers/view.rb
|
|
94
60
|
- red-flatbuffers.gemspec
|
|
@@ -97,7 +63,7 @@ licenses:
|
|
|
97
63
|
- Apache-2.0
|
|
98
64
|
metadata:
|
|
99
65
|
bug_tracker_uri: https://github.com/red-data-tools/red-flatbuffers/issues
|
|
100
|
-
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
|
|
101
67
|
source_code_uri: https://github.com/red-data-tools/red-flatbuffers
|
|
102
68
|
rdoc_options: []
|
|
103
69
|
require_paths:
|
|
@@ -106,14 +72,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
106
72
|
requirements:
|
|
107
73
|
- - ">="
|
|
108
74
|
- !ruby/object:Gem::Version
|
|
109
|
-
version:
|
|
75
|
+
version: 3.1.0
|
|
110
76
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
111
77
|
requirements:
|
|
112
78
|
- - ">="
|
|
113
79
|
- !ruby/object:Gem::Version
|
|
114
80
|
version: '0'
|
|
115
81
|
requirements: []
|
|
116
|
-
rubygems_version:
|
|
82
|
+
rubygems_version: 4.0.3
|
|
117
83
|
specification_version: 4
|
|
118
84
|
summary: Red FlatBuffers is a pure Ruby FlatBuffers implementation.
|
|
119
85
|
test_files: []
|