defmodule Protox.Encode do
@moduledoc """
This module contains the functions necessary to encode protobuf messages.
"""
import Protox.Guards
import Bitwise
use Protox.{
Float,
WireTypes
}
alias Protox.{
Varint,
Zigzag
}
@doc """
Encode a protobuf message into IO data.
## Example
msg = %Fiz.Foo{a: 3, b: %{1 => %Fiz.Baz{}}}
{:ok, iodata} = Protox.Encode.encode(msg)
"""
@deprecated "Use Protox.encode/1 instead"
@spec encode(struct()) :: {:ok, iodata()} | {:error, any()}
def encode(msg) do
# A compilation error at the line below usually happen if `msg`
# is not a struct generated by protox.
msg.__struct__.encode(msg)
end
@doc """
Throwing version of `encode/1`.
"""
@deprecated "Use Protox.encode!/1 instead"
@spec encode!(struct()) :: iodata() | no_return()
def encode!(msg) do
# A compilation error at the line below usually happen if `msg`
# is not a struct generated by protox.
msg.__struct__.encode!(msg)
end
@doc false
@spec make_key_bytes(Protox.Types.tag(), Protox.Types.type()) :: iodata
def make_key_bytes(tag, ty) do
Varint.encode(make_key(tag, ty))
end
@doc false
@spec make_key(Protox.Types.tag(), Protox.Types.type()) :: non_neg_integer
def make_key(tag, ty) when is_primitive_varint(ty), do: tag <<< 3 ||| @wire_varint
def make_key(tag, {:enum, _}), do: tag <<< 3 ||| @wire_varint
def make_key(tag, ty) when is_primitive_fixed64(ty), do: tag <<< 3 ||| @wire_64bits
def make_key(tag, ty) when is_delimited(ty), do: tag <<< 3 ||| @wire_delimited
def make_key(tag, {:message, _}), do: tag <<< 3 ||| @wire_delimited
def make_key(tag, :packed), do: tag <<< 3 ||| @wire_delimited
def make_key(tag, :map_entry), do: tag <<< 3 ||| @wire_delimited
def make_key(tag, ty) when is_primitive_fixed32(ty), do: tag <<< 3 ||| @wire_32bits
@doc false
@spec encode_varint_signed(integer) :: iodata
def encode_varint_signed(value) do
value |> Zigzag.encode() |> Varint.encode()
end
@doc false
@spec encode_varint_64(integer) :: iodata
def encode_varint_64(value) do
<<res::unsigned-native-64>> = <<value::signed-native-64>>
Varint.encode(res)
end
@doc false
@spec encode_varint_32(integer) :: iodata
def encode_varint_32(value) when value < 0 do
encode_varint_64(value)
end
@doc false
def encode_varint_32(value) do
<<res::unsigned-native-32>> = <<value::signed-native-32>>
Varint.encode(res)
end
@doc false
@spec encode_bool(boolean) :: binary
def encode_bool(false), do: <<0>>
def encode_bool(true), do: <<1>>
@doc false
@spec encode_int32(integer) :: iodata
def encode_int32(value), do: encode_varint_32(value)
@doc false
@spec encode_int64(integer) :: iodata
def encode_int64(value), do: encode_varint_64(value)
@doc false
@spec encode_sint32(integer) :: iodata
def encode_sint32(value), do: encode_varint_signed(value)
@doc false
@spec encode_sint64(integer) :: iodata
def encode_sint64(value), do: encode_varint_signed(value)
@doc false
@spec encode_uint32(non_neg_integer) :: iodata
def encode_uint32(value), do: encode_varint_32(value)
@doc false
@spec encode_uint64(non_neg_integer) :: iodata
def encode_uint64(value), do: encode_varint_64(value)
@doc false
@spec encode_fixed64(integer) :: binary
def encode_fixed64(value), do: <<value::little-64>>
@doc false
@spec encode_sfixed64(integer) :: binary
def encode_sfixed64(value), do: <<value::signed-little-64>>
@doc false
@spec encode_fixed32(integer) :: binary
def encode_fixed32(value), do: <<value::little-32>>
@doc false
@spec encode_sfixed32(integer) :: binary
def encode_sfixed32(value), do: <<value::signed-little-32>>
@doc false
@spec encode_double(float | atom) :: binary
def encode_double(:infinity), do: @positive_infinity_64
def encode_double(:"-infinity"), do: @negative_infinity_64
def encode_double(:nan), do: @nan_64
def encode_double(value), do: <<value::float-little-64>>
@doc false
@spec encode_float(float | atom) :: binary
def encode_float(:infinity), do: @positive_infinity_32
def encode_float(:"-infinity"), do: @negative_infinity_32
def encode_float(:nan), do: @nan_32
def encode_float(value), do: <<value::float-little-32>>
@doc false
@spec encode_enum(integer) :: iodata
def encode_enum(value), do: encode_varint_32(value)
@doc false
@spec encode_string(String.t()) :: iodata
def encode_string(value) do
[Varint.encode(byte_size(value)), value]
end
@doc false
@spec encode_bytes(binary) :: iodata
def encode_bytes(value) do
[Varint.encode(byte_size(value)), value]
end
@doc false
@spec encode_message(struct) :: iodata
def encode_message(value) do
encoded = value |> encode!() |> :binary.list_to_bin()
[Varint.encode(byte_size(encoded)), encoded]
end
end