# 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)>> ]), <>, <<0x00::size(64)>> ] end def to_binary(:QUITQ, opaque) do [ bcat([ request(), opb(:QUITQ), <<0x00::size(16)>>, <<0x00>>, datatype(), reserved(), <<0x00::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)>> ]), <>, <<0x00::size(64)>> ] end def to_binary(:VERSION, opaque) do [ bcat([ request(), opb(:VERSION), <<0x00::size(16)>>, <<0x00>>, datatype(), reserved(), <<0x00::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)>> ]), <>, <<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)>> ]), <>, <<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)]), <>, bcat([<<0x00>>, datatype(), reserved()]), <>, <>, <<0x00::size(64)>>, key ] end def to_binary(:GET, opaque, key) do [ bcat([request(), opb(:GET)]), <>, bcat([<<0x00>>, datatype(), reserved()]), <>, <>, <<0x00::size(64)>>, key ] end def to_binary(:GETQ, opaque, key) do [ bcat([request(), opb(:GETQ)]), <>, bcat([<<0x00>>, datatype(), reserved()]), <>, <>, <<0x00::size(64)>>, key ] end def to_binary(:GETK, opaque, key) do [ bcat([request(), opb(:GETK)]), <>, bcat([<<0x00>>, datatype(), reserved()]), <>, <>, <<0x00::size(64)>>, key ] end def to_binary(:GETKQ, opaque, key) do [ bcat([request(), opb(:GETKQ)]), <>, bcat([<<0x00>>, datatype(), reserved()]), <>, <>, <<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)>> ]), <>, <<0x00::size(64)>>, <> ] end def to_binary(:FLUSHQ, opaque, expiry) do [ bcat([ request(), opb(:FLUSHQ), <<0x00::size(16)>>, <<0x04>>, datatype(), reserved(), <<0x04::size(32)>> ]), <>, <<0x00::size(64)>>, <> ] end def to_binary(:AUTH_START, opaque, key) do [ bcat([request(), opb(:AUTH_START)]), <>, bcat([<<0x00>>, datatype(), reserved()]), <>, <>, <<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)]), <>, bcat([<<0x00>>, datatype(), reserved()]), <>, <>, <<0x00::size(64)>>, key, value ] end def to_binary(:DELETE, opaque, key, cas) do [ bcat([request(), opb(:DELETE)]), <>, bcat([<<0x00>>, <<0x00>>, <<0x0000::size(16)>>]), <>, <>, <>, key ] end def to_binary(:DELETEQ, opaque, key, cas) do [ bcat([request(), opb(:DELETEQ)]), <>, bcat([<<0x00>>, <<0x00>>, <<0x0000::size(16)>>]), <>, <>, <>, 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)]), <>, bcat([<<0x00>>, datatype(), reserved()]), <>, <>, <>, key, value ] end def to_binary(:APPENDQ, opaque, key, value, cas) do [ bcat([request(), opb(:APPENDQ)]), <>, bcat([<<0x00>>, datatype(), reserved()]), <>, <>, <>, key, value ] end def to_binary(:PREPEND, opaque, key, value, cas) do [ bcat([request(), opb(:PREPEND)]), <>, bcat([<<0x00>>, datatype(), reserved()]), <>, <>, <>, key, value ] end def to_binary(:PREPENDQ, opaque, key, value, cas) do [ bcat([request(), opb(:PREPENDQ)]), <>, bcat([<<0x00>>, datatype(), reserved()]), <>, <>, <>, 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)]), <>, bcat([<<0x14>>, <<0x00>>, <<0x0000::size(16)>>]), <>, <>, <>, <>, <>, <>, key ] end def to_binary(:INCREMENTQ, opaque, key, delta, initial, cas, expiry) do [ bcat([request(), opb(:INCREMENTQ)]), <>, bcat([<<0x14>>, <<0x00>>, <<0x0000::size(16)>>]), <>, <>, <>, <>, <>, <>, key ] end def to_binary(:DECREMENT, opaque, key, delta, initial, cas, expiry) do [ bcat([request(), opb(:DECREMENT)]), <>, bcat([<<0x14>>, <<0x00>>, <<0x0000::size(16)>>]), <>, <>, <>, <>, <>, <>, key ] end def to_binary(:DECREMENTQ, opaque, key, delta, initial, cas, expiry) do [ bcat([request(), opb(:DECREMENTQ)]), <>, bcat([<<0x14>>, <<0x00>>, <<0x0000::size(16)>>]), <>, <>, <>, <>, <>, <>, key ] end def to_binary(:SET, opaque, key, value, cas, expiry, flag) do [ bcat([request(), opb(:SET)]), <>, bcat([<<0x08>>, <<0x00>>, <<0x0000::size(16)>>]), <>, <>, <>, <>, <>, key, value ] end def to_binary(:SETQ, opaque, key, value, cas, expiry, flag) do [ bcat([request(), opb(:SETQ)]), <>, bcat([<<0x08>>, <<0x00>>, <<0x0000::size(16)>>]), <>, <>, <>, <>, <>, key, value ] end def to_binary(:ADD, opaque, key, value, expiry, cas, flag) do [ bcat([request(), opb(:ADD)]), <>, bcat([<<0x08>>, <<0x00>>, <<0x0000::size(16)>>]), <>, <>, <>, <>, <>, key, value ] end def to_binary(:ADDQ, opaque, key, value, cas, expiry, flag) do [ bcat([request(), opb(:ADDQ)]), <>, bcat([<<0x08>>, <<0x00>>, <<0x0000::size(16)>>]), <>, <>, <>, <>, <>, key, value ] end def to_binary(:REPLACE, opaque, key, value, cas, expiry, flag) do [ bcat([request(), opb(:REPLACE)]), <>, bcat([<<0x08>>, <<0x00>>, <<0x0000::size(16)>>]), <>, <>, <>, <>, <>, key, value ] end def to_binary(:REPLACEQ, opaque, key, value, cas, expiry, flag) do [ bcat([request(), opb(:REPLACEQ)]), <>, bcat([<<0x08>>, <<0x00>>, <<0x0000::size(16)>>]), <>, <>, <>, <>, <>, 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 <> = rest {opaque, {:ok, value}} end def parse_body(%Header{status: 0x0000, opcode: op(:INCREMENTQ), opaque: opaque}, rest) do <> = rest {opaque, {:ok, value}} end def parse_body(%Header{status: 0x0000, opcode: op(:DECREMENT), opaque: opaque}, rest) do <> = rest {opaque, {:ok, value}} end def parse_body(%Header{status: 0x0000, opcode: op(:DECREMENTQ), opaque: opaque}, rest) do <> = 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 <> = 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