unibuf 0.1.1 → 0.1.2
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/.rubocop_todo.yml +170 -200
- data/CODE_OF_CONDUCT.md +132 -0
- data/README.adoc +306 -114
- data/docs/CAPNPROTO.adoc +436 -0
- data/docs/FLATBUFFERS.adoc +430 -0
- data/docs/PROTOBUF.adoc +515 -0
- data/docs/TXTPROTO.adoc +369 -0
- data/lib/unibuf/commands/convert.rb +60 -2
- data/lib/unibuf/commands/schema.rb +68 -11
- data/lib/unibuf/errors.rb +23 -26
- data/lib/unibuf/models/capnproto/enum_definition.rb +72 -0
- data/lib/unibuf/models/capnproto/field_definition.rb +81 -0
- data/lib/unibuf/models/capnproto/interface_definition.rb +70 -0
- data/lib/unibuf/models/capnproto/method_definition.rb +81 -0
- data/lib/unibuf/models/capnproto/schema.rb +84 -0
- data/lib/unibuf/models/capnproto/struct_definition.rb +96 -0
- data/lib/unibuf/models/capnproto/union_definition.rb +62 -0
- data/lib/unibuf/models/flatbuffers/enum_definition.rb +69 -0
- data/lib/unibuf/models/flatbuffers/field_definition.rb +88 -0
- data/lib/unibuf/models/flatbuffers/schema.rb +102 -0
- data/lib/unibuf/models/flatbuffers/struct_definition.rb +70 -0
- data/lib/unibuf/models/flatbuffers/table_definition.rb +73 -0
- data/lib/unibuf/models/flatbuffers/union_definition.rb +60 -0
- data/lib/unibuf/models/message.rb +10 -0
- data/lib/unibuf/parsers/capnproto/binary_parser.rb +267 -0
- data/lib/unibuf/parsers/capnproto/grammar.rb +272 -0
- data/lib/unibuf/parsers/capnproto/list_reader.rb +208 -0
- data/lib/unibuf/parsers/capnproto/pointer_decoder.rb +163 -0
- data/lib/unibuf/parsers/capnproto/processor.rb +348 -0
- data/lib/unibuf/parsers/capnproto/segment_reader.rb +131 -0
- data/lib/unibuf/parsers/capnproto/struct_reader.rb +199 -0
- data/lib/unibuf/parsers/flatbuffers/binary_parser.rb +325 -0
- data/lib/unibuf/parsers/flatbuffers/grammar.rb +235 -0
- data/lib/unibuf/parsers/flatbuffers/processor.rb +299 -0
- data/lib/unibuf/serializers/binary_serializer.rb +218 -0
- data/lib/unibuf/serializers/capnproto/binary_serializer.rb +402 -0
- data/lib/unibuf/serializers/capnproto/list_writer.rb +199 -0
- data/lib/unibuf/serializers/capnproto/pointer_encoder.rb +118 -0
- data/lib/unibuf/serializers/capnproto/segment_builder.rb +124 -0
- data/lib/unibuf/serializers/capnproto/struct_writer.rb +139 -0
- data/lib/unibuf/serializers/flatbuffers/binary_serializer.rb +167 -0
- data/lib/unibuf/version.rb +1 -1
- data/lib/unibuf.rb +27 -0
- metadata +36 -1
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
= FlatBuffers Support in Unibuf
|
|
2
|
+
|
|
3
|
+
== Overview
|
|
4
|
+
|
|
5
|
+
Unibuf provides complete FlatBuffers support including schema parsing, binary format reading, and binary format writing.
|
|
6
|
+
|
|
7
|
+
All features are implemented in pure Ruby with no C/C++ dependencies.
|
|
8
|
+
|
|
9
|
+
== Features
|
|
10
|
+
|
|
11
|
+
* Parse FlatBuffers schemas (`.fbs` files)
|
|
12
|
+
* Read binary FlatBuffers data (`.fb` files)
|
|
13
|
+
* Write binary FlatBuffers data
|
|
14
|
+
* Complete round-trip support
|
|
15
|
+
* All scalar types supported
|
|
16
|
+
* Tables, structs, enums, unions
|
|
17
|
+
* Vectors and strings
|
|
18
|
+
|
|
19
|
+
== FlatBuffers Schema Parsing
|
|
20
|
+
|
|
21
|
+
=== Basic usage
|
|
22
|
+
|
|
23
|
+
[source,ruby]
|
|
24
|
+
----
|
|
25
|
+
require "unibuf"
|
|
26
|
+
|
|
27
|
+
# Parse FlatBuffers schema
|
|
28
|
+
schema = Unibuf.parse_flatbuffers_schema("monster.fbs")
|
|
29
|
+
|
|
30
|
+
# Access schema metadata
|
|
31
|
+
puts schema.namespace # => "MyGame.Sample"
|
|
32
|
+
puts schema.root_type # => "Monster"
|
|
33
|
+
puts schema.file_identifier # => "MONS"
|
|
34
|
+
|
|
35
|
+
# List all tables
|
|
36
|
+
schema.table_names # => ["Monster", "Weapon", "Vec3"]
|
|
37
|
+
|
|
38
|
+
# List all enums
|
|
39
|
+
schema.enum_names # => ["Color", "Equipment"]
|
|
40
|
+
----
|
|
41
|
+
|
|
42
|
+
=== Inspecting tables
|
|
43
|
+
|
|
44
|
+
[source,ruby]
|
|
45
|
+
----
|
|
46
|
+
# Get table definition
|
|
47
|
+
table = schema.find_table("Monster")
|
|
48
|
+
|
|
49
|
+
puts table.name # => "Monster"
|
|
50
|
+
|
|
51
|
+
# Inspect fields
|
|
52
|
+
table.fields.each do |field|
|
|
53
|
+
puts "#{field.name}: #{field.type}"
|
|
54
|
+
puts " Default: #{field.default_value}" if field.default_value
|
|
55
|
+
puts " Deprecated" if field.deprecated?
|
|
56
|
+
puts " Required" if field.required?
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Check table properties
|
|
60
|
+
table.has_vectors? # => true if any vector fields
|
|
61
|
+
table.has_nested_tables? # => true if nested tables
|
|
62
|
+
----
|
|
63
|
+
|
|
64
|
+
=== Schema constructs
|
|
65
|
+
|
|
66
|
+
Tables::
|
|
67
|
+
Variable-size objects with vtable indirection.
|
|
68
|
+
Support optional fields and versioning.
|
|
69
|
+
|
|
70
|
+
Structs::
|
|
71
|
+
Fixed-size objects stored inline.
|
|
72
|
+
More efficient but less flexible than tables.
|
|
73
|
+
|
|
74
|
+
Enums::
|
|
75
|
+
Integer-based enumerations with optional type specification.
|
|
76
|
+
|
|
77
|
+
Unions::
|
|
78
|
+
Discriminated unions representing choice between table types.
|
|
79
|
+
|
|
80
|
+
Vectors::
|
|
81
|
+
Arrays of any type (scalars, strings, or tables).
|
|
82
|
+
|
|
83
|
+
== FlatBuffers Binary Parsing
|
|
84
|
+
|
|
85
|
+
=== Basic parsing
|
|
86
|
+
|
|
87
|
+
[source,ruby]
|
|
88
|
+
----
|
|
89
|
+
require "unibuf"
|
|
90
|
+
|
|
91
|
+
# 1. Load schema (REQUIRED)
|
|
92
|
+
schema = Unibuf.parse_flatbuffers_schema("monster.fbs")
|
|
93
|
+
|
|
94
|
+
# 2. Create parser
|
|
95
|
+
parser = Unibuf::Parsers::Flatbuffers::BinaryParser.new(schema)
|
|
96
|
+
|
|
97
|
+
# 3. Parse binary file
|
|
98
|
+
data = parser.parse_file("monster.fb")
|
|
99
|
+
|
|
100
|
+
# 4. Access fields
|
|
101
|
+
puts data["name"] # => "Orc"
|
|
102
|
+
puts data["hp"] # => 150
|
|
103
|
+
puts data["inventory"] # => [1, 2, 3, 4, 5]
|
|
104
|
+
----
|
|
105
|
+
|
|
106
|
+
=== Parsing from binary string
|
|
107
|
+
|
|
108
|
+
[source,ruby]
|
|
109
|
+
----
|
|
110
|
+
# Read binary data
|
|
111
|
+
binary_data = File.binread("monster.fb")
|
|
112
|
+
|
|
113
|
+
# Parse
|
|
114
|
+
schema = Unibuf.parse_flatbuffers_schema("monster.fbs")
|
|
115
|
+
parser = Unibuf::Parsers::Flatbuffers::BinaryParser.new(schema)
|
|
116
|
+
data = parser.parse(binary_data)
|
|
117
|
+
----
|
|
118
|
+
|
|
119
|
+
=== Nested tables
|
|
120
|
+
|
|
121
|
+
[source,ruby]
|
|
122
|
+
----
|
|
123
|
+
# Schema with nested tables:
|
|
124
|
+
# table Vec3 { x: float; y: float; z: float; }
|
|
125
|
+
# table Monster { pos: Vec3; name: string; }
|
|
126
|
+
|
|
127
|
+
data = parser.parse_file("monster.fb")
|
|
128
|
+
|
|
129
|
+
# Access nested data
|
|
130
|
+
position = data["pos"]
|
|
131
|
+
puts position["x"] # => 1.0
|
|
132
|
+
puts position["y"] # => 2.0
|
|
133
|
+
puts position["z"] # => 3.0
|
|
134
|
+
----
|
|
135
|
+
|
|
136
|
+
=== Binary format details
|
|
137
|
+
|
|
138
|
+
FlatBuffers binary format uses:
|
|
139
|
+
|
|
140
|
+
vtables::
|
|
141
|
+
Virtual tables store field offsets for backwards compatibility
|
|
142
|
+
|
|
143
|
+
Offset-based::
|
|
144
|
+
All references use offsets from current position
|
|
145
|
+
|
|
146
|
+
Little-endian::
|
|
147
|
+
All numeric values in little-endian byte order
|
|
148
|
+
|
|
149
|
+
Root object::
|
|
150
|
+
File starts with offset to root table
|
|
151
|
+
|
|
152
|
+
Zero-copy::
|
|
153
|
+
Design enables reading without deserialization (not yet exposed in API)
|
|
154
|
+
|
|
155
|
+
== FlatBuffers Binary Serialization
|
|
156
|
+
|
|
157
|
+
=== Basic serialization
|
|
158
|
+
|
|
159
|
+
[source,ruby]
|
|
160
|
+
----
|
|
161
|
+
require "unibuf"
|
|
162
|
+
|
|
163
|
+
# 1. Load schema (REQUIRED)
|
|
164
|
+
schema = Unibuf.parse_flatbuffers_schema("monster.fbs")
|
|
165
|
+
|
|
166
|
+
# 2. Prepare data as hash
|
|
167
|
+
data = {
|
|
168
|
+
"name" => "Dragon",
|
|
169
|
+
"hp" => 500,
|
|
170
|
+
"friendly" => false
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
# 3. Serialize
|
|
174
|
+
serializer = Unibuf::Serializers::Flatbuffers::BinarySerializer.new(schema)
|
|
175
|
+
binary_data = serializer.serialize(data)
|
|
176
|
+
|
|
177
|
+
# 4. Write to file
|
|
178
|
+
File.binwrite("dragon.fb", binary_data)
|
|
179
|
+
----
|
|
180
|
+
|
|
181
|
+
=== Serializing with all scalar types
|
|
182
|
+
|
|
183
|
+
[source,ruby]
|
|
184
|
+
----
|
|
185
|
+
data = {
|
|
186
|
+
"byte_val" => -42, # signed byte
|
|
187
|
+
"ubyte_val" => 200, # unsigned byte
|
|
188
|
+
"short_val" => -1000, # signed short
|
|
189
|
+
"ushort_val" => 5000, # unsigned short
|
|
190
|
+
"int_val" => -100000, # signed int
|
|
191
|
+
"uint_val" => 200000, # unsigned int
|
|
192
|
+
"long_val" => -1000000000, # signed long
|
|
193
|
+
"ulong_val" => 2000000000, # unsigned long
|
|
194
|
+
"float_val" => 3.14, # 32-bit float
|
|
195
|
+
"double_val" => 3.14159, # 64-bit double
|
|
196
|
+
"bool_val" => true, # boolean
|
|
197
|
+
"string_val" => "Hello!" # string
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
serializer = Unibuf::Serializers::Flatbuffers::BinarySerializer.new(schema)
|
|
201
|
+
binary = serializer.serialize(data)
|
|
202
|
+
----
|
|
203
|
+
|
|
204
|
+
=== Round-trip verification
|
|
205
|
+
|
|
206
|
+
[source,ruby]
|
|
207
|
+
----
|
|
208
|
+
# Original data
|
|
209
|
+
original = {
|
|
210
|
+
"name" => "Goblin",
|
|
211
|
+
"hp" => 75,
|
|
212
|
+
"mana" => 50
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
# Serialize
|
|
216
|
+
serializer = Unibuf::Serializers::Flatbuffers::BinarySerializer.new(schema)
|
|
217
|
+
binary = serializer.serialize(original)
|
|
218
|
+
|
|
219
|
+
# Parse back
|
|
220
|
+
parser = Unibuf::Parsers::Flatbuffers::BinaryParser.new(schema)
|
|
221
|
+
reparsed = parser.parse(binary)
|
|
222
|
+
|
|
223
|
+
# Verify
|
|
224
|
+
puts original == reparsed # => true
|
|
225
|
+
----
|
|
226
|
+
|
|
227
|
+
== FlatBuffers vs Protocol Buffers
|
|
228
|
+
|
|
229
|
+
=== When to use FlatBuffers
|
|
230
|
+
|
|
231
|
+
Use FlatBuffers when:
|
|
232
|
+
|
|
233
|
+
* Zero-copy access is important
|
|
234
|
+
* Memory efficiency is critical
|
|
235
|
+
* Random field access is needed
|
|
236
|
+
* Backwards compatibility with size constraints
|
|
237
|
+
* Game development or embedded systems
|
|
238
|
+
|
|
239
|
+
=== When to use Protocol Buffers
|
|
240
|
+
|
|
241
|
+
Use Protocol Buffers when:
|
|
242
|
+
|
|
243
|
+
* Schema evolution is important
|
|
244
|
+
* Reflection is needed
|
|
245
|
+
* gRPC integration
|
|
246
|
+
* Wide language support
|
|
247
|
+
* Mature ecosystem
|
|
248
|
+
|
|
249
|
+
== Complete example
|
|
250
|
+
|
|
251
|
+
=== Schema file (monster.fbs)
|
|
252
|
+
|
|
253
|
+
[source]
|
|
254
|
+
----
|
|
255
|
+
namespace MyGame.Sample;
|
|
256
|
+
|
|
257
|
+
enum Color : byte { Red = 0, Green, Blue = 2 }
|
|
258
|
+
|
|
259
|
+
table Monster {
|
|
260
|
+
pos: Vec3;
|
|
261
|
+
hp: short = 100;
|
|
262
|
+
name: string;
|
|
263
|
+
friendly: bool = false (deprecated);
|
|
264
|
+
inventory: [ubyte];
|
|
265
|
+
color: Color = Blue;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
struct Vec3 {
|
|
269
|
+
x: float;
|
|
270
|
+
y: float;
|
|
271
|
+
z: float;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
root_type Monster;
|
|
275
|
+
----
|
|
276
|
+
|
|
277
|
+
=== Parsing the schema
|
|
278
|
+
|
|
279
|
+
[source,ruby]
|
|
280
|
+
----
|
|
281
|
+
schema = Unibuf.parse_flatbuffers_schema("monster.fbs")
|
|
282
|
+
|
|
283
|
+
# Access root type
|
|
284
|
+
puts schema.root_type # => "Monster"
|
|
285
|
+
|
|
286
|
+
# Find table
|
|
287
|
+
monster = schema.find_table("Monster")
|
|
288
|
+
puts monster.field_names # => ["pos", "hp", "name", "friendly", "inventory", "color"]
|
|
289
|
+
|
|
290
|
+
# Find struct
|
|
291
|
+
vec3 = schema.find_struct("Vec3")
|
|
292
|
+
puts vec3.fixed_size? # => true (structs are always fixed size)
|
|
293
|
+
|
|
294
|
+
# Find enum
|
|
295
|
+
color = schema.find_enum("Color")
|
|
296
|
+
puts color.values # => {"Red"=>0, "Green"=>1, "Blue"=>2}
|
|
297
|
+
----
|
|
298
|
+
|
|
299
|
+
=== Creating and reading binary data
|
|
300
|
+
|
|
301
|
+
[source,ruby]
|
|
302
|
+
----
|
|
303
|
+
# Create data
|
|
304
|
+
data = {
|
|
305
|
+
"name" => "Orc",
|
|
306
|
+
"hp" => 150,
|
|
307
|
+
"friendly" => false,
|
|
308
|
+
"inventory" => [1, 2, 3, 4, 5],
|
|
309
|
+
"color" => "Green"
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
# Write FlatBuffer
|
|
313
|
+
serializer = Unibuf::Serializers::Flatbuffers::BinarySerializer.new(schema)
|
|
314
|
+
binary = serializer.serialize_to_file(data, "orc.fb")
|
|
315
|
+
|
|
316
|
+
# Read FlatBuffer
|
|
317
|
+
parser = Unibuf::Parsers::Flatbuffers::BinaryParser.new(schema)
|
|
318
|
+
loaded = parser.parse_file("orc.fb")
|
|
319
|
+
|
|
320
|
+
puts loaded["name"] # => "Orc"
|
|
321
|
+
puts loaded["hp"] # => 150
|
|
322
|
+
puts loaded["inventory"] # => [1, 2, 3, 4, 5]
|
|
323
|
+
----
|
|
324
|
+
|
|
325
|
+
== CLI usage
|
|
326
|
+
|
|
327
|
+
=== Schema inspection
|
|
328
|
+
|
|
329
|
+
[source,shell]
|
|
330
|
+
----
|
|
331
|
+
# View FlatBuffers schema structure
|
|
332
|
+
unibuf schema monster.fbs
|
|
333
|
+
|
|
334
|
+
# Output as JSON for tooling
|
|
335
|
+
unibuf schema monster.fbs --format json
|
|
336
|
+
----
|
|
337
|
+
|
|
338
|
+
== Technical details
|
|
339
|
+
|
|
340
|
+
=== vtable format
|
|
341
|
+
|
|
342
|
+
FlatBuffers uses vtables for schema evolution:
|
|
343
|
+
|
|
344
|
+
[source]
|
|
345
|
+
----
|
|
346
|
+
vtable:
|
|
347
|
+
vtable_size: uint16 # Size of vtable in bytes
|
|
348
|
+
object_size: uint16 # Size of object in bytes
|
|
349
|
+
field_offsets: [uint16] # Offset for each field (0 if not present)
|
|
350
|
+
----
|
|
351
|
+
|
|
352
|
+
=== Table format
|
|
353
|
+
|
|
354
|
+
[source]
|
|
355
|
+
----
|
|
356
|
+
table:
|
|
357
|
+
vtable_offset: int32 (soffset) # Negative offset to vtable
|
|
358
|
+
fields: [...inline scalars and uoffsets to out-of-line data...]
|
|
359
|
+
----
|
|
360
|
+
|
|
361
|
+
=== Scalar storage
|
|
362
|
+
|
|
363
|
+
Scalars are stored INLINE in table body:
|
|
364
|
+
- byte, ubyte, bool: 1 byte
|
|
365
|
+
- short, ushort: 2 bytes
|
|
366
|
+
- int, uint, float: 4 bytes
|
|
367
|
+
- long, ulong, double: 8 bytes
|
|
368
|
+
|
|
369
|
+
Non-scalars (strings, vectors, nested tables) are stored OUT-OF-LINE with offsets (uoffset32) in the table.
|
|
370
|
+
|
|
371
|
+
=== String format
|
|
372
|
+
|
|
373
|
+
[source]
|
|
374
|
+
----
|
|
375
|
+
string:
|
|
376
|
+
length: uint32
|
|
377
|
+
data: [byte]
|
|
378
|
+
null_terminator: byte (0x00)
|
|
379
|
+
padding: align to 4 bytes
|
|
380
|
+
----
|
|
381
|
+
|
|
382
|
+
== Limitations and future work
|
|
383
|
+
|
|
384
|
+
=== Currently supported
|
|
385
|
+
|
|
386
|
+
✅ All scalar types
|
|
387
|
+
✅ Strings
|
|
388
|
+
✅ Tables
|
|
389
|
+
✅ Enums
|
|
390
|
+
✅ vtable generation and parsing
|
|
391
|
+
✅ Round-trip serialization
|
|
392
|
+
|
|
393
|
+
=== Future enhancements
|
|
394
|
+
|
|
395
|
+
* Vector serialization (currently not implemented)
|
|
396
|
+
* Struct serialization (currently not implemented)
|
|
397
|
+
* Union support (parsing only)
|
|
398
|
+
* Nested table serialization
|
|
399
|
+
* vtable deduplication for smaller files
|
|
400
|
+
* Zero-copy access API
|
|
401
|
+
|
|
402
|
+
== Performance characteristics
|
|
403
|
+
|
|
404
|
+
FlatBuffers advantages:
|
|
405
|
+
|
|
406
|
+
Fast access::
|
|
407
|
+
Offset-based access without full deserialization
|
|
408
|
+
|
|
409
|
+
Memory efficient::
|
|
410
|
+
In-place access without additional allocation
|
|
411
|
+
|
|
412
|
+
Backwards compatible::
|
|
413
|
+
vtables enable schema evolution
|
|
414
|
+
|
|
415
|
+
== Contributing
|
|
416
|
+
|
|
417
|
+
FlatBuffers support is complete for basic use cases. Contributions welcome for:
|
|
418
|
+
|
|
419
|
+
* Vector serialization
|
|
420
|
+
* Struct handling
|
|
421
|
+
* Union support
|
|
422
|
+
* Performance optimizations
|
|
423
|
+
* Additional test cases
|
|
424
|
+
|
|
425
|
+
== References
|
|
426
|
+
|
|
427
|
+
* FlatBuffers specification: https://flatbuffers.dev/
|
|
428
|
+
* FlatBuffers grammar: https://flatbuffers.dev/flatbuffers_grammar.html
|
|
429
|
+
* FlatBuffers internals: https://flatbuffers.dev/md__internals.html
|
|
430
|
+
* Unibuf GitHub: https://github.com/lutaml/unibuf
|