Packages
protox
0.16.2
2.0.9
2.0.8
2.0.7
2.0.6
2.0.5
2.0.4
2.0.3
2.0.2
2.0.1
2.0.0
2.0.0-dev
1.7.8
1.7.7
1.7.6
1.7.5
1.7.4
retired
1.7.3
1.7.2
1.7.1
1.7.0
1.6.10
1.6.9
1.6.8
1.6.7
1.6.6
1.6.5
1.6.4
1.6.3
1.6.2
1.6.1
1.6.0
1.5.1
1.5.0
1.4.0
1.3.2
1.3.1
1.3.0
1.2.4
1.2.3
1.2.2
1.2.1
1.2.0
1.1.1
1.1.0
1.0.0
0.25.0
0.24.0
0.23.1
0.23.0
0.22.0
0.21.0
0.20.0
0.19.1
0.19.0
0.18.0
0.17.0
0.16.2
0.16.1
0.16.0
0.15.2
0.15.1
0.15.0
0.14.0
0.13.0
0.12.1
0.12.0
0.11.1
0.11.0
0.10.0
0.9.1
0.9.0
0.8.0
0.7.1
0.7.0
A fast, easy to use and 100% conformant Elixir library for Google Protocol Buffers (aka protobuf)
Current section
Files
Jump to
Current section
Files
lib/protox/decode.ex
defmodule Protox.Decode do
@moduledoc false
# Decodes a binary into a message.
use Bitwise
use Protox.Float
use Protox.WireTypes
alias Protox.{
Types,
Varint,
Zigzag
}
@spec decode!(binary, atom, [atom]) :: struct | no_return
def decode!(bytes, mod, required_fields) do
{msg, set_fields} = parse_key_value([], bytes, mod.defs(), struct(mod.__struct__))
case required_fields -- set_fields do
[] -> msg
missing_fields -> raise "Missing required fields #{inspect(missing_fields)}"
end
end
@spec decode(binary, atom, [atom]) :: {:ok, struct} | {:error, any}
def decode(bytes, mod, required_fields) do
try do
{:ok, decode!(bytes, mod, required_fields)}
rescue
e -> {:error, e}
end
end
# -- Private
defmodule MapEntry do
@moduledoc false
defstruct key: nil,
value: nil,
__uf__: []
@spec get_unknown_fields_name() :: :__uf__
def get_unknown_fields_name(), do: :__uf__
end
@spec parse_key_value([atom], binary, map, struct) :: {struct, [atom]}
defp parse_key_value(set_fields, <<>>, _, msg) do
{msg, set_fields}
end
defp parse_key_value(set_fields, bytes, defs, msg) do
{tag, wire_type, rest} = parse_key(bytes)
if tag == 0 do
raise "Illegal field with tag 0"
end
field = defs[tag]
{new_set_fields, new_msg, new_rest} =
if field do
{name, kind, type} = field
{value, new_rest} = parse_value(rest, wire_type, type)
{[elem(field, 0) | set_fields], set_field(msg, name, kind, value), new_rest}
else
{new_msg, new_rest} = parse_unknown(msg, tag, wire_type, rest)
{set_fields, new_msg, new_rest}
end
parse_key_value(new_set_fields, new_rest, defs, new_msg)
end
# Get the key's tag and wire type.
@spec parse_key(binary) :: {non_neg_integer, non_neg_integer, binary}
def parse_key(bytes) do
{key, rest} = Varint.decode(bytes)
{key >>> 3, key &&& 0b111, rest}
end
@spec parse_value(binary, Types.tag(), atom) :: {any, binary}
defp parse_value(bytes, @wire_delimited, type) do
{len, new_bytes} = Varint.decode(bytes)
<<delimited::binary-size(len), rest::binary>> = new_bytes
{parse_delimited(delimited, type), rest}
end
defp parse_value(bytes, _, type) do
parse_single(bytes, type)
end
@spec parse_single(binary, atom) :: {any, binary}
defp parse_single(<<@positive_infinity_64, rest::binary>>, :double) do
{:infinity, rest}
end
defp parse_single(<<@negative_infinity_64, rest::binary>>, :double) do
{:"-infinity", rest}
end
defp parse_single(<<_::48, 0b1111::4, _::4, _::1, 0b1111111::7, rest::binary>>, :double) do
{:nan, rest}
end
defp parse_single(<<value::float-little-64, rest::binary>>, :double) do
{value, rest}
end
defp parse_single(<<value::signed-little-64, rest::binary>>, :sfixed64) do
{value, rest}
end
defp parse_single(<<value::signed-little-64, rest::binary>>, :fixed64) do
{value, rest}
end
defp parse_single(<<@positive_infinity_32, rest::binary>>, :float) do
{:infinity, rest}
end
defp parse_single(<<@negative_infinity_32, rest::binary>>, :float) do
{:"-infinity", rest}
end
defp parse_single(<<_::16, 1::1, _::7, _::1, 0b1111111::7, rest::binary>>, :float) do
{:nan, rest}
end
defp parse_single(<<value::float-little-32, rest::binary>>, :float) do
{value, rest}
end
defp parse_single(<<value::signed-little-32, rest::binary>>, :sfixed32) do
{value, rest}
end
defp parse_single(<<value::signed-little-32, rest::binary>>, :fixed32) do
{value, rest}
end
defp parse_single(bytes, type) do
{value, rest} = Varint.decode(bytes)
{varint_value(value, type), rest}
end
defp parse_delimited(bytes, :string), do: bytes
defp parse_delimited(bytes, :bytes), do: bytes
defp parse_delimited(bytes, type = {:enum, _}), do: parse_repeated_varint([], bytes, type)
defp parse_delimited(bytes, {:message, m}), do: decode!(bytes, m, m.get_required_fields())
defp parse_delimited(bytes, :int32), do: parse_repeated_varint([], bytes, :int32)
defp parse_delimited(bytes, :uint32), do: parse_repeated_varint([], bytes, :uint32)
defp parse_delimited(bytes, :sint32), do: parse_repeated_varint([], bytes, :sint32)
defp parse_delimited(bytes, :int64), do: parse_repeated_varint([], bytes, :int64)
defp parse_delimited(bytes, :uint64), do: parse_repeated_varint([], bytes, :uint64)
defp parse_delimited(bytes, :sint64), do: parse_repeated_varint([], bytes, :sint64)
defp parse_delimited(bytes, :bool), do: parse_repeated_varint([], bytes, :bool)
defp parse_delimited(bytes, :fixed32), do: parse_repeated_fixed([], bytes, :fixed32)
defp parse_delimited(bytes, :sfixed32), do: parse_repeated_fixed([], bytes, :sfixed32)
defp parse_delimited(bytes, :float), do: parse_repeated_fixed([], bytes, :float)
defp parse_delimited(bytes, :fixed64), do: parse_repeated_fixed([], bytes, :fixed64)
defp parse_delimited(bytes, :sfixed64), do: parse_repeated_fixed([], bytes, :sfixed64)
defp parse_delimited(bytes, :double), do: parse_repeated_fixed([], bytes, :double)
defp parse_delimited(bytes, {map_key_type, map_value_type}) do
defs = %{
1 => {:key, {:default, :dummy}, map_key_type},
2 => {:value, {:default, :dummy}, map_value_type}
}
{%MapEntry{key: map_key, value: map_value}, _} = parse_key_value([], bytes, defs, %MapEntry{})
map_key2 =
case map_key do
nil -> Protox.Default.default(map_key_type)
_ -> map_key
end
map_value2 =
case map_value do
nil -> Protox.Default.default(map_value_type)
_ -> map_value
end
{map_key2, map_value2}
end
defp parse_repeated_varint(acc, <<>>, _) do
Enum.reverse(acc)
end
defp parse_repeated_varint(acc, bytes, type) do
{value, rest} = Varint.decode(bytes)
parse_repeated_varint([varint_value(value, type) | acc], rest, type)
end
defp parse_repeated_fixed(acc, <<>>, _) do
Enum.reverse(acc)
end
defp parse_repeated_fixed(acc, bytes, type) do
{value, rest} = parse_single(bytes, type)
parse_repeated_fixed([value | acc], rest, type)
end
@spec varint_value(non_neg_integer, atom) :: integer
defp varint_value(value, :bool), do: value != 0
defp varint_value(value, :sint32), do: Zigzag.decode(value)
defp varint_value(value, :sint64), do: Zigzag.decode(value)
defp varint_value(value, :uint32), do: value
defp varint_value(value, :uint64), do: value
defp varint_value(value, {:enum, mod}) do
<<res::signed-64>> = <<value::64>>
mod.decode(res)
end
defp varint_value(value, :int32) do
<<res::signed-32>> = <<value::32>>
res
end
defp varint_value(value, :int64) do
<<res::signed-64>> = <<value::64>>
res
end
@spec parse_unknown(struct, non_neg_integer, Types.tag(), binary) :: {struct, binary}
def parse_unknown(msg, tag, @wire_varint, bytes) do
{unknown_bytes, rest} = get_unknown_varint_bytes(<<>>, bytes)
{add_unknown_field(msg, tag, @wire_varint, unknown_bytes), rest}
end
def parse_unknown(msg, tag, @wire_64bits, <<unknown_bytes::64, rest::binary>>) do
{add_unknown_field(msg, tag, @wire_64bits, <<unknown_bytes::64>>), rest}
end
def parse_unknown(msg, tag, @wire_delimited, bytes) do
{len, new_bytes} = Varint.decode(bytes)
<<unknown_bytes::binary-size(len), rest::binary>> = new_bytes
{add_unknown_field(msg, tag, @wire_delimited, unknown_bytes), rest}
end
def parse_unknown(msg, tag, @wire_32bits, <<unknown_bytes::32, rest::binary>>) do
{add_unknown_field(msg, tag, @wire_32bits, <<unknown_bytes::32>>), rest}
end
defp get_unknown_varint_bytes(acc, <<0::1, b::7, rest::binary>>) do
{<<acc::binary, 0::1, b::7>>, rest}
end
defp get_unknown_varint_bytes(acc, <<1::1, b::7, rest::binary>>) do
get_unknown_varint_bytes(<<acc::binary, 1::1, b::7>>, rest)
end
defp add_unknown_field(msg, tag, wire_type, bytes) do
unknown_fields_name = msg.__struct__.get_unknown_fields_name()
previous = Map.fetch!(msg, unknown_fields_name)
struct!(msg, [{unknown_fields_name, [{tag, wire_type, bytes} | previous]}])
end
# Set the field correponding to `tag` in `msg` with `value`.
defp set_field(msg, name, kind, value) do
field_value =
case kind do
:map ->
previous = Map.fetch!(msg, name)
{name, Map.put(previous, elem(value, 0), elem(value, 1))}
{:oneof, parent_field} ->
{parent_field, {name, value}}
{:default, _} ->
{name, value}
# repeated
_ ->
previous = Map.fetch!(msg, name)
{name, previous ++ List.wrap(value)}
end
struct!(msg, [field_value])
end
end