Skip to content

Instantly share code, notes, and snippets.

@marysaka
Created July 28, 2017 22:28
Show Gist options
  • Save marysaka/0f52ef91629baff409b456c88c0e9ff5 to your computer and use it in GitHub Desktop.
Save marysaka/0f52ef91629baff409b456c88c0e9ff5 to your computer and use it in GitHub Desktop.
Simple RESP (REdis Serialization Protocol) decoder/encoder in Elixir
defmodule Redis.Protocol.Decoder do
def decode(<<"*", elem_count :: binary-size(1), "\r\n", rest :: binary>>) do
elem_count = elem_count |> String.to_integer
decode_array(rest, elem_count, [])
end
def decode(<<"+", rest :: binary>>), do: decode_simple_string(rest)
def decode(<<"-", rest :: binary>>), do: decode_error_string(rest)
def decode(<<"$", size :: binary-size(1), rest :: binary>>), do: decode_bulk_string(size |> String.to_integer, rest)
def decode(<<":", rest :: binary>>), do: decode_integer_string(rest)
def decode(resp), do: {:error, "Cannot be parsed!", resp}
defp decode_simple_string(rest) do
{end_pos,_} = :binary.match(rest, "\r\n")
<<str :: binary-size(end_pos), "\r\n", rest :: binary>> = rest
{:simple_str, str, rest}
end
defp decode_error_string(rest), do: decode_simple_string(rest) |> put_elem(0, :error_str)
defp decode_integer_string(rest) do
{_, value, rest} = decode_simple_string(rest)
{:integer, value |> String.to_integer, rest}
end
defp decode_bulk_string(-1, rest), do: {:bulk_str, nil, rest}
defp decode_bulk_string(count, rest) do
<<"\r\n", bulk :: binary-size(count), "\r\n", rest :: binary>> = rest
{:bulk_str, bulk, rest}
end
defp decode_array(_, 0, accumulator), do: accumulator
defp decode_array(raw, count, accumulator) do
{type, value, raw} = decode(raw)
decode_array(raw, count - 1, accumulator ++ [{type, value}])
end
end
defmodule Redis.Protocol.Encoder do
def encode(data) when is_list(data), do: "*#{Enum.count(data)}\r\n" <>encode_array(data, "")
def encode({:simple_str, value}), do: "+" <> value <> "\r\n"
def encode({:error_str, value}), do: "-" <> value <> "\r\n"
def encode({:integer, value}), do: ":" <> Integer.to_string(value) <> "\r\n"
def encode({:bulk_str, nil}), do: "$-1\r\n"
def encode({:bulk_str, value}), do: "$#{String.length(value)}\r\n#{value}\r\n"
def encode(_), do: nil
defp encode_array([head | tail], accumulator), do: encode_array(tail, accumulator <> encode(head))
defp encode_array([], accumulator), do: accumulator
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment