Skip to content
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

Change default encoding to binary, instead of hex #3

Merged
merged 2 commits into from
Aug 2, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 25 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,23 @@ And run:
Use ExRLP.encode/1 method to encode an item to RLP representation. An item can be nonnegative integer, binary or list. List can contain integers, binaries or lists.

```elixir

## Examples

iex(1)> "dog" |> ExRLP.encode
"83646f67"

iex(2)> 1000 |> ExRLP.encode
"8203e8"

iex(3)> [ [ [], [] ], [] ] |> ExRLP.encode
"c4c2c0c0c0"
iex(1)> "dog" |> ExRLP.encode(encoding: :hex)
"83646f67"

iex(2)> "dog" |> ExRLP.encode(encoding: :binary)
<<0x83, 0x64, 0x6f, 0x67>>

iex(3)> 1000 |> ExRLP.encode(encoding: :hex)
"8203e8"

# Default encoding is binary
iex(3)> 1000 |> ExRLP.encode
<<0x82, 0x03, 0xe8>>

iex(4)> [ [ [], [] ], [] ] |> ExRLP.encode(encoding: :hex)
"c4c2c0c0c0"
```

Use ExRLP.decode/1 method to decode a rlp encoded data. All items except lists are decoded as binaries so additional deserialization is needed if initially an item of another type was encoded.
Expand All @@ -47,14 +53,14 @@ Use ExRLP.decode/1 method to decode a rlp encoded data. All items except lists a

## Examples

iex(1)> "83646f67" |> ExRLP.decode
"dog"
iex(2)> "8203e8" |> ExRLP.decode |> :binary.decode_unsigned
1000
iex(3)> "c4c2c0c0c0" |> ExRLP.decode
[[[], []], []]
iex(1)> "83646f67" |> ExRLP.decode(:binary, encoding: :hex)
"dog"

iex(2)> "8203e8" |> ExRLP.decode(:binary, encoding: :hex) |> :binary.decode_unsigned
1000

iex(3)> "c4c2c0c0c0" |> ExRLP.decode(:binary, encoding: :hex)
[[[], []], []]
```

More examples can be found in test files.
Expand All @@ -70,10 +76,6 @@ Custom protocols for Map have already been implemented in ExRLP:
defimpl ExRLP.Encoder, for: Map do
alias ExRLP.Encode

def encode(map, _) when map_size(map) < 1 do
"827b7d"
end

def encode(map, _) do
map
|> Map.keys
Expand All @@ -89,10 +91,6 @@ end
defimpl ExRLP.Decoder, for: BitString do
alias ExRLP.Decode

def decode("827b7d", :map, _) do
%{}
end

def decode(value, :map, options) do
keys =
options
Expand All @@ -114,10 +112,10 @@ end
```
So now it's possible to encode/decode maps:
```elixir
iex(1)> %{name: "Vitalik", surname: "Buterin"} |> ExRLP.encode
iex(1)> %{name: "Vitalik", surname: "Buterin"} |> ExRLP.encode(encoding: :hex)
"d087566974616c696b874275746572696e"

iex(2)> "d087566974616c696b874275746572696e" |> ExRLP.decode(:map, keys: [:surname, :name])
iex(2)> "d087566974616c696b874275746572696e" |> ExRLP.decode(:map, keys: [:surname, :name], encoding: :hex)
%{name: "Vitalik", surname: "Buterin"}
```

Expand Down
99 changes: 96 additions & 3 deletions lib/ex_rlp.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,104 @@ defmodule ExRLP do

@moduledoc File.read!("#{__DIR__}/../README.md")

def encode(item, options \\ nil) do
@type t :: nil | [t] | binary()

@doc """
Given an RLP structure, returns the encoding as a string.

## Examples

iex> ExRLP.encode("dog")
<<0x83, ?d, ?o, ?g>>

iex> ExRLP.encode("dog", encoding: :hex)
"83646f67"

iex> ExRLP.encode(Enum.join(for _ <- 1..60, do: "A"))
<<184, 60, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65>>

iex> ExRLP.encode(["cat", "dog"])
<<0xc8, 0x83, ?c, ?a, ?t, 0x83, ?d, ?o, ?g>>

iex> ExRLP.encode("")
<<0x80>>

iex> ExRLP.encode([])
<<0xc0>>

iex> ExRLP.encode("\x0f")
<<0x0f>>

iex> ExRLP.encode(15)
<<0x0f>>

iex> ExRLP.encode(15_000_000_000_000_000_000_000_000_000_000_000)
<<143, 2, 227, 142, 158, 4, 75, 160, 83, 84, 85, 150, 0, 0, 0, 0>>

iex> ExRLP.encode(1024)
<<0x82, 0x04, 0x00>>

iex> ExRLP.encode("\x04\x00")
<<0x82, 0x04, 0x00>>

iex> ExRLP.encode([[],[[]],[[],[[]]]])
<<0xc7, 0xc0, 0xc1, 0xc0, 0xc3, 0xc0, 0xc1, 0xc0>>

iex> ExRLP.encode(for _ <- 1..60, do: [])
<<248, 60, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192>>
"""
@spec encode(t, keyword()) :: binary()
def encode(item, options \\ []) do
item |> Encoder.encode(options)
end

def decode(item, type \\ :binary, options \\ nil) do
@doc """
Given an RLP-encoded string, returns a decoded RPL structure (which is an array of RLP structures or binaries).

## Examples

iex> ExRLP.decode(<<>>)
nil

iex> ExRLP.decode(<<0x83, ?d, ?o, ?g>>)
"dog"

iex> ExRLP.decode("83646f67", :binary, encoding: :hex)
"dog"

iex> ExRLP.decode(<<184, 60, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65>>)
Enum.join(for _ <- 1..60, do: "A")

iex> ExRLP.decode(<<0xc8, 0x83, ?c, ?a, ?t, 0x83, ?d, ?o, ?g>>)
["cat", "dog"]

iex> ExRLP.decode(<<198, 51, 132, 99, 111, 111, 108>>)
["3", "cool"]

iex> ExRLP.decode(<<0x80>>)
""

iex> ExRLP.decode(<<0xc0>>)
[]

iex> ExRLP.decode(<<0x0f>>)
<<0x0f>>

iex> ExRLP.decode(<<0x82, 0x04, 0x00>>)
"\x04\x00"

iex> ExRLP.decode(<<0xc7, 0xc0, 0xc1, 0xc0, 0xc3, 0xc0, 0xc1, 0xc0>>)
[[],[[]],[[],[[]]]]

iex> ExRLP.decode(<<248, 60, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192>>)
for _ <- 1..60, do: []

iex> ExRLP.decode(<<143, 2, 227, 142, 158, 4, 75, 160, 83, 84, 85, 150, 0, 0, 0, 0>>) |> :binary.decode_unsigned
15_000_000_000_000_000_000_000_000_000_000_000
"""
@spec decode(binary(), atom(), keyword()) :: t
def decode(item, type \\ :binary, options \\ []) do
item |> Decoder.decode(type, options)
end
end

end
29 changes: 19 additions & 10 deletions lib/ex_rlp/decoder.ex
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
defmodule ExRLP.Decode do
@moduledoc false

def decode(item) when is_binary(item) do
@spec decode(binary(), :binary | :hex) :: ExRLP.t
def decode(item, encoding) when is_binary(item) do
item
|> decode_hex
|> maybe_decode_hex(encoding)
|> decode_item
end

@spec maybe_decode_hex(binary(), atom()) :: binary()
defp maybe_decode_hex(value, :binary), do: value
defp maybe_decode_hex(value, :hex), do: decode_hex(value)

@spec decode_item(binary(), ExRLP.t) :: ExRLP.t
defp decode_item(rlp_binary, result \\ nil)

defp decode_item("", result) do
Expand Down Expand Up @@ -61,6 +67,7 @@ defmodule ExRLP.Decode do
decode_item(new_tail, new_result)
end

@spec add_new_item(ExRLP.t, ExRLP.t) :: ExRLP.t
def add_new_item(nil, new_item) do
new_item
end
Expand All @@ -69,6 +76,7 @@ defmodule ExRLP.Decode do
result ++ [new_item]
end

@spec add_decoded_list(ExRLP.t, binary()) :: ExRLP.t
defp add_decoded_list(nil, rlp_list_binary) do
decode_item(rlp_list_binary, [])
end
Expand All @@ -79,13 +87,15 @@ defmodule ExRLP.Decode do
result ++ [list_items]
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, 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
Expand All @@ -96,6 +106,7 @@ defmodule ExRLP.Decode do
{item, new_tail}
end

@spec decode_hex(binary()) :: binary()
defp decode_hex(binary) do
{:ok, decoded_binary} = binary |> Base.decode16(case: :lower)

Expand All @@ -110,18 +121,16 @@ end
defimpl ExRLP.Decoder, for: BitString do
alias ExRLP.Decode

def decode("827b7d", :map, _) do
%{}
end

@spec decode(binary(), atom(), keyword()) :: ExRLP.t
def decode(value, type \\ :binary, options \\ [])
def decode(value, :map, options) do
keys =
options
|> Keyword.fetch!(:keys)
|> Keyword.get(:keys, [])
|> Enum.sort

value
|> Decode.decode
|> Decode.decode(Keyword.get(options, :encoding, :binary))
|> Enum.with_index
|> Enum.reduce(%{}, fn({value, index}, acc) ->
key = keys |> Enum.at(index)
Expand All @@ -130,7 +139,7 @@ defimpl ExRLP.Decoder, for: BitString do
end)
end

def decode(value, :binary, _) do
value |> Decode.decode
def decode(value, :binary, options) do
value |> Decode.decode(Keyword.get(options, :encoding, :binary))
end
end
Loading