class FlatBuffers::Serializer::TableSerializer
def add_field(field, value)
def add_field(field, value) if value.nil? @field_metadata[field] = { inline: true, offset: nil, } else case field.base_type when String add_field_object(field, value) when Array add_field_array(field, value) when :string align!(@table, View::OFFSET_SIZE) @field_metadata[field] = { inline: false, table_offset: @table.bytesize, value_offset: @values.bytesize, } @values.append_as_bytes(pack_value(field.base_type, value)) @table.append_as_bytes(DUMMY_OFFSET) # Replaced later else align!(@table, field.alignment_size) @field_metadata[field] = { inline: true, offset: @table.bytesize, } @table.append_as_bytes(pack_value(field.base_type, value)) end end end
def add_field_array(field, value)
def add_field_array(field, value) align!(@table, View::OFFSET_SIZE) align!(@values, View::OFFSET_SIZE) value_offset = @values.bytesize # The number of elements. @values.append_as_bytes([value.size].pack("L<")) element_base_type = field.base_type[0] case element_base_type when String klass = Object.const_get(element_base_type) if klass < Struct value.each do |v| sub_struct_serializer = StructSerializer.new(@values) klass.serialize(v, sub_struct_serializer) end else offset_base = @values.bytesize # Placeholder for offsets. value.size.times do @values.append_as_bytes(DUMMY_OFFSET) # Replaced later end align!(@values, LARGEST_ALIGNMENT_SIZE) value.each do |v| sub_table_serializer = TableSerializer.new sub_table_data, sub_table_offset = klass.serialize(v, sub_table_serializer) element_offset = @values.bytesize + sub_table_offset # Update offset placeholder. relative_element_offset = element_offset - offset_base @values[offset_base, View::OFFSET_SIZE] = [relative_element_offset].pack("L<") offset_base += View::OFFSET_SIZE @values.append_as_bytes(sub_table_data) end end @field_metadata[field] = { inline: false, table_offset: @table.bytesize, value_offset: value_offset, } when :string offset_base = @values.bytesize # Placeholder for offsets. value.size.times do @values.append_as_bytes(DUMMY_OFFSET) # Replaced later end value.each do |v| packed_value = pack_value(element_base_type, v) element_offset = @values.bytesize # Update offset placeholder. relative_element_offset = element_offset - offset_base @values[offset_base, View::OFFSET_SIZE] = [relative_element_offset].pack("L<") offset_base += View::OFFSET_SIZE @values.append_as_bytes(packed_value) end @field_metadata[field] = { inline: false, table_offset: @table.bytesize, value_offset: value_offset, } else value.each do |v| packed_value = pack_value(element_base_type, v) @values.append_as_bytes(packed_value) end @field_metadata[field] = { inline: false, table_offset: @table.bytesize, value_offset: value_offset, } end @table.append_as_bytes(DUMMY_OFFSET) # Replaced later end
def add_field_object(field, value)
def add_field_object(field, value) align!(@table, View::OFFSET_SIZE) klass = Object.const_get(field.base_type) if klass < Struct @field_metadata[field] = { inline: true, offset: @table.bytesize, } sub_struct_serializer = StructSerializer.new(@table) klass.serialize(value, sub_struct_serializer) elsif klass < Union align!(@values, LARGEST_ALIGNMENT_SIZE) sub_table_serializer = TableSerializer.new sub_table_data, sub_table_offset = value.class.table_class.serialize(value, sub_table_serializer) @field_metadata[field] = { inline: false, table_offset: @table.bytesize, value_offset: @values.bytesize + sub_table_offset, } @values.append_as_bytes(sub_table_data) @table.append_as_bytes(DUMMY_OFFSET) # Replaced later else align!(@values, LARGEST_ALIGNMENT_SIZE) sub_table_serializer = TableSerializer.new sub_table_data, sub_table_offset = klass.serialize(value, sub_table_serializer) @field_metadata[field] = { inline: false, table_offset: @table.bytesize, value_offset: @values.bytesize + sub_table_offset, } @values.append_as_bytes(sub_table_data) @table.append_as_bytes(DUMMY_OFFSET) # Replaced later end end
def finish
def finish vtable_size = View::VTable.compute_size(@field_metadata.size) align!(@table, LARGEST_ALIGNMENT_SIZE) table_size = @table.bytesize field_offsets = [] @field_metadata.each do |field, metadata| if metadata[:inline] # Scalar or struct offset = metadata[:offset] if offset.nil? field_offsets[field.index] = 0 else field_offsets[field.index] = offset end else # Table, string or vector. # |vtable|@table|@values| # # `vtable:` |vtable_size|table_size|field_offsets| # `field_offsets` uses relative offset from the start of # `vtable`. # # `@table`: |vtable_offset|fields| # `fields` uses relative offset from itself. # The offset in `fields`. This is relative from the start # of `@table`. table_offset = metadata[:table_offset] # The offset in `@values`. This is relative from the start # of `@table`. value_offset = metadata[:value_offset] # `@values` is placed just after `@table`. # `offset` is relative from `table_offset`. offset = table_size - table_offset + value_offset @table[table_offset, View::Table::VTABLE_OFFSET_SIZE] = [offset].pack("L<") field_offsets[field.index] = table_offset end end field_offsets.each_with_index do |offset, i| field_offsets[i] = 0 if offset.nil? end data = +"".b vtable = View::VTable.serialize(vtable_size, table_size, field_offsets) data.append_as_bytes(vtable) # We don't need "-" here because vtable_offset is subtracted # (not added). vtable_offset = vtable_size @table[0, View::Table::VTABLE_OFFSET_SIZE] = [vtable_offset].pack("l<") data.append_as_bytes(@table) align!(@values, LARGEST_ALIGNMENT_SIZE) data.append_as_bytes(@values) table_offset = vtable_size [data, table_offset] end
def initialize
def initialize @field_metadata = {} # vtable_offset. This is replaced later. @table = DUMMY_OFFSET.dup @values = +"".b end
def start
def start yield finish end