Current section
Files
Jump to
Current section
Files
lib/fdb/coder/dynamic.ex
defmodule FDB.Coder.Dynamic do
use FDB.Coder.Behaviour
@spec new() :: FDB.Coder.t()
def new do
coders = %{
byte_string: FDB.Coder.ByteString.new(),
unicode_string: FDB.Coder.UnicodeString.new(),
integer: FDB.Coder.Integer.new(),
boolean: FDB.Coder.Boolean.new(),
arbitrary_integer: FDB.Coder.ArbitraryInteger.new(),
uuid: FDB.Coder.UUID.new(),
float32: FDB.Coder.Float.new(),
float64: FDB.Coder.Float.new(64),
versionstamp: FDB.Coder.Versionstamp.new()
}
%FDB.Coder{module: __MODULE__, opts: coders}
end
@impl true
def encode({nil, nil}, _coders), do: <<0x00>>
def encode({tag, value}, coders)
when tag in [
:byte_string,
:unicode_string,
:integer,
:boolean,
:arbitrary_integer,
:uuid,
:float32,
:float64,
:versionstamp
] do
coder = coders[tag]
coder.module.encode(value, coder.opts)
end
def encode({:nested, values}, coders) do
encoded =
Enum.map(Tuple.to_list(values), fn
{nil, nil} -> <<0x00, 0xFF>>
value -> encode(value, coders)
end)
|> Enum.join(<<>>)
<<0x05>> <> encoded <> <<0x00>>
end
def encode(values, coders) when is_tuple(values) do
Enum.map(Tuple.to_list(values), fn value ->
encode(value, coders)
end)
|> Enum.join(<<>>)
end
@impl true
def decode(rest, coders) do
{loop(rest, coders, {}), <<>>}
end
defp loop(<<>>, _coders, acc), do: acc
defp loop(rest, coders, acc) do
{acc, rest} = do_decode(rest, coders, acc)
loop(rest, coders, acc)
end
defp do_decode(<<0x00>> <> rest, _coders, acc),
do: {Tuple.append(acc, {nil, nil}), rest}
defp do_decode(<<0x01>> <> _rest = full, coders, acc),
do: apply_coder(:byte_string, full, coders, acc)
defp do_decode(<<0x02>> <> _rest = full, coders, acc),
do: apply_coder(:unicode_string, full, coders, acc)
defp do_decode(<<0x20>> <> _rest = full, coders, acc),
do: apply_coder(:float32, full, coders, acc)
defp do_decode(<<0x21>> <> _rest = full, coders, acc),
do: apply_coder(:float64, full, coders, acc)
defp do_decode(<<0x30>> <> _rest = full, coders, acc),
do: apply_coder(:uuid, full, coders, acc)
defp do_decode(<<0x33>> <> _rest = full, coders, acc),
do: apply_coder(:versionstamp, full, coders, acc)
defp do_decode(<<0x05>> <> rest, coders, acc) do
{value, rest} = do_decode_nested_tuple(rest, coders, {})
{Tuple.append(acc, {:nested, value}), rest}
end
defp do_decode(<<x::integer-size(8), _rest::binary>> = full, coders, acc) when x in 0x0C..0x1C,
do: apply_coder(:integer, full, coders, acc)
defp do_decode(<<x::integer-size(8), _rest::binary>> = full, coders, acc)
when x in [0x26, 0x27],
do: apply_coder(:boolean, full, coders, acc)
defp do_decode(<<x::integer-size(8), _rest::binary>> = full, coders, acc)
when x in [0x1D, 0x0B],
do: apply_coder(:arbitrary_integer, full, coders, acc)
defp apply_coder(c, rest, coders, acc) do
{value, rest} = coders[c].module.decode(rest, coders[c].opts)
{Tuple.append(acc, {c, value}), rest}
end
defp do_decode_nested_tuple(<<0x00, 0xFF>> <> _rest = full, coders, values) do
{values, <<0xFF>> <> rest} = do_decode(full, coders, values)
do_decode_nested_tuple(rest, coders, values)
end
defp do_decode_nested_tuple(<<0x00>> <> rest, _coders, values), do: {values, rest}
defp do_decode_nested_tuple(rest, coders, values) do
{values, rest} = do_decode(rest, coders, values)
do_decode_nested_tuple(rest, coders, values)
end
end