-
Notifications
You must be signed in to change notification settings - Fork 10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix lists decoding #17
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,83 +3,117 @@ defmodule ExRLP.Decode do | |
|
||
@spec decode(binary(), keyword()) :: ExRLP.t() | ||
def decode(item, options \\ []) when is_binary(item) do | ||
case item | ||
|> unencode(Keyword.get(options, :encoding, :binary)) | ||
|> decode_item do | ||
[res] -> res | ||
[] -> nil | ||
els -> els | ||
end | ||
item | ||
|> unencode(Keyword.get(options, :encoding, :binary)) | ||
|> decode_item | ||
end | ||
|
||
@spec unencode(binary() | String.t(), atom()) :: binary() | ||
@spec unencode(binary(), atom()) :: binary() | ||
defp unencode(value, :binary), do: value | ||
defp unencode(value, :hex), do: Base.decode16!(value, case: :lower) | ||
defp unencode(value, :hex), do: decode_hex(value) | ||
|
||
@spec decode_item(binary(), ExRLP.t()) :: ExRLP.t() | ||
defp decode_item(rlp_binary, result \\ []) | ||
defp decode_item(rlp_binary, result \\ nil) | ||
|
||
# When we don't have any RLP left, return the accumulator | ||
defp decode_item(<<>>, result) do | ||
defp decode_item("", result) when is_list(result) do | ||
Enum.reverse(result) | ||
end | ||
|
||
# Decodes the head represents an item to be added directly | ||
# to the result. | ||
defp decode_item("", result) do | ||
result | ||
end | ||
|
||
defp decode_item(<<(<<prefix>>), tail::binary>>, result) when prefix < 128 do | ||
decode_item(tail, [<<prefix>> | result]) | ||
new_item = <<prefix>> | ||
|
||
new_result = add_new_item(result, new_item) | ||
|
||
decode_item(tail, new_result) | ||
end | ||
|
||
# Decodes medium length-binary? | ||
defp decode_item(<<(<<prefix>>), tail::binary>>, result) when prefix <= 183 do | ||
{new_item, new_tail} = decode_medium_binary(prefix, tail, 128) | ||
|
||
decode_item(new_tail, [new_item | result]) | ||
new_result = add_new_item(result, new_item) | ||
|
||
decode_item(new_tail, new_result) | ||
end | ||
|
||
# Decodes long length-binary? | ||
defp decode_item(<<(<<be_size_prefix>>), tail::binary>>, result) when be_size_prefix < 192 do | ||
{new_item, new_tail} = decode_long_binary(be_size_prefix, tail, 183) | ||
|
||
decode_item(new_tail, [new_item | result]) | ||
new_result = add_new_item(result, new_item) | ||
|
||
decode_item(new_tail, new_result) | ||
end | ||
|
||
defp decode_item(<<(<<be_size_prefix>>), tail::binary>>, result) when be_size_prefix == 192 do | ||
new_item = [] | ||
|
||
new_result = add_new_item(result, new_item) | ||
|
||
decode_item(tail, new_result) | ||
end | ||
|
||
defp decode_item(<<(<<prefix>>), tail::binary>>, result) when prefix <= 247 do | ||
{list, new_tail} = decode_medium_binary(prefix, tail, 192) | ||
|
||
new_result = add_decoded_list(result, list) | ||
|
||
decode_item(new_tail, new_result) | ||
end | ||
|
||
defp decode_item(<<(<<be_size_prefix>>), tail::binary>>, result) do | ||
{list, new_tail} = decode_long_binary(be_size_prefix, tail, 247) | ||
|
||
new_result = result |> add_decoded_list(list) | ||
|
||
decode_item(new_tail, new_result) | ||
end | ||
|
||
# Decodes an empty list | ||
defp decode_item(<<(<<be_size_prefix>>), tail::binary>>, result) | ||
when be_size_prefix == 192 do | ||
decode_item(tail, [[] | result]) | ||
@spec add_new_item(ExRLP.t(), ExRLP.t()) :: ExRLP.t() | ||
def add_new_item(nil, new_item) do | ||
new_item | ||
end | ||
|
||
defp decode_item(<<(<<prefix>>), tail::binary>>, result) do | ||
{list_bin, new_tail} = | ||
if prefix <= 247 do | ||
decode_medium_binary(prefix, tail, 192) | ||
else | ||
decode_long_binary(prefix, tail, 247) | ||
end | ||
def add_new_item(result, new_item) do | ||
[new_item | result] | ||
end | ||
|
||
@spec add_decoded_list(ExRLP.t(), binary()) :: ExRLP.t() | ||
defp add_decoded_list(nil, rlp_list_binary) do | ||
rlp_list_binary | ||
|> decode_item([]) | ||
|> Enum.reverse() | ||
end | ||
|
||
next_list = decode_item(list_bin, []) | ||
defp add_decoded_list(result, rlp_list_binary) do | ||
list_items = decode_item(rlp_list_binary, []) | ||
|
||
decode_item(new_tail, [next_list | result]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This does remove a tail-call optimization, right? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no, it's moved to |
||
[list_items | result] | ||
end | ||
|
||
@spec decode_medium_binary(integer(), binary(), integer()) :: {binary(), binary()} | ||
defp decode_medium_binary(length_prefix, tail, prefix) do | ||
item_length = length_prefix - prefix | ||
<<item::binary-size(item_length), new_tail::binary()>> = tail | ||
<<item::binary-size(item_length), new_tail::binary>> = tail | ||
|
||
{item, new_tail} | ||
end | ||
|
||
@spec decode_long_binary(integer(), binary(), integer()) :: {binary(), binary()} | ||
defp decode_long_binary(be_size_prefix, tail, prefix) do | ||
be_size = be_size_prefix - prefix | ||
<<be::binary-size(be_size), data::binary()>> = tail | ||
<<be::binary-size(be_size), data::binary>> = tail | ||
|
||
item_length = :binary.decode_unsigned(be) | ||
|
||
<<item::binary-size(item_length), new_tail::binary()>> = data | ||
<<item::binary-size(item_length), new_tail::binary>> = data | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. would there be an option to extract this into a function head? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think so because length is not constant There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what the warning suggest is that it could delay the binary creation if it was passed in a function call. |
||
|
||
{item, new_tail} | ||
end | ||
|
||
@spec decode_hex(binary()) :: binary() | ||
defp decode_hex(binary) do | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think this is used anywhere? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's used on line 13 |
||
Base.decode16!(binary, case: :lower) | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if this function is more clear than simply inline the list operation. Mostly, it obscures (in the code above) that this function is just a simple operation and nothing more complicated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, I agree. But we may have
nil
in the result variable.I can change it to
in every place where this function is used