Current section

Files

Jump to
crc lib crc model.ex
Raw

lib/crc/model.ex

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
]
def decode(binary) when is_binary(binary) do
parse(binary, %__MODULE__{})
end
def decode_file(filename) do
with {:ok, content} <- :file.read_file(filename) do
decode_list(content)
end
end
def decode_list(binary) when is_binary(binary) do
decode_list(binary, [])
end
def dump_code(model_or_models) do
IO.puts(gen_code(model_or_models))
end
def gen_code(model = %__MODULE__{}) do
[
gen_stubs(model),
"\n",
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",
tables
]
end
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)
]
entries =
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
table =
if model.sick do
:lists.duplicate(256, 0)
else
context = :crc_nif.crc_init(model)
{true, table} = :crc_nif.debug_table(context)
table
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)
refin = "#{model.refin}"
refout = "#{model.refin}"
xorout = encode_hex(model.xorout, bits)
check = encode_hex(model.check, bits)
residue = encode_hex(model.residue, bits)
name = "\"#{model.name}\""
aliases =
if map_size(model.aliases) == 0 do
[]
else
for {_, val} <- model.aliases, into: [] do
[?\s, "alias=\"", val, ?\"]
end
end
[
if model.sick do
["/* 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"]
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"
]
end
@doc false
defp decode_list(data, acc) 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
{rest, value} = take_until_whitespace(rest, <<>>)
value = :erlang.binary_to_integer(value)
acc = %{ acc | width: value }
parse(rest, acc)
end
defp parse("poly=" <> rest, acc = %{ poly: nil }) do
{rest, "0x" <> value} = take_until_whitespace(rest, <<>>)
value = decode_hex(value)
acc = %{ acc | poly: value }
parse(rest, acc)
end
defp parse("init=" <> rest, acc = %{ init: nil }) do
{rest, "0x" <> value} = take_until_whitespace(rest, <<>>)
value = decode_hex(value)
acc = %{ acc | init: value }
parse(rest, acc)
end
defp parse("xorout=" <> rest, acc = %{ xorout: nil }) do
{rest, "0x" <> value} = take_until_whitespace(rest, <<>>)
value = decode_hex(value)
acc = %{ acc | xorout: value }
parse(rest, acc)
end
defp parse("check=" <> rest, acc = %{ check: nil }) do
{rest, "0x" <> value} = take_until_whitespace(rest, <<>>)
value = decode_hex(value)
acc = %{ acc | check: value }
parse(rest, acc)
end
defp parse("residue=" <> rest, acc = %{ residue: nil }) do
{rest, "0x" <> value} = take_until_whitespace(rest, <<>>)
value = decode_hex(value)
acc = %{ acc | residue: value }
parse(rest, acc)
end
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 }
parse(rest, acc)
end
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 }
parse(rest, acc)
end
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 }
parse(rest, acc)
end
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 }
parse(rest, acc)
end
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 }
parse(rest, acc)
end
defp parse(<< ?\n, rest :: binary() >>, acc) do
acc =
if is_nil(acc.sick) do
%{ 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 =
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 }
{:ok, acc, rest}
end
end
defp parse(<< _, rest :: binary() >>, acc) do
parse(rest, acc)
end
defp parse(<<>>, acc) do
parse(<< ?\n >>, acc)
end
@doc false
defp decode_hex(value) do
:erlang.binary_to_integer(value, 16)
end
@doc false
defp encode_hex(value, bits) do
"0x" <> Base.encode16(<< value :: unsigned-big-integer-unit(1)-size(bits) >>, case: :lower)
end
@doc false
defp take_until_whitespace(<<>>, acc) do
{<<>>, acc}
end
defp take_until_whitespace(rest = << ?\n, _ :: binary() >>, acc) do
{rest, acc}
end
defp take_until_whitespace(<< ?\s, rest :: binary() >>, acc) do
{rest, acc}
end
defp take_until_whitespace(<< c, rest :: binary() >>, acc) do
take_until_whitespace(rest, << acc ::binary(), c >>)
end
@doc false
defp underscore(<< ?", rest :: binary() >>, acc) do
underscore(rest, acc)
end
defp underscore(<< c, rest :: binary() >>, acc) when c in ?A..?Z do
underscore(rest, << acc :: binary(), (c + 32) >>)
end
defp underscore(<< c, rest :: binary() >>, acc) when c in ?a..?z or c in ?0..?9 do
underscore(rest, << acc :: binary(), c >>)
end
defp underscore(<< c, rest :: binary() >>, acc) when c in [?-, ?/] do
underscore(rest, << acc :: binary(), ?_ >>)
end
defp underscore(<<>>, acc) do
acc
end
@doc false
defp strip_quotes(<< ?", rest :: binary() >>, acc) do
strip_quotes(rest, acc)
end
defp strip_quotes(<< c, rest :: binary() >>, acc) do
strip_quotes(rest, << acc :: binary(), c >>)
end
defp strip_quotes(<<>>, acc) do
acc
end
end