Current section

Files

Jump to
smppex lib smppex protocol unpack.ex
Raw

lib/smppex/protocol/unpack.ex

defmodule SMPPEX.Protocol.Unpack do
alias SMPPEX.Protocol.Unpack.Helpers
@null 0
@unexpected_data_end "Unexpected end of data"
@invalid_c_octet_string_format "C-Octet String: wrong format"
@invalid_fixed_c_octet_string "C-Octet String(fixed): invalid"
@invalid_c_octet_string_no_terminator "C-Octet String(var): null terminator not found"
@type unpack_error_result :: {:error, any}
@type integer_size :: 1 | 2 | 4
@spec integer(binary, integer_size) :: unpack_error_result | {:ok, non_neg_integer, binary}
def integer(bin, size) when (size == 1 or size == 2 or size == 4) and is_binary(bin) do
integer_bit_size = size * 8
case bin do
<<int :: big-unsigned-integer-size(integer_bit_size), rest :: binary>> -> {:ok, int, rest}
_ -> {:error, @unexpected_data_end}
end
end
@type length_spec :: {:fixed, pos_integer} | {:max, pos_integer}
@type kind :: :ascii | :hex | :dec
@type c_octet_string_result :: unpack_error_result | {:ok, binary, binary}
@spec c_octet_string(binary, length_spec) :: c_octet_string_result
def c_octet_string(bin, length_spec) when is_binary(bin) do
c_octet_string(bin, length_spec, :ascii)
end
@spec c_octet_string(binary, length_spec, kind) :: c_octet_string_result
def c_octet_string(bin, {:fixed, len}, kind) when len >= 1 and is_binary(bin) do
str_length = len - 1
case bin do
<< @null :: size(8), rest :: binary >> -> {:ok, "", rest}
<< str :: binary-size(str_length), @null :: size(8), rest :: binary >> ->
case valid_kind?(str, kind) do
true -> {:ok, str, rest}
false -> {:error, @invalid_c_octet_string_format}
end
<< _ :: binary-size(len), _ :: binary >> -> {:error, @invalid_fixed_c_octet_string}
_ -> {:error, @unexpected_data_end}
end
end
def c_octet_string(bin, {:max, len}, kind) when len >= 1 and is_binary(bin) do
case Helpers.take_until(bin, @null, len) do
{str, rest} -> case valid_kind?(str, kind) do
true -> {:ok, str, rest}
false -> {:error, @invalid_c_octet_string_format}
end
:not_found -> {:error, @invalid_c_octet_string_no_terminator}
end
end
@spec valid_kind?(binary, kind) :: boolean
defp valid_kind?(_str, :ascii), do: true
defp valid_kind?(str, :dec), do: Helpers.dec?(str)
defp valid_kind?(str, :hex), do: Helpers.hex?(str)
@spec c_octet_string(binary, non_neg_integer) :: unpack_error_result | {:ok, binary, binary}
def octet_string(bin, len) when len >= 0 and is_binary(bin) do
case bin do
<< str :: binary-size(len), rest :: binary >> -> {:ok, str, rest}
_ -> {:error, @unexpected_data_end}
end
end
@type parsed_tlv :: {non_neg_integer, binary}
@spec tlv(binary) :: unpack_error_result | {:ok, parsed_tlv, binary}
def tlv(bin) when byte_size(bin) < 4 do
{:error, @unexpected_data_end}
end
def tlv(<<tag :: big-unsigned-integer-size(16), len :: big-unsigned-integer-size(16), value_and_rest :: binary>>) do
case value_and_rest do
<< value :: binary-size(len), rest :: binary >> -> {:ok, {tag, value}, rest}
_ -> {:error, @unexpected_data_end}
end
end
end