From 39f040aec21ffbb3ca6f6474c432d046a30cdb03 Mon Sep 17 00:00:00 2001 From: Rodney Norris Date: Fri, 20 Mar 2026 15:23:14 -0500 Subject: [PATCH 1/2] chore: update dependencies and fix deprecation warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Update all dependencies (ex_doc 0.24→0.40, propcheck 1.0→1.5, elixir_make 0.6→0.9, proper 1.2→1.5, and supporting packages) - Run mix format --migrate to fix bitstring specifier deprecations and modernize code style - Tighten Elixir version requirement from ">= 1.4.2 and < 2.0.0" to "~> 1.14" - Fix missing trailing newline in .gitignore Co-Authored-By: Claude Sonnet 4.6 --- .gitignore | 1 + lib/crc.ex | 6 +- lib/crc/legacy.ex | 202 ++++++++++++------------ lib/crc/model.ex | 296 +++++++++++++++++++++++------------ mix.exs | 2 +- mix.lock | 19 +-- test/crc_16_test.exs | 3 +- test/crc_test.exs | 231 +++++++++++++++------------ test/support/checksum_xor.ex | 4 +- test/support/crc_16.ex | 21 ++- test/support/crc_8_koop.ex | 27 ++-- 11 files changed, 477 insertions(+), 335 deletions(-) diff --git a/.gitignore b/.gitignore index e7a4bd9..5ad3dba 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,4 @@ crc-*.tar # Editor folders /.idea /.vscode +/.claude diff --git a/lib/crc.ex b/lib/crc.ex index 3f7b2af..90a8a8d 100644 --- a/lib/crc.ex +++ b/lib/crc.ex @@ -109,7 +109,7 @@ defmodule CRC do @doc """ Returns a list of all the compiled CRC models. """ - @spec list() :: [{atom, String.t}] + @spec list() :: [{atom, String.t()}] def list() do :crc_nif.crc_list() |> Map.to_list() @@ -122,10 +122,10 @@ defmodule CRC do Filter is compiled into a regular expression and matched against the model name and description. """ - @spec list(binary) :: [{atom, String.t}] + @spec list(binary) :: [{atom, String.t()}] def list(filter) do list() - |> Enum.filter(&(list_filter(&1, filter))) + |> Enum.filter(&list_filter(&1, filter)) end defp list_filter({model_atom, model_name}, filter) do diff --git a/lib/crc/legacy.ex b/lib/crc/legacy.ex index 868a900..c05b978 100644 --- a/lib/crc/legacy.ex +++ b/lib/crc/legacy.ex @@ -1,103 +1,103 @@ defmodule CRC.Legacy do - @moduledoc false - - # Legacy CRC functions, these may be depraced in a future release and removed in v1.0 - RN - defmacro __using__(_) do - quote do - @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 + @moduledoc false + + # Legacy CRC functions, these may be depraced in a future release and removed in v1.0 - RN + defmacro __using__(_) do + quote do + @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 -end \ No newline at end of file + end +end diff --git a/lib/crc/model.ex b/lib/crc/model.ex index d6cd1bd..2223d1e 100644 --- a/lib/crc/model.ex +++ b/lib/crc/model.ex @@ -1,42 +1,39 @@ defmodule CRC.Model do - @type t() :: %__MODULE__{ - bits: 0x00..0xff, - sick: boolean(), - width: 0x00..0xff, - poly: 0x0000000000000000..0xffffffffffffffff, - init: 0x0000000000000000..0xffffffffffffffff, - refin: boolean(), - refout: boolean(), - xorout: 0x0000000000000000..0xffffffffffffffff, - check: 0x0000000000000000..0xffffffffffffffff, - residue: 0x0000000000000000..0xffffffffffffffff, - name: binary, - key: atom(), - aliases: %{ - optional(atom()) => binary - }, - slow: boolean(), - value: non_neg_integer() - } - - defstruct [ - bits: nil, - sick: nil, - width: nil, - poly: nil, - init: nil, - refin: nil, - refout: nil, - xorout: nil, - check: nil, - residue: nil, - name: nil, - key: nil, - aliases: %{}, - slow: false, - value: 0 - ] + bits: 0x00..0xFF, + sick: boolean(), + width: 0x00..0xFF, + poly: 0x0000000000000000..0xFFFFFFFFFFFFFFFF, + init: 0x0000000000000000..0xFFFFFFFFFFFFFFFF, + refin: boolean(), + refout: boolean(), + xorout: 0x0000000000000000..0xFFFFFFFFFFFFFFFF, + check: 0x0000000000000000..0xFFFFFFFFFFFFFFFF, + residue: 0x0000000000000000..0xFFFFFFFFFFFFFFFF, + name: binary, + key: atom(), + aliases: %{ + optional(atom()) => binary + }, + slow: boolean(), + value: non_neg_integer() + } + + defstruct bits: nil, + sick: nil, + width: nil, + poly: nil, + init: nil, + refin: nil, + refout: nil, + xorout: nil, + check: nil, + residue: nil, + name: nil, + key: nil, + aliases: %{}, + slow: false, + value: 0 def decode(binary) when is_binary(binary) do parse(binary, %__MODULE__{}) @@ -63,19 +60,23 @@ defmodule CRC.Model do gen_tables(model) ] end + def gen_code(models = [%__MODULE__{} | _]) do stubs = for model <- models, into: [] do {model.width, model.key, gen_stubs(model)} end |> :lists.usort() + tables = for model <- models, into: [] do {model.width, model.key, gen_tables(model)} end |> :lists.usort() + stubs = for {_, _, stub} <- stubs, into: [], do: stub tables = for {_, _, table} <- tables, into: [], do: table + [ stubs, "\n", @@ -83,24 +84,26 @@ defmodule CRC.Model do ] end - def gen_stubs(%__MODULE__{ aliases: aliases, name: name, key: root_key }) do + def gen_stubs(%__MODULE__{aliases: aliases, name: name, key: root_key}) do entries = [ {root_key, "{{NULL, NULL}, false, 0, \"#{root_key}\", 0, \"#{root_key}\", \"#{name}\"},\n"} - | (for {key, val} <- aliases, into: [] do - {key, "{{NULL, NULL}, false, 0, \"#{root_key}\", 0, \"#{key}\", \"#{val}\"},\n"} - end) + | for {key, val} <- aliases, into: [] do + {key, "{{NULL, NULL}, false, 0, \"#{root_key}\", 0, \"#{key}\", \"#{val}\"},\n"} + end ] + entries = - for {_, entry} <- Enum.sort_by(entries, fn ({key, _}) -> key end), into: [] do + for {_, entry} <- Enum.sort_by(entries, fn {key, _} -> key end), into: [] do entry end + [ "/* #{root_key} */\n" | entries ] end - def gen_tables(model = %__MODULE__{ bits: bits }) do + def gen_tables(model = %__MODULE__{bits: bits}) do table = if model.sick do :lists.duplicate(256, 0) @@ -109,9 +112,14 @@ defmodule CRC.Model do {true, table} = :crc_nif.debug_table(context) table end - [_ | table_values] = :lists.flatten(for n <- table, into: [] do - [?,, encode_hex(n, bits)] - end) + + [_ | table_values] = + :lists.flatten( + for n <- table, into: [] do + [?,, encode_hex(n, bits)] + end + ) + width = "#{model.width}" poly = encode_hex(model.poly, bits) init = encode_hex(model.init, bits) @@ -121,6 +129,7 @@ defmodule CRC.Model do check = encode_hex(model.check, bits) residue = encode_hex(model.residue, bits) name = "\"#{model.name}\"" + aliases = if map_size(model.aliases) == 0 do [] @@ -129,23 +138,76 @@ defmodule CRC.Model do [?\s, "alias=\"", val, ?\"] end end + [ if model.sick do - ["/* width=", width, " poly=", poly, " init=", init, " sick=true check=", check, " name=", name, aliases, " */\n"] + [ + "/* width=", + width, + " poly=", + poly, + " init=", + init, + " sick=true check=", + check, + " name=", + name, + aliases, + " */\n" + ] else - ["/* width=", width, " poly=", poly, " init=", init, " refin=", refin, " refout=", refout, " xorout=", xorout, " check=", check, " residue=", residue, " name=", name, aliases, " */\n"] + [ + "/* width=", + width, + " poly=", + poly, + " init=", + init, + " refin=", + refin, + " refout=", + refout, + " xorout=", + xorout, + " check=", + check, + " residue=", + residue, + " name=", + name, + aliases, + " */\n" + ] end, "{{{NULL, NULL}, true, 0, \"#{model.key}\", #{bits}},\n", " #{model.sick},\n", - " ", width, ",\n", - " ", poly, ",\n", - " ", init, ",\n", - " ", refin, ",\n", - " ", refout, ",\n", - " ", xorout, ",\n", - " ", check, ",\n", - " ", residue, ",\n", - " {", table_values, "}},\n" + " ", + width, + ",\n", + " ", + poly, + ",\n", + " ", + init, + ",\n", + " ", + refin, + ",\n", + " ", + refout, + ",\n", + " ", + xorout, + ",\n", + " ", + check, + ",\n", + " ", + residue, + ",\n", + " {", + table_values, + "}},\n" ] end @@ -154,103 +216,124 @@ defmodule CRC.Model do case decode(data) do {:ok, model, rest} -> decode_list(rest, [model | acc]) + {:error, _, <<>>} -> {:ok, :lists.reverse(acc)} + {:error, model, rest} -> {:error, model, rest} end end @doc false - defp parse("width=" <> rest, acc = %{ width: nil }) do + defp parse("width=" <> rest, acc = %{width: nil}) do {rest, value} = take_until_whitespace(rest, <<>>) value = :erlang.binary_to_integer(value) - acc = %{ acc | width: value } + acc = %{acc | width: value} parse(rest, acc) end - defp parse("poly=" <> rest, acc = %{ poly: nil }) do + + defp parse("poly=" <> rest, acc = %{poly: nil}) do {rest, "0x" <> value} = take_until_whitespace(rest, <<>>) value = decode_hex(value) - acc = %{ acc | poly: value } + acc = %{acc | poly: value} parse(rest, acc) end - defp parse("init=" <> rest, acc = %{ init: nil }) do + + defp parse("init=" <> rest, acc = %{init: nil}) do {rest, "0x" <> value} = take_until_whitespace(rest, <<>>) value = decode_hex(value) - acc = %{ acc | init: value } + acc = %{acc | init: value} parse(rest, acc) end - defp parse("xorout=" <> rest, acc = %{ xorout: nil }) do + + defp parse("xorout=" <> rest, acc = %{xorout: nil}) do {rest, "0x" <> value} = take_until_whitespace(rest, <<>>) value = decode_hex(value) - acc = %{ acc | xorout: value } + acc = %{acc | xorout: value} parse(rest, acc) end - defp parse("check=" <> rest, acc = %{ check: nil }) do + + defp parse("check=" <> rest, acc = %{check: nil}) do {rest, "0x" <> value} = take_until_whitespace(rest, <<>>) value = decode_hex(value) - acc = %{ acc | check: value } + acc = %{acc | check: value} parse(rest, acc) end - defp parse("residue=" <> rest, acc = %{ residue: nil }) do + + defp parse("residue=" <> rest, acc = %{residue: nil}) do {rest, "0x" <> value} = take_until_whitespace(rest, <<>>) value = decode_hex(value) - acc = %{ acc | residue: value } + acc = %{acc | residue: value} parse(rest, acc) end - defp parse("refin=" <> rest, acc = %{ refin: nil }) do + + defp parse("refin=" <> rest, acc = %{refin: nil}) do {rest, value} = take_until_whitespace(rest, <<>>) + value = case :erlang.binary_to_atom(value, :unicode) do val when is_boolean(val) -> val end - acc = %{ acc | refin: value } + + acc = %{acc | refin: value} parse(rest, acc) end - defp parse("refout=" <> rest, acc = %{ refout: nil }) do + + defp parse("refout=" <> rest, acc = %{refout: nil}) do {rest, value} = take_until_whitespace(rest, <<>>) + value = case :erlang.binary_to_atom(value, :unicode) do val when is_boolean(val) -> val end - acc = %{ acc | refout: value } + + acc = %{acc | refout: value} parse(rest, acc) end - defp parse("sick=" <> rest, acc = %{ sick: nil }) do + + defp parse("sick=" <> rest, acc = %{sick: nil}) do {rest, value} = take_until_whitespace(rest, <<>>) + value = case :erlang.binary_to_atom(value, :unicode) do val when is_boolean(val) -> val end - acc = %{ acc | sick: value } + + acc = %{acc | sick: value} parse(rest, acc) end - defp parse("name=" <> rest, acc = %{ name: nil }) do + + defp parse("name=" <> rest, acc = %{name: nil}) do {rest, value} = take_until_whitespace(rest, <<>>) name = strip_quotes(value, <<>>) root_key = :erlang.binary_to_atom(underscore(value, <<>>), :unicode) - acc = %{ acc | name: name, key: root_key } + acc = %{acc | name: name, key: root_key} parse(rest, acc) end - defp parse("alias=" <> rest, acc = %{ aliases: aliases }) do + + defp parse("alias=" <> rest, acc = %{aliases: aliases}) do {rest, value} = take_until_whitespace(rest, <<>>) name = strip_quotes(value, <<>>) key = :erlang.binary_to_atom(underscore(value, <<>>), :unicode) aliases = Map.put(aliases, key, name) - acc = %{ acc | aliases: aliases } + acc = %{acc | aliases: aliases} parse(rest, acc) end - defp parse(<< ?\n, rest :: binary >>, acc) do + + defp parse(<>, acc) do acc = if is_nil(acc.sick) do - %{ acc | sick: false } + %{acc | sick: false} else acc end + if is_nil(acc.width) or is_nil(acc.refin) or is_nil(acc.poly) do {:error, acc, rest} else - bits = (div(acc.width, 8) + (if rem(acc.width, 8) == 0, do: 0, else: 1)) * 8 + bits = (div(acc.width, 8) + if(rem(acc.width, 8) == 0, do: 0, else: 1)) * 8 + bits = case bits do _ when bits in [8, 16, 32, 64] -> bits @@ -259,15 +342,18 @@ defmodule CRC.Model do _ when bits > 16 and bits < 32 -> 32 _ when bits > 32 and bits < 64 -> 64 end - acc = %{ acc | bits: bits } + + acc = %{acc | bits: bits} {:ok, acc, rest} end end - defp parse(<< _, rest :: binary >>, acc) do + + defp parse(<<_, rest::binary>>, acc) do parse(rest, acc) end + defp parse(<<>>, acc) do - parse(<< ?\n >>, acc) + parse(<>, acc) end @doc false @@ -277,49 +363,57 @@ defmodule CRC.Model do @doc false defp encode_hex(value, bits) do - "0x" <> Base.encode16(<< value :: unsigned-big-integer-unit(1)-size(bits) >>, case: :lower) + "0x" <> Base.encode16(<>, case: :lower) end @doc false defp take_until_whitespace(<<>>, acc) do {<<>>, acc} end - defp take_until_whitespace(rest = << ?\n, _ :: binary >>, acc) do + + defp take_until_whitespace(rest = <>, acc) do {rest, acc} end - defp take_until_whitespace(<< ?\s, rest :: binary >>, acc) do + + defp take_until_whitespace(<>, acc) do {rest, acc} end - defp take_until_whitespace(<< c, rest :: binary >>, acc) do - take_until_whitespace(rest, << acc ::binary, c >>) + + defp take_until_whitespace(<>, acc) do + take_until_whitespace(rest, <>) end @doc false - defp underscore(<< ?", rest :: binary >>, acc) do + defp underscore(<>, acc) do underscore(rest, acc) end - defp underscore(<< c, rest :: binary >>, acc) when c in ?A..?Z do - underscore(rest, << acc :: binary, (c + 32) >>) + + defp underscore(<>, acc) when c in ?A..?Z do + underscore(rest, <>) end - defp underscore(<< c, rest :: binary >>, acc) when c in ?a..?z or c in ?0..?9 do - underscore(rest, << acc :: binary, c >>) + + defp underscore(<>, acc) when c in ?a..?z or c in ?0..?9 do + underscore(rest, <>) end - defp underscore(<< c, rest :: binary >>, acc) when c in [?-, ?/] do - underscore(rest, << acc :: binary, ?_ >>) + + defp underscore(<>, acc) when c in [?-, ?/] do + underscore(rest, <>) end + defp underscore(<<>>, acc) do acc end @doc false - defp strip_quotes(<< ?", rest :: binary >>, acc) do + defp strip_quotes(<>, acc) do strip_quotes(rest, acc) end - defp strip_quotes(<< c, rest :: binary >>, acc) do - strip_quotes(rest, << acc :: binary, c >>) + + defp strip_quotes(<>, acc) do + strip_quotes(rest, <>) end + defp strip_quotes(<<>>, acc) do acc end - end diff --git a/mix.exs b/mix.exs index 4e258fd..355fb1d 100644 --- a/mix.exs +++ b/mix.exs @@ -8,7 +8,7 @@ defmodule CRC.Mixfile do [ app: :crc, version: @version, - elixir: ">= 1.4.2 and < 2.0.0", + elixir: "~> 1.14", elixirc_paths: elixirc_paths(Mix.env()), build_embedded: Mix.env() == :prod, start_permanent: Mix.env() == :prod, diff --git a/mix.lock b/mix.lock index 791a10d..31f4388 100644 --- a/mix.lock +++ b/mix.lock @@ -2,14 +2,15 @@ "bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], []}, "credo": {:hex, :credo, "0.8.6", "335f723772d35da499b5ebfdaf6b426bfb73590b6fcbc8908d476b75f8cbca3f", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, optional: false]}]}, "earmark": {:hex, :earmark, "1.4.3", "364ca2e9710f6bff494117dbbd53880d84bebb692dafc3a78eb50aa3183f2bfd", [:mix], [], "hexpm", "8cf8a291ebf1c7b9539e3cddb19e9cef066c2441b1640f13c34c1d3cfc825fec"}, - "earmark_parser": {:hex, :earmark_parser, "1.4.13", "0c98163e7d04a15feb62000e1a891489feb29f3d10cb57d4f845c405852bbef8", [:mix], [], "hexpm", "d602c26af3a0af43d2f2645613f65841657ad6efc9f0e361c3b6c06b578214ba"}, - "elixir_make": {:hex, :elixir_make, "0.6.0", "38349f3e29aff4864352084fc736fa7fa0f2995a819a737554f7ebd28b85aaab", [:mix], [], "hexpm", "d522695b93b7f0b4c0fcb2dfe73a6b905b1c301226a5a55cb42e5b14d509e050"}, - "ex_doc": {:hex, :ex_doc, "0.24.2", "e4c26603830c1a2286dae45f4412a4d1980e1e89dc779fcd0181ed1d5a05c8d9", [:mix], [{:earmark_parser, "~> 1.4.0", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "e134e1d9e821b8d9e4244687fb2ace58d479b67b282de5158333b0d57c6fb7da"}, - "makeup": {:hex, :makeup, "1.0.5", "d5a830bc42c9800ce07dd97fa94669dfb93d3bf5fcf6ea7a0c67b2e0e4a7f26c", [:mix], [{:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cfa158c02d3f5c0c665d0af11512fed3fba0144cf1aadee0f2ce17747fba2ca9"}, - "makeup_elixir": {:hex, :makeup_elixir, "0.15.1", "b5888c880d17d1cc3e598f05cdb5b5a91b7b17ac4eaf5f297cb697663a1094dd", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.1", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "db68c173234b07ab2a07f645a5acdc117b9f99d69ebf521821d89690ae6c6ec8"}, - "makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"}, - "nimble_parsec": {:hex, :nimble_parsec, "1.1.0", "3a6fca1550363552e54c216debb6a9e95bd8d32348938e13de5eda962c0d7f89", [:mix], [], "hexpm", "08eb32d66b706e913ff748f11694b17981c0b04a33ef470e33e11b3d3ac8f54b"}, - "propcheck": {:hex, :propcheck, "1.0.4", "f83174301b637bce011a4ebf0e4f10e9bae38d6eefebdd117677c7884bc99aa4", [:mix], [{:proper, "~> 1.2.0", [hex: :proper, repo: "hexpm", optional: false]}], "hexpm", "b77d96e155d0c569fd1b18275655751ddf59483bd6e7e6ff5d54fb5bdd3be224"}, - "proper": {:hex, :proper, "1.2.0", "1466492385959412a02871505434e72e92765958c60dba144b43863554b505a4", [:make, :mix, :rebar3], [], "hexpm", "cbc3766c08337806741343d330bf4bcb826155d2141be8514c4b02858aa19fd3"}, + "earmark_parser": {:hex, :earmark_parser, "1.4.44", "f20830dd6b5c77afe2b063777ddbbff09f9759396500cdbe7523efd58d7a339c", [:mix], [], "hexpm", "4778ac752b4701a5599215f7030989c989ffdc4f6df457c5f36938cc2d2a2750"}, + "elixir_make": {:hex, :elixir_make, "0.9.0", "6484b3cd8c0cee58f09f05ecaf1a140a8c97670671a6a0e7ab4dc326c3109726", [:mix], [], "hexpm", "db23d4fd8b757462ad02f8aa73431a426fe6671c80b200d9710caf3d1dd0ffdb"}, + "ex_doc": {:hex, :ex_doc, "0.40.1", "67542e4b6dde74811cfd580e2c0149b78010fd13001fda7cfeb2b2c2ffb1344d", [:mix], [{:earmark_parser, "~> 1.4.44", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "bcef0e2d360d93ac19f01a85d58f91752d930c0a30e2681145feea6bd3516e00"}, + "libgraph": {:hex, :libgraph, "0.16.0", "3936f3eca6ef826e08880230f806bfea13193e49bf153f93edcf0239d4fd1d07", [:mix], [], "hexpm", "41ca92240e8a4138c30a7e06466acc709b0cbb795c643e9e17174a178982d6bf"}, + "makeup": {:hex, :makeup, "1.2.1", "e90ac1c65589ef354378def3ba19d401e739ee7ee06fb47f94c687016e3713d1", [:mix], [{:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d36484867b0bae0fea568d10131197a4c2e47056a6fbe84922bf6ba71c8d17ce"}, + "makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"}, + "makeup_erlang": {:hex, :makeup_erlang, "1.0.3", "4252d5d4098da7415c390e847c814bad3764c94a814a0b4245176215615e1035", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "953297c02582a33411ac6208f2c6e55f0e870df7f80da724ed613f10e6706afd"}, + "nimble_parsec": {:hex, :nimble_parsec, "1.4.2", "8efba0122db06df95bfaa78f791344a89352ba04baedd3849593bfce4d0dc1c6", [:mix], [], "hexpm", "4b21398942dda052b403bbe1da991ccd03a053668d147d53fb8c4e0efe09c973"}, + "propcheck": {:hex, :propcheck, "1.5.0", "df6f554a97eba4f92c3e4bc7758c8480e8bc401188af32cfe09641e81d8a55ba", [:mix], [{:libgraph, "~> 0.13", [hex: :libgraph, repo: "hexpm", optional: false]}, {:proper, "~> 1.5", [hex: :proper, repo: "hexpm", optional: false]}], "hexpm", "3388aa7909277faed166dfced39fad6ecae3c3be064352d37646be459fc80e09"}, + "proper": {:hex, :proper, "1.5.0", "4a87ce8b219858e64c9aa015f4f7ed739d0ac3ca6bfd77fa62794617ee784225", [:rebar3], [], "hexpm", "fbf7cf0625e5441a314514f7d8e1eaff52090fcd94ff34b5171ff293d3f9969a"}, "shouldi": {:hex, :shouldi, "0.2.3"}, } diff --git a/test/crc_16_test.exs b/test/crc_16_test.exs index bdb0225..6e6aef9 100644 --- a/test/crc_16_test.exs +++ b/test/crc_16_test.exs @@ -35,7 +35,6 @@ defmodule CRC_16_Test do assert CRC.ccitt_16(large_input) == 0xBE01 end - test "calculate correct CRC-16 ccitt with default 0xFFFF - test data 2" do assert CRC.ccitt_16(@test_data_02) == 0x8796 end @@ -71,7 +70,7 @@ defmodule CRC_16_Test do end test "calculate correct CRC-16 kermit - test data 2" do - assert CRC.ccitt_16_kermit(@test_data_02) == 0xf90c + assert CRC.ccitt_16_kermit(@test_data_02) == 0xF90C end # Modbus diff --git a/test/crc_test.exs b/test/crc_test.exs index d403997..6f5a2f0 100644 --- a/test/crc_test.exs +++ b/test/crc_test.exs @@ -14,7 +14,7 @@ defmodule CRCTest do assert CRC.crc_8(large_input) == 0xF4 assert CRC.crc_8(large_input) == CRC8KOOP.calc(large_input) end - + test "CRC-8 test data 2" do assert CRC.crc_8(@test_data_02) == 0x90 assert CRC.crc_8(@test_data_02) == CRC8KOOP.calc(@test_data_02) @@ -53,100 +53,112 @@ defmodule CRCTest do end test "fast module verifies all checks" do - assert :crc_algorithm.verify_check(:crc_fast, %{ display: :failed }) == [] + assert :crc_algorithm.verify_check(:crc_fast, %{display: :failed}) == [] end test "pure module verifies all checks" do - assert :crc_algorithm.verify_check(:crc_pure, %{ display: :failed }) == [] + assert :crc_algorithm.verify_check(:crc_pure, %{display: :failed}) == [] end test "slow module verifies all checks" do - assert :crc_algorithm.verify_check(:crc_slow, %{ display: :failed }) == [] + assert :crc_algorithm.verify_check(:crc_slow, %{display: :failed}) == [] end test "fast module verifies all residues" do - assert :crc_algorithm.verify_residue(:crc_fast, %{ display: :failed }) == [] + assert :crc_algorithm.verify_residue(:crc_fast, %{display: :failed}) == [] end test "pure module verifies all residues" do - assert :crc_algorithm.verify_residue(:crc_pure, %{ display: :failed }) == [] + assert :crc_algorithm.verify_residue(:crc_pure, %{display: :failed}) == [] end test "slow module verifies all residues" do - assert :crc_algorithm.verify_residue(:crc_slow, %{ display: :failed }) == [] + assert :crc_algorithm.verify_residue(:crc_slow, %{display: :failed}) == [] end property "matching CRC for known models" do models = Map.keys(:crc_nif.crc_list()) + forall {model, input} in {oneof(models), binary()} do - :crc_slow.calc(model, input) === :crc_fast.calc(model, input) and :crc_fast.calc(model, input) === :crc_pure.calc(model, input) + :crc_slow.calc(model, input) === :crc_fast.calc(model, input) and + :crc_fast.calc(model, input) === :crc_pure.calc(model, input) end end property "matching CRC for unknown models" do - model_gen_unsafe = let { - width, - poly, - init, - refin, - refout, - xorout - } <- { - integer(1, 64), - such_that(n <- integer(), when: n > 0), - integer(), - boolean(), - boolean(), - integer() - } do - msb_mask = 1 <<< (width - 1) - crc_mask = 1 ||| ((msb_mask - 1) <<< 1) - %{ - width: width, - poly: poly &&& crc_mask, - init: init &&& crc_mask, - refin: refin, - refout: refout, - xorout: xorout &&& crc_mask - } - end - model_gen = such_that(%{ poly: poly } <- model_gen_unsafe, when: (poly > 0) and rem(poly, 2) != 0) + model_gen_unsafe = + let { + width, + poly, + init, + refin, + refout, + xorout + } <- { + integer(1, 64), + such_that(n <- integer(), when: n > 0), + integer(), + boolean(), + boolean(), + integer() + } do + msb_mask = 1 <<< (width - 1) + crc_mask = 1 ||| (msb_mask - 1) <<< 1 + + %{ + width: width, + poly: poly &&& crc_mask, + init: init &&& crc_mask, + refin: refin, + refout: refout, + xorout: xorout &&& crc_mask + } + end + + model_gen = such_that(%{poly: poly} <- model_gen_unsafe, when: poly > 0 and rem(poly, 2) != 0) + forall {model, input} <- {model_gen, binary()} do - :crc_slow.calc(model, input) === :crc_fast.calc(model, input) and :crc_fast.calc(model, input) === :crc_pure.calc(model, input) + :crc_slow.calc(model, input) === :crc_fast.calc(model, input) and + :crc_fast.calc(model, input) === :crc_pure.calc(model, input) end end property "matching SICK for unknown models" do - model_gen_unsafe = let { - width, - poly, - init, - refin, - refout, - xorout - } <- { - return(16), - such_that(n <- integer(), when: n > 0), - integer(), - boolean(), - boolean(), - integer() - } do - msb_mask = 1 <<< (width - 1) - crc_mask = 1 ||| ((msb_mask - 1) <<< 1) - %{ - width: width, - poly: poly &&& crc_mask, - init: init &&& crc_mask, - refin: refin, - refout: refout, - xorout: xorout &&& crc_mask, - sick: true - } - end - model_gen = such_that(%{ poly: poly } <- model_gen_unsafe, when: (poly > 0) and rem(poly, 2) != 0) + model_gen_unsafe = + let { + width, + poly, + init, + refin, + refout, + xorout + } <- { + return(16), + such_that(n <- integer(), when: n > 0), + integer(), + boolean(), + boolean(), + integer() + } do + msb_mask = 1 <<< (width - 1) + crc_mask = 1 ||| (msb_mask - 1) <<< 1 + + %{ + width: width, + poly: poly &&& crc_mask, + init: init &&& crc_mask, + refin: refin, + refout: refout, + xorout: xorout &&& crc_mask, + sick: true + } + end + + model_gen = such_that(%{poly: poly} <- model_gen_unsafe, when: poly > 0 and rem(poly, 2) != 0) + forall {model, input} <- {model_gen, binary()} do - :crc_slow.calc(model, input) === :crc_fast.calc(model, input) and :crc_fast.calc(model, input) === :crc_pure.calc(model, input) + :crc_slow.calc(model, input) === :crc_fast.calc(model, input) and + :crc_fast.calc(model, input) === :crc_pure.calc(model, input) end end @@ -154,13 +166,18 @@ defmodule CRCTest do property "verifies against pycrc" do models = Map.keys(:crc_nif.crc_list()) # Remove unsupported pycrc models - models = models -- [ - :crc_16_sick - ] + models = + models -- + [ + :crc_16_sick + ] + infos = for model <- models, into: %{}, do: {model, :crc_fast.info(:crc_fast.init(model))} - f = fn (x, bits) -> - "0x" <> Base.encode16(<< x :: unsigned-big-integer-unit(1)-size(bits) >>, case: :lower) + + f = fn x, bits -> + "0x" <> Base.encode16(<>, case: :lower) end + forall {model, input} in {oneof(models), binary()} do %{ bits: bits, @@ -171,6 +188,7 @@ defmodule CRCTest do refout: refout, xorout: xorout } = infos[model] + command = << "#{System.get_env("PYCRC_BIN")}", " --algorithm=bbb", @@ -182,21 +200,25 @@ defmodule CRCTest do " --reflect-out=#{if refout, do: "True", else: "False"}", " --xor-out=#{f.(xorout, bits)}" >> + results = command |> :erlang.binary_to_list() |> :os.cmd() |> :erlang.list_to_binary() |> String.trim() + results = case results do - << "0x", rest :: binary >> -> rest + <<"0x", rest::binary>> -> rest _ -> results end + crc_le = :erlang.binary_to_integer(results, 16) fast_challenge = :crc_fast.calc(model, input) pure_challenge = :crc_pure.calc(model, input) slow_challenge = :crc_slow.calc(model, input) + if fast_challenge === crc_le do fast_challenge === crc_le and pure_challenge === crc_le and slow_challenge === crc_le else @@ -206,8 +228,9 @@ defmodule CRCTest do else width end - crc_le_bin = << crc_le :: unsigned-little-integer-unit(1)-size(size) >> - << crc_be :: unsigned-big-integer-unit(1)-size(size) >> = crc_le_bin + + crc_le_bin = <> + <> = crc_le_bin fast_challenge === crc_be and pure_challenge === crc_be and slow_challenge === crc_be end end @@ -218,44 +241,57 @@ defmodule CRCTest do property "verifies against reveng" do models = Map.keys(:crc_nif.crc_list()) # Remove unsupported RevEng models - models = models -- [ - :crc_8_koop, - :crc_16_sick, - :crc_64_jones - ] + models = + models -- + [ + :crc_8_koop, + :crc_16_sick, + :crc_64_jones + ] + infos = for model <- models, into: %{}, do: {model, :crc_fast.info(:crc_fast.init(model))} - names = for {model, %{ name: name }} <- infos, into: %{} do - name = - case name do - "CRC-16/A" -> "CRC-A" - "CRC-16/MODBUS" -> "MODBUS" - "CRC-16/X-25" -> "X-25" - "CRC-16/XMODEM" -> "XMODEM" - "CRC-32/JAMCRC" -> "JAMCRC" - "CRC-32/XFER" -> "XFER" - _ -> name - end - {model, name} - end - sizes = for {model, %{ bits: bits }} <- infos, into: %{}, do: {model, bits} - widths = for {model, %{ width: width }} <- infos, into: %{}, do: {model, width} + + names = + for {model, %{name: name}} <- infos, into: %{} do + name = + case name do + "CRC-16/A" -> "CRC-A" + "CRC-16/MODBUS" -> "MODBUS" + "CRC-16/X-25" -> "X-25" + "CRC-16/XMODEM" -> "XMODEM" + "CRC-32/JAMCRC" -> "JAMCRC" + "CRC-32/XFER" -> "XFER" + _ -> name + end + + {model, name} + end + + sizes = for {model, %{bits: bits}} <- infos, into: %{}, do: {model, bits} + widths = for {model, %{width: width}} <- infos, into: %{}, do: {model, width} + forall {model, input} in {oneof(models), binary()} do - command = "#{System.get_env("REVENG_BIN")} -c -m \"#{names[model]}\" \"#{Base.encode16(input)}\"" + command = + "#{System.get_env("REVENG_BIN")} -c -m \"#{names[model]}\" \"#{Base.encode16(input)}\"" + results = command |> :erlang.binary_to_list() |> :os.cmd() |> :erlang.list_to_binary() |> String.trim() + results = case results do - << "0x", rest :: binary >> -> rest + <<"0x", rest::binary>> -> rest _ -> results end + crc_le = :erlang.binary_to_integer(results, 16) fast_challenge = :crc_fast.calc(model, input) pure_challenge = :crc_pure.calc(model, input) slow_challenge = :crc_slow.calc(model, input) + if fast_challenge === crc_le do fast_challenge === crc_le and pure_challenge === crc_le and slow_challenge === crc_le else @@ -265,8 +301,9 @@ defmodule CRCTest do else widths[model] end - crc_le_bin = << crc_le :: unsigned-little-integer-unit(1)-size(size) >> - << crc_be :: unsigned-big-integer-unit(1)-size(size) >> = crc_le_bin + + crc_le_bin = <> + <> = crc_le_bin fast_challenge === crc_be and pure_challenge === crc_be and slow_challenge === crc_be end end diff --git a/test/support/checksum_xor.ex b/test/support/checksum_xor.ex index c0660aa..13313b1 100644 --- a/test/support/checksum_xor.ex +++ b/test/support/checksum_xor.ex @@ -9,8 +9,8 @@ defmodule ChecksumXOR do defp calc(<<>>, sum) do sum end - defp calc(<< c, rest :: binary() >>, sum) do + + defp calc(<>, sum) do calc(rest, bxor(sum, c)) end - end diff --git a/test/support/crc_16.ex b/test/support/crc_16.ex index 7efbf3c..1e2a412 100644 --- a/test/support/crc_16.ex +++ b/test/support/crc_16.ex @@ -14,9 +14,10 @@ defmodule CRC16 do end def update(crc, <<>>) do - crc &&& 0xffff + crc &&& 0xFFFF end - def update(crc, << c, buf :: binary() >>) do + + def update(crc, <>) do update(do_update(crc, c, 0), buf) end @@ -26,12 +27,14 @@ defmodule CRC16 do @doc false defp do_final(crc, 16) do - crc = crc &&& 0xffff + crc = crc &&& 0xFFFF do_final_reverse(crc, 0x8000, 0x0001, 0) end + defp do_final(crc, i) do flag = crc >>> 15 - crc = (crc <<< 1) &&& 0xffff + crc = crc <<< 1 &&& 0xFFFF + if flag === 0 do do_final(crc, i + 1) else @@ -41,9 +44,10 @@ defmodule CRC16 do @doc false defp do_final_reverse(_crc, 0, _j, acc) do - acc &&& 0xffff + acc &&& 0xFFFF # acc end + defp do_final_reverse(crc, i, j, acc) do if (i &&& crc) === 0 do do_final_reverse(crc, i >>> 1, j <<< 1, acc) @@ -54,16 +58,17 @@ defmodule CRC16 do @doc false defp do_update(crc, _c, 8) do - crc &&& 0xffff + crc &&& 0xFFFF end + defp do_update(crc, c, read) do flag = crc >>> 15 - crc = ((crc <<< 1) &&& 0xffff) ||| ((c >>> read) &&& 0x01) + crc = (crc <<< 1 &&& 0xFFFF) ||| (c >>> read &&& 0x01) + if flag === 0 do do_update(crc, c, read + 1) else do_update(bxor(crc, @crc16), c, read + 1) end end - end diff --git a/test/support/crc_8_koop.ex b/test/support/crc_8_koop.ex index 6d5c3b5..7408817 100644 --- a/test/support/crc_8_koop.ex +++ b/test/support/crc_8_koop.ex @@ -9,17 +9,18 @@ defmodule CRC8KOOP do |> final() end - def init(seed \\ 0xff) do - bxor(seed, 0xff) + def init(seed \\ 0xFF) do + bxor(seed, 0xFF) end def update(crc, <<>>) do - crc &&& 0xff + crc &&& 0xFF end - def update(crc, << d, data :: binary() >>) do + + def update(crc, <>) do c = reflect(d, 8) crc = do_update(crc, c, 0) - update(crc &&& 0xff, data) + update(crc &&& 0xFF, data) end def final(crc) do @@ -29,15 +30,17 @@ defmodule CRC8KOOP do @doc false defp do_final(crc, 8) do crc = reflect(crc, 8) - (bxor(crc, 0xff)) &&& 0xff + bxor(crc, 0xFF) &&& 0xFF end + defp do_final(crc, i) do bit = crc &&& 0x80 crc = crc <<< 1 + if bit === 0 do do_final(crc, i + 1) else - do_final(bxor(crc, 0x4d), i + 1) + do_final(bxor(crc, 0x4D), i + 1) end end @@ -45,13 +48,15 @@ defmodule CRC8KOOP do defp do_update(crc, _c, 8) do crc end + defp do_update(crc, c, i) do bit = crc &&& 0x80 - crc = (crc <<< 1) ||| ((c >>> (7 - i)) &&& 0x01) + crc = crc <<< 1 ||| (c >>> (7 - i) &&& 0x01) + if bit === 0 do do_update(crc, c, i + 1) else - do_update(bxor(crc, 0x4d), c, i + 1) + do_update(bxor(crc, 0x4D), c, i + 1) end end @@ -64,11 +69,11 @@ defmodule CRC8KOOP do @doc false defp reflect(data, data_len, i, ret) when i < data_len do data = data >>> 1 - ret = (ret <<< 1) ||| (data &&& 0x01) + ret = ret <<< 1 ||| (data &&& 0x01) reflect(data, data_len, i + 1, ret) end + defp reflect(_data, _data_len, _i, ret) do ret end - end From bf1d6d0d054b90fc548eff52aed685d4a1ce1692 Mon Sep 17 00:00:00 2001 From: Rodney Norris Date: Fri, 20 Mar 2026 15:29:43 -0500 Subject: [PATCH 2/2] chore: add credo and fix all initial issues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add credo ~> 1.7 as dev/test dependency - Rename test modules to PascalCase (CRC_16_Test → CRC16Test, CRC_32_Test → CRC32Test) - Add @moduledoc false to internal/test-support modules (CRC.Model, CRC16, CRC8KOOP, ChecksumXOR) - Suppress FunctionNames check on ccitt_16_1D0F (legacy public API, renaming is breaking) - Extract normalize_bits/1 and round_to_supported_bits/1 from CRC.Model.parse/2 to reduce cyclomatic complexity - Update README copyright year Co-Authored-By: Claude Sonnet 4.6 --- .credo.exs | 221 +++++++++++++++++++++++++++++++++++ README.md | 2 +- lib/crc/legacy.ex | 1 + lib/crc/model.ex | 26 +++-- mix.exs | 1 + mix.lock | 6 +- test/crc_16_test.exs | 2 +- test/crc_32_test.exs | 2 +- test/support/checksum_xor.ex | 1 + test/support/crc_16.ex | 1 + test/support/crc_8_koop.ex | 1 + 11 files changed, 247 insertions(+), 17 deletions(-) create mode 100644 .credo.exs diff --git a/.credo.exs b/.credo.exs new file mode 100644 index 0000000..0e4b6a0 --- /dev/null +++ b/.credo.exs @@ -0,0 +1,221 @@ +# This file contains the configuration for Credo and you are probably reading +# this after creating it with `mix credo.gen.config`. +# +# If you find anything wrong or unclear in this file, please report an +# issue on GitHub: https://github.com/rrrene/credo/issues +# +%{ + # + # You can have as many configs as you like in the `configs:` field. + configs: [ + %{ + # + # Run any config using `mix credo -C `. If no config name is given + # "default" is used. + # + name: "default", + # + # These are the files included in the analysis: + files: %{ + # + # You can give explicit globs or simply directories. + # In the latter case `**/*.{ex,exs}` will be used. + # + included: [ + "lib/", + "src/", + "test/", + "web/", + "apps/*/lib/", + "apps/*/src/", + "apps/*/test/", + "apps/*/web/" + ], + excluded: [~r"/_build/", ~r"/deps/", ~r"/node_modules/"] + }, + # + # Load and configure plugins here: + # + plugins: [], + # + # If you create your own checks, you must specify the source files for + # them here, so they can be loaded by Credo before running the analysis. + # + requires: [], + # + # If you want to enforce a style guide and need a more traditional linting + # experience, you can change `strict` to `true` below: + # + strict: false, + # + # To modify the timeout for parsing files, change this value: + # + parse_timeout: 5000, + # + # If you want to use uncolored output by default, you can change `color` + # to `false` below: + # + color: true, + # + # You can customize the parameters of any check by adding a second element + # to the tuple. + # + # To disable a check put `false` as second element: + # + # {Credo.Check.Design.DuplicatedCode, false} + # + checks: %{ + enabled: [ + # + ## Consistency Checks + # + {Credo.Check.Consistency.ExceptionNames, []}, + {Credo.Check.Consistency.LineEndings, []}, + {Credo.Check.Consistency.ParameterPatternMatching, []}, + {Credo.Check.Consistency.SpaceAroundOperators, []}, + {Credo.Check.Consistency.SpaceInParentheses, []}, + {Credo.Check.Consistency.TabsOrSpaces, []}, + + # + ## Design Checks + # + # You can customize the priority of any check + # Priority values are: `low, normal, high, higher` + # + {Credo.Check.Design.AliasUsage, + [priority: :low, if_nested_deeper_than: 2, if_called_more_often_than: 0]}, + {Credo.Check.Design.TagFIXME, []}, + # You can also customize the exit_status of each check. + # If you don't want TODO comments to cause `mix credo` to fail, just + # set this value to 0 (zero). + # + {Credo.Check.Design.TagTODO, [exit_status: 2]}, + + # + ## Readability Checks + # + {Credo.Check.Readability.AliasOrder, []}, + {Credo.Check.Readability.FunctionNames, []}, + {Credo.Check.Readability.LargeNumbers, []}, + {Credo.Check.Readability.MaxLineLength, [priority: :low, max_length: 120]}, + {Credo.Check.Readability.ModuleAttributeNames, []}, + {Credo.Check.Readability.ModuleDoc, []}, + {Credo.Check.Readability.ModuleNames, []}, + {Credo.Check.Readability.ParenthesesInCondition, []}, + {Credo.Check.Readability.ParenthesesOnZeroArityDefs, []}, + {Credo.Check.Readability.PipeIntoAnonymousFunctions, []}, + {Credo.Check.Readability.PredicateFunctionNames, []}, + {Credo.Check.Readability.PreferImplicitTry, []}, + {Credo.Check.Readability.RedundantBlankLines, []}, + {Credo.Check.Readability.Semicolons, []}, + {Credo.Check.Readability.SpaceAfterCommas, []}, + {Credo.Check.Readability.StringSigils, []}, + {Credo.Check.Readability.TrailingBlankLine, []}, + {Credo.Check.Readability.TrailingWhiteSpace, []}, + {Credo.Check.Readability.UnnecessaryAliasExpansion, []}, + {Credo.Check.Readability.VariableNames, []}, + {Credo.Check.Readability.WithSingleClause, []}, + + # + ## Refactoring Opportunities + # + {Credo.Check.Refactor.Apply, []}, + {Credo.Check.Refactor.CondStatements, []}, + {Credo.Check.Refactor.CyclomaticComplexity, []}, + {Credo.Check.Refactor.FilterCount, []}, + {Credo.Check.Refactor.FilterFilter, []}, + {Credo.Check.Refactor.FunctionArity, []}, + {Credo.Check.Refactor.LongQuoteBlocks, []}, + {Credo.Check.Refactor.MapJoin, []}, + {Credo.Check.Refactor.MatchInCondition, []}, + {Credo.Check.Refactor.NegatedConditionsInUnless, []}, + {Credo.Check.Refactor.NegatedConditionsWithElse, []}, + {Credo.Check.Refactor.Nesting, []}, + {Credo.Check.Refactor.RedundantWithClauseResult, []}, + {Credo.Check.Refactor.RejectReject, []}, + {Credo.Check.Refactor.UnlessWithElse, []}, + {Credo.Check.Refactor.WithClauses, []}, + + # + ## Warnings + # + {Credo.Check.Warning.ApplicationConfigInModuleAttribute, []}, + {Credo.Check.Warning.BoolOperationOnSameValues, []}, + {Credo.Check.Warning.Dbg, []}, + {Credo.Check.Warning.ExpensiveEmptyEnumCheck, []}, + {Credo.Check.Warning.IExPry, []}, + {Credo.Check.Warning.IoInspect, []}, + {Credo.Check.Warning.MissedMetadataKeyInLoggerConfig, []}, + {Credo.Check.Warning.OperationOnSameValues, []}, + {Credo.Check.Warning.OperationWithConstantResult, []}, + {Credo.Check.Warning.RaiseInsideRescue, []}, + {Credo.Check.Warning.SpecWithStruct, []}, + {Credo.Check.Warning.StructFieldAmount, []}, + {Credo.Check.Warning.UnsafeExec, []}, + {Credo.Check.Warning.UnusedEnumOperation, []}, + {Credo.Check.Warning.UnusedFileOperation, []}, + {Credo.Check.Warning.UnusedKeywordOperation, []}, + {Credo.Check.Warning.UnusedListOperation, []}, + {Credo.Check.Warning.UnusedMapOperation, []}, + {Credo.Check.Warning.UnusedPathOperation, []}, + {Credo.Check.Warning.UnusedRegexOperation, []}, + {Credo.Check.Warning.UnusedStringOperation, []}, + {Credo.Check.Warning.UnusedTupleOperation, []}, + {Credo.Check.Warning.WrongTestFilename, []} + ], + disabled: [ + # + # Checks scheduled for next check update (opt-in for now) + {Credo.Check.Refactor.UtcNowTruncate, []}, + + # + # Controversial and experimental checks (opt-in, just move the check to `:enabled` + # and be sure to use `mix credo --strict` to see low priority checks) + # + {Credo.Check.Consistency.MultiAliasImportRequireUse, []}, + {Credo.Check.Consistency.UnusedVariableNames, []}, + {Credo.Check.Design.DuplicatedCode, []}, + {Credo.Check.Design.SkipTestWithoutComment, []}, + {Credo.Check.Readability.AliasAs, []}, + {Credo.Check.Readability.BlockPipe, []}, + {Credo.Check.Readability.ImplTrue, []}, + {Credo.Check.Readability.MultiAlias, []}, + {Credo.Check.Readability.NestedFunctionCalls, []}, + {Credo.Check.Readability.OneArityFunctionInPipe, []}, + {Credo.Check.Readability.OnePipePerLine, []}, + {Credo.Check.Readability.SeparateAliasRequire, []}, + {Credo.Check.Readability.SingleFunctionToBlockPipe, []}, + {Credo.Check.Readability.SinglePipe, []}, + {Credo.Check.Readability.Specs, []}, + {Credo.Check.Readability.StrictModuleLayout, []}, + {Credo.Check.Readability.WithCustomTaggedTuple, []}, + {Credo.Check.Refactor.ABCSize, []}, + {Credo.Check.Refactor.AppendSingleItem, []}, + {Credo.Check.Refactor.CondInsteadOfIfElse, []}, + {Credo.Check.Refactor.DoubleBooleanNegation, []}, + {Credo.Check.Refactor.FilterReject, []}, + {Credo.Check.Refactor.IoPuts, []}, + {Credo.Check.Refactor.MapMap, []}, + {Credo.Check.Refactor.ModuleDependencies, []}, + {Credo.Check.Refactor.NegatedIsNil, []}, + {Credo.Check.Refactor.PassAsyncInTestCases, []}, + {Credo.Check.Refactor.PipeChainStart, []}, + {Credo.Check.Refactor.RejectFilter, []}, + {Credo.Check.Refactor.VariableRebinding, []}, + {Credo.Check.Warning.LazyLogging, []}, + {Credo.Check.Warning.LeakyEnvironment, []}, + {Credo.Check.Warning.MapGetUnsafePass, []}, + {Credo.Check.Warning.MixEnv, []}, + {Credo.Check.Warning.UnsafeToAtom, []} + # {Credo.Check.Warning.UnusedOperation, [{MyMagicModule, [:fun1, :fun2]}]} + + # {Credo.Check.Refactor.MapInto, []}, + + # + # Custom checks can be created using `mix credo.gen.check`. + # + ] + } + } + ] +} diff --git a/README.md b/README.md index d50dc87..62468ea 100644 --- a/README.md +++ b/README.md @@ -133,7 +133,7 @@ PyCRC is used as a part of the TravisCI test suite. ## Copyright and License -Copyright (c) 2015 Rodney Norris +Copyright (c) 2026 Rodney Norris CRC is released under the MIT License. See the [LICENSE.md](./LICENSE.md) file for further details. diff --git a/lib/crc/legacy.ex b/lib/crc/legacy.ex index c05b978..3eb8306 100644 --- a/lib/crc/legacy.ex +++ b/lib/crc/legacy.ex @@ -62,6 +62,7 @@ defmodule CRC.Legacy do This CCIT method uses a 0x1021 polynomial. """ @spec ccitt_16_1D0F(binary) :: number + # credo:disable-for-next-line Credo.Check.Readability.FunctionNames def ccitt_16_1D0F(input) do extend_model_seed(:crc_16_ccitt_false, 0x1D0F) |> :crc_fast.calc(input) diff --git a/lib/crc/model.ex b/lib/crc/model.ex index 2223d1e..7ad0813 100644 --- a/lib/crc/model.ex +++ b/lib/crc/model.ex @@ -1,4 +1,6 @@ defmodule CRC.Model do + @moduledoc false + @type t() :: %__MODULE__{ bits: 0x00..0xFF, sick: boolean(), @@ -332,18 +334,7 @@ defmodule CRC.Model do if is_nil(acc.width) or is_nil(acc.refin) or is_nil(acc.poly) do {:error, acc, rest} else - bits = (div(acc.width, 8) + if(rem(acc.width, 8) == 0, do: 0, else: 1)) * 8 - - bits = - case bits do - _ when bits in [8, 16, 32, 64] -> bits - _ when bits < 8 -> 8 - _ when bits > 8 and bits < 16 -> 16 - _ when bits > 16 and bits < 32 -> 32 - _ when bits > 32 and bits < 64 -> 64 - end - - acc = %{acc | bits: bits} + acc = %{acc | bits: normalize_bits(acc.width)} {:ok, acc, rest} end end @@ -356,6 +347,17 @@ defmodule CRC.Model do parse(<>, acc) end + defp normalize_bits(width) do + bits = div(width + 7, 8) * 8 + round_to_supported_bits(bits) + end + + defp round_to_supported_bits(bits) when bits in [8, 16, 32, 64], do: bits + defp round_to_supported_bits(bits) when bits < 8, do: 8 + defp round_to_supported_bits(bits) when bits < 16, do: 16 + defp round_to_supported_bits(bits) when bits < 32, do: 32 + defp round_to_supported_bits(_bits), do: 64 + @doc false defp decode_hex(value) do :erlang.binary_to_integer(value, 16) diff --git a/mix.exs b/mix.exs index 355fb1d..5c83338 100644 --- a/mix.exs +++ b/mix.exs @@ -32,6 +32,7 @@ defmodule CRC.Mixfile do defp deps() do [ {:elixir_make, "~> 0.6", runtime: false}, + {:credo, "~> 1.7", only: [:dev, :test], runtime: false}, {:ex_doc, ">= 0.0.0", only: :dev, runtime: false}, {:propcheck, "~> 1.0", only: :test} ] diff --git a/mix.lock b/mix.lock index 31f4388..b155366 100644 --- a/mix.lock +++ b/mix.lock @@ -1,10 +1,12 @@ %{ - "bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], []}, - "credo": {:hex, :credo, "0.8.6", "335f723772d35da499b5ebfdaf6b426bfb73590b6fcbc8908d476b75f8cbca3f", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, optional: false]}]}, + "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, + "credo": {:hex, :credo, "1.7.17", "f92b6aa5b26301eaa5a35e4d48ebf5aa1e7094ac00ae38f87086c562caf8a22f", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "1eb5645c835f0b6c9b5410f94b5a185057bcf6d62a9c2b476da971cde8749645"}, "earmark": {:hex, :earmark, "1.4.3", "364ca2e9710f6bff494117dbbd53880d84bebb692dafc3a78eb50aa3183f2bfd", [:mix], [], "hexpm", "8cf8a291ebf1c7b9539e3cddb19e9cef066c2441b1640f13c34c1d3cfc825fec"}, "earmark_parser": {:hex, :earmark_parser, "1.4.44", "f20830dd6b5c77afe2b063777ddbbff09f9759396500cdbe7523efd58d7a339c", [:mix], [], "hexpm", "4778ac752b4701a5599215f7030989c989ffdc4f6df457c5f36938cc2d2a2750"}, "elixir_make": {:hex, :elixir_make, "0.9.0", "6484b3cd8c0cee58f09f05ecaf1a140a8c97670671a6a0e7ab4dc326c3109726", [:mix], [], "hexpm", "db23d4fd8b757462ad02f8aa73431a426fe6671c80b200d9710caf3d1dd0ffdb"}, "ex_doc": {:hex, :ex_doc, "0.40.1", "67542e4b6dde74811cfd580e2c0149b78010fd13001fda7cfeb2b2c2ffb1344d", [:mix], [{:earmark_parser, "~> 1.4.44", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "bcef0e2d360d93ac19f01a85d58f91752d930c0a30e2681145feea6bd3516e00"}, + "file_system": {:hex, :file_system, "1.1.1", "31864f4685b0148f25bd3fbef2b1228457c0c89024ad67f7a81a3ffbc0bbad3a", [:mix], [], "hexpm", "7a15ff97dfe526aeefb090a7a9d3d03aa907e100e262a0f8f7746b78f8f87a5d"}, + "jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"}, "libgraph": {:hex, :libgraph, "0.16.0", "3936f3eca6ef826e08880230f806bfea13193e49bf153f93edcf0239d4fd1d07", [:mix], [], "hexpm", "41ca92240e8a4138c30a7e06466acc709b0cbb795c643e9e17174a178982d6bf"}, "makeup": {:hex, :makeup, "1.2.1", "e90ac1c65589ef354378def3ba19d401e739ee7ee06fb47f94c687016e3713d1", [:mix], [{:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d36484867b0bae0fea568d10131197a4c2e47056a6fbe84922bf6ba71c8d17ce"}, "makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"}, diff --git a/test/crc_16_test.exs b/test/crc_16_test.exs index 6e6aef9..026f03e 100644 --- a/test/crc_16_test.exs +++ b/test/crc_16_test.exs @@ -1,4 +1,4 @@ -defmodule CRC_16_Test do +defmodule CRC16Test do use ExUnit.Case use PropCheck diff --git a/test/crc_32_test.exs b/test/crc_32_test.exs index 7db1057..0f20ccf 100644 --- a/test/crc_32_test.exs +++ b/test/crc_32_test.exs @@ -1,4 +1,4 @@ -defmodule CRC_32_Test do +defmodule CRC32Test do use ExUnit.Case use PropCheck diff --git a/test/support/checksum_xor.ex b/test/support/checksum_xor.ex index 13313b1..04c49a8 100644 --- a/test/support/checksum_xor.ex +++ b/test/support/checksum_xor.ex @@ -1,4 +1,5 @@ defmodule ChecksumXOR do + @moduledoc false import Bitwise def calc(input) do diff --git a/test/support/crc_16.ex b/test/support/crc_16.ex index 1e2a412..d806334 100644 --- a/test/support/crc_16.ex +++ b/test/support/crc_16.ex @@ -1,4 +1,5 @@ defmodule CRC16 do + @moduledoc false import Bitwise @crc16 0x8005 diff --git a/test/support/crc_8_koop.ex b/test/support/crc_8_koop.ex index 7408817..9f9be19 100644 --- a/test/support/crc_8_koop.ex +++ b/test/support/crc_8_koop.ex @@ -1,4 +1,5 @@ defmodule CRC8KOOP do + @moduledoc false import Bitwise # Adapted from ./pycrc.py --generate=c --algorithm=bbb --model=crc-8-koop