defmodule CRC do @moduledoc """ This module is used to calculate CRC (Cyclic Redundancy Check) values for binary data. It uses NIF functions written in C to interate over the given binary calculating the CRC checksum value. CRC implementations have been tested against these online calculators to validate their correctness to the best of our ability. https://www.lammertbies.nl/comm/info/crc-calculation.html http://www.sunshine2k.de/coding/javascript/crc/crc_js.html """ @spec crc(:crc_algorithm.params(), iodata()) :: :crc_algorithm.value() defdelegate crc(params, input), to: :crc @spec crc_init(:crc_algorithm.params()) :: :crc_algorithm.resource() defdelegate crc_init(params), to: :crc @spec crc_update(:crc_algorithm.resource(), iodata()) :: :crc_algorithm.resource() defdelegate crc_update(resource, input), to: :crc @spec crc_final(:crc_algorithm.resource()) :: :crc_algorithm.value() defdelegate crc_final(resource), to: :crc @model_list :crc_nif.crc_list() |> Map.to_list() |> Enum.map(fn {model, map} -> {model, Map.get(map, model)} end) @doc """ Returns a list of all the pre-defined CRC models """ @spec list() :: [{atom, String.t}] def list() do @model_list end @doc """ Returns a list of all pre-defined CRC Models that match the filter given. Filter is compiled into a regular expression and matched against the model name and description. """ @spec list(binary) :: [{atom, String.t}] def list(filter) do @model_list |> Enum.filter(&(list_filter(&1, filter))) end defp list_filter({model_atom, model_name}, filter) do atom_string = Atom.to_string(model_atom) {:ok, rfilter} = Regex.compile(filter) Regex.match?(rfilter, atom_string) or Regex.match?(rfilter, model_name) end @doc """ Calculates a 8-bit CRC with polynomial x^8+x^6+x^3+x^2+1, 0x14D. Chosen based on Koopman, et al. (0xA6 in his notation = 0x14D >> 1): http://www.ece.cmu.edu/~koopman/roses/dsn04/koopman04_crc_poly_embedded.pdf seed defaults to 0xFF if one is not given """ @spec crc_8(binary, number) :: number defdelegate crc_8(input, seed \\ 0xFF), to: :crc @doc """ Calculates a 16-bit ANSI CRC checksum for the provided binary """ @spec crc_16(binary) :: number def crc_16(input), do: :crc_fast.calc(:crc_16, input) @doc """ Calculates a 16-bit CCITT CRC with the given seed, seed defaults to 0xFFFF if one is not given. This CCIT method uses a 0x1021 polynomial. """ @spec ccitt_16(binary) :: number def ccitt_16(input), do: :crc_fast.calc(:crc_16_ccitt_false, input) @spec ccitt_16(binary, number) :: number def ccitt_16(input, seed) do extend_model_seed(:crc_16_ccitt_false, seed) |> :crc_fast.calc(input) end @doc """ Calculates a 16-bit CCITT Kermit CRC This CCIT method uses a 0x8408 polynomial. """ @spec ccitt_16_kermit(binary) :: number def ccitt_16_kermit(input), do: :crc_fast.calc(:crc_16_kermit, input) @spec ccitt_16_kermit(binary, number) :: number def ccitt_16_kermit(input, seed) do extend_model_seed(:crc_16_kermit, seed) |> :crc_fast.calc(input) end @doc """ Calculates a 16-bit CCITT XMODEM CRC This CCIT method uses a 0x1021 polynomial. """ @spec ccitt_16_xmodem(binary) :: number def ccitt_16_xmodem(input), do: :crc_fast.calc(:xmodem, input) @doc """ Calculates a 16-bit CCITT 0x1D0F CRC This CCIT method uses a 0x1021 polynomial. """ @spec ccitt_16_1D0F(binary) :: number def ccitt_16_1D0F(input) do extend_model_seed(:crc_16_ccitt_false, 0x1D0F) |> :crc_fast.calc(input) end @doc """ Calculates a 16-bit modbus CRC """ @spec crc_16_modbus(binary) :: number def crc_16_modbus(input), do: :crc_fast.calc(:crc_16_modbus, input) @doc """ Calculates a 16-bit Sick CRC """ @spec crc_16_sick(binary) :: number def crc_16_sick(input), do: :crc_fast.calc(:crc_16_sick, input) @doc """ Calculates a 16-bit DNP CRC """ @spec crc_16_dnp(binary) :: number def crc_16_dnp(input), do: :crc_fast.calc(:crc_16_dnp, input) @doc """ Calculates a 32-bit CRC """ @spec crc_32(binary) :: number def crc_32(input), do: :crc_fast.calc(:crc_32, input) @doc """ Calculates an XOR checksum for the given binary """ @spec checksum_xor(binary) :: number defdelegate checksum_xor(input), to: :crc defp extend_model_seed(model, seed), do: %{extend: model, init: seed} end