diff --git a/lib/protobuf/decoder.ex b/lib/protobuf/decoder.ex index b8609c76..7dbacf11 100644 --- a/lib/protobuf/decoder.ex +++ b/lib/protobuf/decoder.ex @@ -15,8 +15,7 @@ defmodule Protobuf.Decoder do bin |> build_message(struct(module), props) - |> reverse_repeated(repeated_fields) - |> Map.update!(:__unknown_fields__, &Enum.reverse/1) + |> reverse_repeated([:__unknown_fields__ | repeated_fields]) |> transform_module(module) end @@ -173,12 +172,12 @@ defmodule Protobuf.Decoder do defp handle_value(<>, field_number, wire_type, value, message, props) do case props.field_props do %{^field_number => %FieldProps{packed?: true, name_atom: name_atom} = prop} -> - new_message = update_in_message(message, name_atom, &value_for_packed(value, &1, prop)) + new_message = update_in_message(message, name_atom, value, &value_for_packed/3, prop) build_message(rest, new_message, props) %{^field_number => %FieldProps{wire_type: ^wire_type} = prop} -> key = field_key(prop, props) - new_message = update_in_message(message, key, &value_for_field(value, &1, prop)) + new_message = update_in_message(message, key, value, &value_for_field/3, prop) build_message(rest, new_message, props) # Repeated fields of primitive numeric types can be "packed". Their packed? flag will be @@ -187,7 +186,7 @@ defmodule Protobuf.Decoder do # https://developers.google.com/protocol-buffers/docs/encoding#packed %{^field_number => %FieldProps{repeated?: true, name_atom: name_atom} = prop} when wire_type == wire_delimited() -> - new_message = update_in_message(message, name_atom, &value_for_packed(value, &1, prop)) + new_message = update_in_message(message, name_atom, value, &value_for_packed/3, prop) build_message(rest, new_message, props) %{^field_number => %FieldProps{wire_type: expected, name: field}} -> @@ -374,16 +373,21 @@ defmodule Protobuf.Decoder do defp decode_fixed64(<<>>, _type, acc), do: acc - defp reverse_repeated(message, repeated_fields) do - Enum.reduce(repeated_fields, message, fn repeated_field, message_acc -> - case message_acc do - %{^repeated_field => values} when is_list(values) -> - %{message_acc | repeated_field => Enum.reverse(values)} + defp reverse_repeated(message, [repeated_field | rest]) do + message = + case message do + %{^repeated_field => [_, _ | _] = values} -> + %{message | repeated_field => Enum.reverse(values)} _other -> - message_acc + message end - end) + + reverse_repeated(message, rest) + end + + defp reverse_repeated(message, []) do + message end defp field_key(%FieldProps{oneof: nil, name_atom: key}, _message_props) do @@ -395,13 +399,16 @@ defmodule Protobuf.Decoder do key end - defp update_in_message(message, key, update_fun) do + # Receives an update_fun and calls it with value and props params to avoid + # the extra memory usage of creating an anonymous function with the two + # two params in it's context + defp update_in_message(message, key, value, update_fun, props) do current = case message do %_{^key => value} -> value %_{} -> nil end - Map.put(message, key, update_fun.(current)) + Map.put(message, key, update_fun.(value, current, props)) end end