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