defmodule MeshxRpc.Protocol.Block.Decode do @moduledoc false alias MeshxRpc.Common.Structs.Data @error_prefix_remote :error_rpc_remote @time_unit :microsecond @req_cast_1 10 @req_cast_o 11 @rsp_cast_ack 15 @req_call_1 50 @req_call_o 51 @rsp_call_1 55 @rsp_call_o 56 @err 199 def decode(<> = pld, %Data{} = data) when cmd in [@req_cast_1, @req_call_1] do <> = rest if conn_ref == data.remote.conn_ref do fun_req = case cmd do @req_cast_1 -> :cast @req_call_1 -> :call end hdr = :binary.part(pld, 0, byte_size(pld) - (dta_size + byte_size(cks))) if is_nil(data.req_ref) and is_nil(data.req_seq) do data = %Data{data | fun_name: String.to_atom(fun_name), fun_req: fun_req, req_ref: req_ref, dta: [dta]} {:ok, data, hdr, cks, ser_flag} else if req_ref == data.req_ref and (is_nil(data.req_seq) or data.req_seq == 2) do data = %Data{data | fun_name: String.to_existing_atom(fun_name), fun_req: fun_req, dta: [dta] ++ data.dta} {:ok, data, hdr, cks, ser_flag} else {:error, :invalid_ref} end end else {:error, :invalid_ref} end end def decode(<<@rsp_call_1::integer-unsigned-size(8), rest::binary>> = pld, %Data{} = data) do <> = rest if req_ref == data.req_ref and conn_ref == data.remote.conn_ref and (data.req_seq == 2 or data.req_seq == nil) do hdr = :binary.part(pld, 0, byte_size(pld) - (dta_size + byte_size(cks))) {:ok, %Data{data | dta: [dta] ++ data.dta}, hdr, cks, ser_flag} else {:error, :invalid_ref} end end def decode(<> = pld, %Data{} = data) when cmd in [@req_cast_o, @req_call_o] do <> = rest {cks, cks_size} = if maybe_cks == <<>> do {<<>>, 0} else <> = maybe_cks {cks, 4 + byte_size(cks)} end hdr = :binary.part(pld, 0, byte_size(pld) - (dta_size + cks_size)) if conn_ref == data.remote.conn_ref do if is_nil(data.req_ref) and is_nil(data.req_seq) do data = %Data{data | req_seq: seq, req_ref: req_ref, dta: [dta]} {:cont, data, hdr, cks} else if req_ref == data.req_ref and seq == data.req_seq - 1 do data = %Data{data | req_seq: seq, dta: [dta] ++ data.dta} {:cont, data, hdr, cks} else {:error, :invalid_ref} end end else {:error, :invalid_ref} end end def decode(<> = pld, %Data{} = data) when cmd == @rsp_call_o do <> = rest {cks, cks_size} = if maybe_cks == <<>> do {<<>>, 0} else <> = maybe_cks {cks, 4 + byte_size(cks)} end hdr = :binary.part(pld, 0, byte_size(pld) - (dta_size + cks_size)) if req_ref == data.req_ref and conn_ref == data.remote.conn_ref do if is_nil(data.req_seq) do data = %Data{data | req_seq: seq, dta: [dta]} {:cont, data, hdr, cks} else if seq == data.req_seq - 1 do data = %Data{data | req_seq: seq, dta: [dta] ++ data.dta} {:cont, data, hdr, cks} else {:error, :invalid_ref} end end else {:error, :invalid_ref} end end def decode(<<@rsp_cast_ack::integer-unsigned-size(8), rest::binary>>, %Data{} = data) do <> = rest if req_ref == data.req_ref and conn_ref == data.remote.conn_ref, do: :ok_ack, else: {:error, :invalid_ref} end def decode(<<@err::integer-unsigned-size(8), rest::binary>>, %Data{} = data) do <> = rest if req_ref == data.req_ref and conn_ref == data.remote.conn_ref do {{_, _, _}, {m, f, o}} = data.serialize_mfa case apply(m, f, [err, o, ser_flag]) do {:ok, args} -> {@error_prefix_remote, args} {:error, err} -> {:error, err} err -> {:error, err} end else {:error, :invalid_ref} end end def bin_to_args(%Data{} = data, ser_flag) do dta = Enum.join(data.dta) {{_, _, _}, {m, f, o}} = data.serialize_mfa start = System.monotonic_time(@time_unit) case apply(m, f, [dta, o, ser_flag]) do {:ok, args} -> dt = System.monotonic_time(@time_unit) - start {:ok, args, dt} {:error, err} -> {:error, err} err -> {:error, err} end end end