Current section

Files

Jump to
memcachex lib memcache protocol.ex
Raw

lib/memcache/protocol.ex

# https://github.com/memcached/old-wiki/blob/master/MemcacheBinaryProtocol.wiki
defmodule Memcache.Protocol do
@moduledoc false
import Memcache.BinaryUtils
alias Memcache.BinaryUtils.Header
def to_binary(:QUIT, opaque) do
[
bcat([
request(),
opb(:QUIT),
<<0x00::size(16)>>,
<<0x00>>,
datatype(),
reserved(),
<<0x00::size(32)>>
]),
<<opaque::size(32)>>,
<<0x00::size(64)>>
]
end
def to_binary(:QUITQ, opaque) do
[
bcat([
request(),
opb(:QUITQ),
<<0x00::size(16)>>,
<<0x00>>,
datatype(),
reserved(),
<<0x00::size(32)>>
]),
<<opaque::size(32)>>,
<<0x00::size(64)>>
]
end
def to_binary(:NOOP, opaque) do
[
bcat([
request(),
opb(:NOOP),
<<0x00::size(16)>>,
<<0x00>>,
datatype(),
reserved(),
<<0x00::size(32)>>
]),
<<opaque::size(32)>>,
<<0x00::size(64)>>
]
end
def to_binary(:VERSION, opaque) do
[
bcat([
request(),
opb(:VERSION),
<<0x00::size(16)>>,
<<0x00>>,
datatype(),
reserved(),
<<0x00::size(32)>>
]),
<<opaque::size(32)>>,
<<0x00::size(64)>>
]
end
def to_binary(:STAT, opaque) do
[
bcat([
request(),
opb(:STAT),
<<0x00::size(16)>>,
<<0x00>>,
datatype(),
reserved(),
<<0x00::size(32)>>
]),
<<opaque::size(32)>>,
<<0x00::size(64)>>
]
end
def to_binary(:AUTH_LIST, opaque) do
[
bcat([
request(),
opb(:AUTH_LIST),
<<0x00::size(16)>>,
<<0x00>>,
datatype(),
reserved(),
<<0x00::size(32)>>
]),
<<opaque::size(32)>>,
<<0x00::size(64)>>
]
end
def to_binary(command, opaque) do
to_binary(command, opaque, 0)
end
def to_binary(:STAT, opaque, key) do
[
bcat([request(), opb(:STAT)]),
<<byte_size(key)::size(16)>>,
bcat([<<0x00>>, datatype(), reserved()]),
<<byte_size(key)::size(32)>>,
<<opaque::size(32)>>,
<<0x00::size(64)>>,
key
]
end
def to_binary(:GET, opaque, key) do
[
bcat([request(), opb(:GET)]),
<<byte_size(key)::size(16)>>,
bcat([<<0x00>>, datatype(), reserved()]),
<<byte_size(key)::size(32)>>,
<<opaque::size(32)>>,
<<0x00::size(64)>>,
key
]
end
def to_binary(:GETQ, opaque, key) do
[
bcat([request(), opb(:GETQ)]),
<<byte_size(key)::size(16)>>,
bcat([<<0x00>>, datatype(), reserved()]),
<<byte_size(key)::size(32)>>,
<<opaque::size(32)>>,
<<0x00::size(64)>>,
key
]
end
def to_binary(:GETK, opaque, key) do
[
bcat([request(), opb(:GETK)]),
<<byte_size(key)::size(16)>>,
bcat([<<0x00>>, datatype(), reserved()]),
<<byte_size(key)::size(32)>>,
<<opaque::size(32)>>,
<<0x00::size(64)>>,
key
]
end
def to_binary(:GETKQ, opaque, key) do
[
bcat([request(), opb(:GETKQ)]),
<<byte_size(key)::size(16)>>,
bcat([<<0x00>>, datatype(), reserved()]),
<<byte_size(key)::size(32)>>,
<<opaque::size(32)>>,
<<0x00::size(64)>>,
key
]
end
def to_binary(:FLUSH, opaque, expiry) do
[
bcat([
request(),
opb(:FLUSH),
<<0x00::size(16)>>,
<<0x04>>,
datatype(),
reserved(),
<<0x04::size(32)>>
]),
<<opaque::size(32)>>,
<<0x00::size(64)>>,
<<expiry::size(32)>>
]
end
def to_binary(:FLUSHQ, opaque, expiry) do
[
bcat([
request(),
opb(:FLUSHQ),
<<0x00::size(16)>>,
<<0x04>>,
datatype(),
reserved(),
<<0x04::size(32)>>
]),
<<opaque::size(32)>>,
<<0x00::size(64)>>,
<<expiry::size(32)>>
]
end
def to_binary(:AUTH_START, opaque, key) do
[
bcat([request(), opb(:AUTH_START)]),
<<byte_size(key)::size(16)>>,
bcat([<<0x00>>, datatype(), reserved()]),
<<byte_size(key)::size(32)>>,
<<opaque::size(32)>>,
<<0x00::size(64)>>,
key
]
end
def to_binary(command, opaque, key) do
to_binary(command, opaque, key, 0)
end
def to_binary(:AUTH_START, opaque, key, value) do
[
bcat([request(), opb(:AUTH_START)]),
<<byte_size(key)::size(16)>>,
bcat([<<0x00>>, datatype(), reserved()]),
<<byte_size(key) + IO.iodata_length(value)::size(32)>>,
<<opaque::size(32)>>,
<<0x00::size(64)>>,
key,
value
]
end
def to_binary(:DELETE, opaque, key, cas) do
[
bcat([request(), opb(:DELETE)]),
<<byte_size(key)::size(16)>>,
bcat([<<0x00>>, <<0x00>>, <<0x0000::size(16)>>]),
<<byte_size(key)::size(32)>>,
<<opaque::size(32)>>,
<<cas::size(64)>>,
key
]
end
def to_binary(:DELETEQ, opaque, key, cas) do
[
bcat([request(), opb(:DELETEQ)]),
<<byte_size(key)::size(16)>>,
bcat([<<0x00>>, <<0x00>>, <<0x0000::size(16)>>]),
<<byte_size(key)::size(32)>>,
<<opaque::size(32)>>,
<<cas::size(64)>>,
key
]
end
def to_binary(command, opaque, key, value) do
to_binary(command, opaque, key, value, 0)
end
def to_binary(:APPEND, opaque, key, value, cas) do
[
bcat([request(), opb(:APPEND)]),
<<byte_size(key)::size(16)>>,
bcat([<<0x00>>, datatype(), reserved()]),
<<byte_size(key) + IO.iodata_length(value)::size(32)>>,
<<opaque::size(32)>>,
<<cas::size(64)>>,
key,
value
]
end
def to_binary(:APPENDQ, opaque, key, value, cas) do
[
bcat([request(), opb(:APPENDQ)]),
<<byte_size(key)::size(16)>>,
bcat([<<0x00>>, datatype(), reserved()]),
<<byte_size(key) + IO.iodata_length(value)::size(32)>>,
<<opaque::size(32)>>,
<<cas::size(64)>>,
key,
value
]
end
def to_binary(:PREPEND, opaque, key, value, cas) do
[
bcat([request(), opb(:PREPEND)]),
<<byte_size(key)::size(16)>>,
bcat([<<0x00>>, datatype(), reserved()]),
<<byte_size(key) + IO.iodata_length(value)::size(32)>>,
<<opaque::size(32)>>,
<<cas::size(64)>>,
key,
value
]
end
def to_binary(:PREPENDQ, opaque, key, value, cas) do
[
bcat([request(), opb(:PREPENDQ)]),
<<byte_size(key)::size(16)>>,
bcat([<<0x00>>, datatype(), reserved()]),
<<byte_size(key) + IO.iodata_length(value)::size(32)>>,
<<opaque::size(32)>>,
<<cas::size(64)>>,
key,
value
]
end
def to_binary(command, opaque, key, value, cas) do
to_binary(command, opaque, key, value, cas, 0)
end
def to_binary(command, opaque, key, value, cas, flag) do
to_binary(command, opaque, key, value, cas, flag, 0)
end
def to_binary(:INCREMENT, opaque, key, delta, initial, cas, expiry) do
[
bcat([request(), opb(:INCREMENT)]),
<<byte_size(key)::size(16)>>,
bcat([<<0x14>>, <<0x00>>, <<0x0000::size(16)>>]),
<<byte_size(key) + 20::size(32)>>,
<<opaque::size(32)>>,
<<cas::size(64)>>,
<<delta::size(64)>>,
<<initial::size(64)>>,
<<expiry::size(32)>>,
key
]
end
def to_binary(:INCREMENTQ, opaque, key, delta, initial, cas, expiry) do
[
bcat([request(), opb(:INCREMENTQ)]),
<<byte_size(key)::size(16)>>,
bcat([<<0x14>>, <<0x00>>, <<0x0000::size(16)>>]),
<<byte_size(key) + 20::size(32)>>,
<<opaque::size(32)>>,
<<cas::size(64)>>,
<<delta::size(64)>>,
<<initial::size(64)>>,
<<expiry::size(32)>>,
key
]
end
def to_binary(:DECREMENT, opaque, key, delta, initial, cas, expiry) do
[
bcat([request(), opb(:DECREMENT)]),
<<byte_size(key)::size(16)>>,
bcat([<<0x14>>, <<0x00>>, <<0x0000::size(16)>>]),
<<byte_size(key) + 20::size(32)>>,
<<opaque::size(32)>>,
<<cas::size(64)>>,
<<delta::size(64)>>,
<<initial::size(64)>>,
<<expiry::size(32)>>,
key
]
end
def to_binary(:DECREMENTQ, opaque, key, delta, initial, cas, expiry) do
[
bcat([request(), opb(:DECREMENTQ)]),
<<byte_size(key)::size(16)>>,
bcat([<<0x14>>, <<0x00>>, <<0x0000::size(16)>>]),
<<byte_size(key) + 20::size(32)>>,
<<opaque::size(32)>>,
<<cas::size(64)>>,
<<delta::size(64)>>,
<<initial::size(64)>>,
<<expiry::size(32)>>,
key
]
end
def to_binary(:SET, opaque, key, value, cas, expiry, flag) do
[
bcat([request(), opb(:SET)]),
<<byte_size(key)::size(16)>>,
bcat([<<0x08>>, <<0x00>>, <<0x0000::size(16)>>]),
<<byte_size(key) + 8 + IO.iodata_length(value)::size(32)>>,
<<opaque::size(32)>>,
<<cas::size(64)>>,
<<flag::size(32)>>,
<<expiry::size(32)>>,
key,
value
]
end
def to_binary(:SETQ, opaque, key, value, cas, expiry, flag) do
[
bcat([request(), opb(:SETQ)]),
<<byte_size(key)::size(16)>>,
bcat([<<0x08>>, <<0x00>>, <<0x0000::size(16)>>]),
<<byte_size(key) + 8 + IO.iodata_length(value)::size(32)>>,
<<opaque::size(32)>>,
<<cas::size(64)>>,
<<flag::size(32)>>,
<<expiry::size(32)>>,
key,
value
]
end
def to_binary(:ADD, opaque, key, value, expiry, cas, flag) do
[
bcat([request(), opb(:ADD)]),
<<byte_size(key)::size(16)>>,
bcat([<<0x08>>, <<0x00>>, <<0x0000::size(16)>>]),
<<byte_size(key) + 8 + IO.iodata_length(value)::size(32)>>,
<<opaque::size(32)>>,
<<cas::size(64)>>,
<<flag::size(32)>>,
<<expiry::size(32)>>,
key,
value
]
end
def to_binary(:ADDQ, opaque, key, value, cas, expiry, flag) do
[
bcat([request(), opb(:ADDQ)]),
<<byte_size(key)::size(16)>>,
bcat([<<0x08>>, <<0x00>>, <<0x0000::size(16)>>]),
<<byte_size(key) + 8 + IO.iodata_length(value)::size(32)>>,
<<opaque::size(32)>>,
<<cas::size(64)>>,
<<flag::size(32)>>,
<<expiry::size(32)>>,
key,
value
]
end
def to_binary(:REPLACE, opaque, key, value, cas, expiry, flag) do
[
bcat([request(), opb(:REPLACE)]),
<<byte_size(key)::size(16)>>,
bcat([<<0x08>>, <<0x00>>, <<0x0000::size(16)>>]),
<<byte_size(key) + 8 + IO.iodata_length(value)::size(32)>>,
<<opaque::size(32)>>,
<<cas::size(64)>>,
<<flag::size(32)>>,
<<expiry::size(32)>>,
key,
value
]
end
def to_binary(:REPLACEQ, opaque, key, value, cas, expiry, flag) do
[
bcat([request(), opb(:REPLACEQ)]),
<<byte_size(key)::size(16)>>,
bcat([<<0x08>>, <<0x00>>, <<0x0000::size(16)>>]),
<<byte_size(key) + 8 + IO.iodata_length(value)::size(32)>>,
<<opaque::size(32)>>,
<<cas::size(64)>>,
<<flag::size(32)>>,
<<expiry::size(32)>>,
key,
value
]
end
def parse_header(<<
0x81::size(8),
opcode::size(8),
key_length::size(16),
extra_length::size(8),
data_type::size(8),
status::size(16),
total_body_length::size(32),
opaque::size(32),
cas::size(64)
>>) do
%Header{
opcode: opcode,
key_length: key_length,
extra_length: extra_length,
data_type: data_type,
status: status,
total_body_length: total_body_length,
opaque: opaque,
cas: cas
}
end
def total_body_size(%Header{total_body_length: size}) do
size
end
def parse_body(
%Header{
status: 0x0000,
opcode: op(:GET),
extra_length: extra_length,
total_body_length: total_body_length,
opaque: opaque
},
rest
) do
value_size = total_body_length - extra_length
<<_extra::binary-size(extra_length), value::binary-size(value_size)>> = rest
{opaque, {:ok, value}}
end
def parse_body(
%Header{
status: 0x0000,
opcode: op(:GETQ),
extra_length: extra_length,
total_body_length: total_body_length,
opaque: opaque
},
rest
) do
value_size = total_body_length - extra_length
<<_extra::binary-size(extra_length), value::binary-size(value_size)>> = rest
{opaque, {:ok, value}}
end
def parse_body(
%Header{
status: 0x0000,
opcode: op(:GETK),
extra_length: extra_length,
key_length: key_length,
total_body_length: total_body_length,
opaque: opaque
},
rest
) do
value_size = total_body_length - extra_length - key_length
<<_extra::binary-size(extra_length), key::binary-size(key_length),
value::binary-size(value_size)>> = rest
{opaque, {:ok, key, value}}
end
def parse_body(
%Header{
status: 0x0000,
opcode: op(:GETKQ),
extra_length: extra_length,
key_length: key_length,
total_body_length: total_body_length,
opaque: opaque
},
rest
) do
value_size = total_body_length - extra_length - key_length
<<_extra::binary-size(extra_length), key::binary-size(key_length),
value::binary-size(value_size)>> = rest
{opaque, {:ok, key, value}}
end
def parse_body(%Header{status: 0x0000, opcode: op(:VERSION), opaque: opaque}, rest) do
{opaque, {:ok, rest}}
end
def parse_body(%Header{status: 0x0000, opcode: op(:AUTH_LIST), opaque: opaque}, rest) do
{opaque, {:ok, rest}}
end
def parse_body(%Header{status: 0x0000, opcode: op(:INCREMENT), opaque: opaque}, rest) do
<<value::size(64)>> = rest
{opaque, {:ok, value}}
end
def parse_body(%Header{status: 0x0000, opcode: op(:INCREMENTQ), opaque: opaque}, rest) do
<<value::size(64)>> = rest
{opaque, {:ok, value}}
end
def parse_body(%Header{status: 0x0000, opcode: op(:DECREMENT), opaque: opaque}, rest) do
<<value::size(64)>> = rest
{opaque, {:ok, value}}
end
def parse_body(%Header{status: 0x0000, opcode: op(:DECREMENTQ), opaque: opaque}, rest) do
<<value::size(64)>> = rest
{opaque, {:ok, value}}
end
def parse_body(%Header{status: 0x0000, opcode: op(:AUTH_START), opaque: opaque}, _rest) do
{opaque, {:ok}}
end
def parse_body(
%Header{
status: 0x0000,
opcode: op(:STAT),
key_length: 0,
total_body_length: 0,
opaque: opaque
},
_rest
) do
{opaque, {:ok, :done}}
end
def parse_body(
%Header{
status: 0x0000,
opcode: op(:STAT),
key_length: key_length,
total_body_length: total_body_length,
opaque: opaque
},
rest
) do
value_size = total_body_length - key_length
<<key::binary-size(key_length), value::binary-size(value_size)>> = rest
{opaque, {:ok, key, value}}
end
def parse_body(%Header{status: 0x0021, opaque: opaque}, body) do
{opaque, {:error, :auth_step, body}}
end
defparse_empty(:SET)
defparse_empty(:ADD)
defparse_empty(:REPLACE)
defparse_empty(:DELETE)
defparse_empty(:QUIT)
defparse_empty(:FLUSH)
defparse_empty(:NOOP)
defparse_empty(:APPEND)
defparse_empty(:PREPEND)
defparse_error(0x0001, "Key not found")
defparse_error(0x0002, "Key exists")
defparse_error(0x0003, "Value too large")
defparse_error(0x0004, "Invalid arguments")
defparse_error(0x0005, "Item not stored")
defparse_error(0x0006, "Incr/Decr on non-numeric value")
defparse_error(0x0008, "Authentication Error")
defparse_error(0x0009, "Authentication Continue")
defparse_error(0x0020, "Authentication required / Not Successful")
defparse_error(0x0081, "Unknown command")
defparse_error(0x0082, "Out of memory")
def quiet_response(:GETQ), do: {:error, "Key not found"}
def quiet_response(:GETKQ), do: {:error, "Key not found"}
def quiet_response(:SETQ), do: {:ok}
def quiet_response(:ADDQ), do: {:ok}
def quiet_response(:DELETEQ), do: {:ok}
def quiet_response(:REPLACEQ), do: {:ok}
def quiet_response(:INCREMENTQ), do: {:ok}
def quiet_response(:DECREMENTQ), do: {:ok}
def quiet_response(:APPENDQ), do: {:ok}
def quiet_response(:PREPENDQ), do: {:ok}
def quiet_response(:FLUSHQ), do: {:ok}
def quiet_response(:QUITQ), do: {:ok}
end