Skip to content

Commit 7337cc2

Browse files
committed
Add encode_to_iodata support in Protobuf.JSON
1 parent 8cf602d commit 7337cc2

File tree

2 files changed

+42
-4
lines changed

2 files changed

+42
-4
lines changed

lib/protobuf/json.ex

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,8 @@ defmodule Protobuf.JSON do
121121
"""
122122
@spec encode!(struct, [encode_opt]) :: String.t() | no_return
123123
def encode!(struct, opts \\ []) do
124-
case encode(struct, opts) do
125-
{:ok, json} -> json
124+
case encode_to_iodata(struct, opts) do
125+
{:ok, iodata} -> IO.iodata_to_binary(iodata)
126126
{:error, error} -> raise error
127127
end
128128
end
@@ -169,9 +169,41 @@ defmodule Protobuf.JSON do
169169
"""
170170
@spec encode(struct, [encode_opt]) ::
171171
{:ok, String.t()} | {:error, EncodeError.t() | Exception.t()}
172-
def encode(%_{} = struct, opts \\ []) when is_list(opts) do
172+
def encode(struct, opts \\ []) when is_list(opts) do
173+
case encode_to_iodata(struct, opts) do
174+
{:ok, iodata} -> {:ok, IO.iodata_to_binary(iodata)}
175+
{:error, error} -> {:error, error}
176+
end
177+
end
178+
179+
@doc """
180+
Similar to `encode!/2`, but returns iodata
181+
182+
See `encode_to_iodata/2` for more information about when this function should
183+
be preferred over `encode!/2`.
184+
"""
185+
@spec encode_to_iodata!(struct, [encode_opt]) :: iodata()
186+
def encode_to_iodata!(struct, opts \\ []) when is_list(opts) do
187+
case encode_to_iodata(struct, opts) do
188+
{:ok, iodata} -> iodata
189+
{:error, error} -> raise error
190+
end
191+
end
192+
193+
@doc """
194+
Similar to `encode/2`, but returns iodata
195+
196+
This function should be preferred to encode/2, if the generated JSON will be
197+
handed over to one of the IO functions or sent over the socket. The Erlang
198+
runtime is able to leverage vectorised writes and avoid allocating a continuous
199+
buffer for the whole resulting string, lowering memory use and increasing
200+
performance.
201+
"""
202+
@spec encode_to_iodata(struct, [encode_opt]) ::
203+
{:ok, iodata()} | {:error, EncodeError.t() | Exception.t()}
204+
def encode_to_iodata(%_{} = struct, opts \\ []) when is_list(opts) do
173205
if jason = load_jason() do
174-
with {:ok, map} <- to_encodable(struct, opts), do: jason.encode(map)
206+
with {:ok, map} <- to_encodable(struct, opts), do: jason.encode_to_iodata(map)
175207
else
176208
{:error, EncodeError.new(:no_json_lib)}
177209
end

test/protobuf/json_test.exs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ defmodule Protobuf.JSONTest do
88
assert Protobuf.JSON.encode!(%TestMsg.Foo2{b: 10}) == ~S|{"b":"10"}|
99
end
1010

11+
test "encode_to_iodata variants encode to iodata" do
12+
assert iodata = Protobuf.JSON.encode_to_iodata!(%TestMsg.Foo2{b: 10})
13+
assert {:ok, ^iodata} = Protobuf.JSON.encode_to_iodata(%TestMsg.Foo2{b: 10})
14+
assert IO.iodata_to_binary(iodata) == ~S|{"b":"10"}|
15+
end
16+
1117
test "encoding string field with invalid UTF-8 data" do
1218
message = %Scalars{string: " \xff "}
1319
assert {:error, %Jason.EncodeError{}} = Protobuf.JSON.encode(message)

0 commit comments

Comments
 (0)