Packages
This is a flatbuffers implementation in Elixir. In contrast to existing implementations there is no need to compile code from a schema. Instead, data and schemas are processed dynamically at runtime, offering greater flexibility.
Current section
Files
Jump to
Current section
Files
lib/random_access.ex
defmodule Eflatbuffers.RandomAccess do
alias Eflatbuffers.Utils
def get([], root_table, 0, data, schema) do
Eflatbuffers.Reader.read(root_table, 0, data, schema)
end
def get([key | keys], {:table, %{ name: table_name }}, table_pointer_pointer, data, {tables, _} = schema) when is_atom(key) do
{:table, table_options} = Map.get(tables, table_name)
{index, type} = Map.get(table_options.indices, key)
{type_concrete, index_concrete} =
case type do
{:union, %{name: union_name}} ->
# we are getting the field type from the field
# and the data is actually in the next field
# since the schema does not contain the *_type field
type_pointer = data_pointer(index, table_pointer_pointer, data)
union_type_index = Eflatbuffers.Reader.read({:byte, %{ default: 0 }}, type_pointer, data, schema) - 1
{:union, union_definition} = Map.get(tables, union_name)
union_type = Map.get(union_definition.members, union_type_index)
type = {:table, %{ name: union_type }}
{type, index + 1}
_ ->
{type, index}
end
case data_pointer(index_concrete, table_pointer_pointer, data) do
false ->
# we encountered a null pointer, we return nil
# whether we reached the end of the path or not
nil
data_pointer ->
case keys do
[] ->
# this is the terminus where we switch to eager reading
Eflatbuffers.Reader.read(type_concrete, data_pointer, data, schema)
_ ->
# there are keys left, we recurse
get(keys, type_concrete, data_pointer, data, schema)
end
end
end
def get([index | keys], {:vector, %{ type: type }}, vector_pointer, data, schema) when is_integer(index) do
<< _ :: binary-size(vector_pointer), vector_offset :: unsigned-little-size(32), _ :: binary >> = data
vector_length_pointer = vector_pointer + vector_offset
<< _ :: binary-size(vector_length_pointer), vector_length :: unsigned-little-size(32), _ :: binary >> = data
element_offset =
case Utils.scalar?(type) do
true ->
Utils.scalar_size(Utils.extract_scalar_type(type, schema))
false ->
4
end
data_offset = vector_length_pointer + 4 + index * element_offset
case vector_length < index + 1 do
true ->
throw(:index_out_of_range)
false ->
case keys do
[] ->
Eflatbuffers.Reader.read(type, data_offset, data, schema)
_ ->
get(keys, type, data_offset, data, schema)
end
end
end
def data_pointer(index, table_pointer_pointer, data) do
<< _ :: binary-size(table_pointer_pointer), table_offset :: little-size(32), _ :: binary >> = data
table_pointer = table_pointer_pointer + table_offset
<< _ :: binary-size(table_pointer), vtable_offset :: little-signed-size(32), _ :: binary >> = data
vtable_pointer = table_pointer - vtable_offset + 4 + index * 2
<< _ :: binary-size(vtable_pointer), data_offset :: little-size(16), _ :: binary >> = data
case data_offset do
0 -> false
_ -> table_pointer + data_offset
end
end
def index_and_type(fields, key) do
{{^key, type}, index} = Enum.find(Enum.with_index(fields), fn({{name, _}, _}) -> name == key end)
{index, type}
end
end