Current section

Files

Jump to
crc src crc_pure.erl
Raw

src/crc_pure.erl

%% -*- mode: erlang; tab-width: 4; indent-tabs-mode: 1; st-rulers: [70] -*-
%% vim: ts=4 sw=4 ft=erlang noet
-module(crc_pure).
-behaviour(crc_algorithm).
%% crc_algorithm callbacks
-export([calc/2]).
-export([init/1]).
-export([update/2]).
-export([final/1]).
-export([info/1]).
-export([residue/1]).
%% Elixir API
-export(['__struct__'/0]).
-export(['__struct__'/1]).
%% CRC API
-export([crc_reflect/2]).
-export([crc_init/1]).
-export([crc_update/3]).
-export([crc_final/2]).
-export([crc_residue/1]).
%% SICK API
-export([sick_init/1]).
-export([sick_update/3]).
-export([sick_final/2]).
%%====================================================================
%% crc_algorithm callbacks
%%====================================================================
calc(Params, Iodata) ->
Resource0 = init(Params),
Resource1 = update(Resource0, Iodata),
final(Resource1).
init(Params = {Width, Poly, Init, Refin, Refout, Xorout, Check0, Residue0, Sick0})
when (is_integer(Width) andalso Width >= 0)
andalso (is_integer(Poly) andalso Poly >= 0)
andalso (is_integer(Init) andalso Init >= 0)
andalso is_boolean(Refin) andalso is_boolean(Refout)
andalso (is_integer(Xorout) andalso Xorout >= 0) ->
Check =
case Check0 of
_ when is_integer(Check0) andalso Check0 >= 0 -> Check0;
nil -> 0;
_ -> erlang:error({badarg, [Params]})
end,
Residue =
case Residue0 of
_ when is_integer(Residue0) andalso Residue0 >= 0 -> Residue0;
nil -> 0;
_ -> erlang:error({badarg, [Params]})
end,
Sick =
case Sick0 of
_ when is_boolean(Sick0) -> Sick0;
nil -> false;
_ -> erlang:error({badarg, [Params]})
end,
MSBMask = 1 bsl (Width - 1),
CRCMask = 1 bor ((MSBMask - 1) bsl 1),
CRCShift = case Width < 8 of true -> (8 - Width); false -> 0 end,
case Poly > CRCMask orelse Init > CRCMask orelse Xorout > CRCMask orelse Check > CRCMask orelse Residue > CRCMask of
true ->
erlang:error({badarg, [Params]});
false ->
Resource = '__struct__'(#{
width => Width,
poly => Poly,
init => Init,
refin => Refin,
refout => Refout,
xorout => Xorout,
check => Check,
residue => Residue,
sick => Sick,
msb_mask => MSBMask,
crc_mask => CRCMask,
crc_shift => CRCShift
}),
case Sick of
false ->
Resource#{ value := crc_init(Resource) };
true ->
{Value, Extra} = sick_init(Resource),
Resource#{ value := Value, extra := Extra }
end
end;
init(Key) when is_atom(Key) ->
Resource = crc_fast:init(Key),
Params = crc_nif:crc_info(Resource),
init(Params);
init(Params = #{
width := Width,
poly := Poly,
init := Init,
refin := Refin,
refout := Refout,
xorout := Xorout
})
when (is_integer(Width) andalso Width >= 0)
andalso (is_integer(Poly) andalso Poly >= 0)
andalso (is_integer(Init) andalso Init >= 0)
andalso is_boolean(Refin) andalso is_boolean(Refout)
andalso (is_integer(Xorout) andalso Xorout >= 0) ->
Check = maps:get(check, Params, nil),
Residue = maps:get(residue, Params, nil),
Sick = maps:get(sick, Params, nil),
init({Width, Poly, Init, Refin, Refout, Xorout, Check, Residue, Sick});
init(BadParams) ->
erlang:error({badarg, [BadParams]}).
update(Resource=#{ '__struct__' := ?MODULE, value := Value, sick := false }, Iodata) ->
Resource#{ value := crc_update(Resource, Value, erlang:iolist_to_binary(Iodata)) };
update(Resource=#{ '__struct__' := ?MODULE, value := Value, extra := Extra, sick := true }, Iodata) ->
{NewValue, NewExtra} = sick_update(Resource, {Value, Extra}, erlang:iolist_to_binary(Iodata)),
Resource#{ value := NewValue, extra := NewExtra }.
final(Resource=#{ '__struct__' := ?MODULE, value := Value, sick := false }) ->
crc_final(Resource, Value);
final(Resource=#{ '__struct__' := ?MODULE, value := Value, sick := true }) ->
sick_final(Resource, Value).
info(#{
'__struct__' := ?MODULE,
width := Width,
poly := Poly,
init := Init,
refin := Refin,
refout := Refout,
xorout := Xorout,
check := Check,
residue := Residue,
sick := Sick
}) ->
#{
width => Width,
poly => Poly,
init => Init,
refin => Refin,
refout => Refout,
xorout => Xorout,
check => Check,
residue => Residue,
sick => Sick
};
info(Params) ->
info(init(Params)).
residue(Resource=#{ '__struct__' := ?MODULE, sick := false }) ->
crc_residue(Resource);
residue(Resource=#{ '__struct__' := ?MODULE, width := Width, refout := Refout, xorout := Xorout0, sick := true }) ->
Xorout =
case Refout of
true -> crc_reflect(Xorout0, Width);
false -> Xorout0
end,
Copy = Resource#{ init := 0, xorout := 0, value := 0 },
{CRC, _Extra} = sick_update(Copy, {0, 0}, << Xorout:Width >>),
sick_final(Copy, CRC);
residue(Params) ->
residue(init(Params)).
%%%===================================================================
%%% Elixir API functions
%%%===================================================================
'__struct__'() ->
#{
'__struct__' => ?MODULE,
width => nil,
poly => nil,
init => nil,
refin => nil,
refout => nil,
xorout => nil,
check => nil,
residue => nil,
sick => nil,
msb_mask => nil,
crc_mask => nil,
crc_shift => nil,
value => nil,
extra => nil
}.
'__struct__'(List) when is_list(List) ->
'__struct__'(maps:from_list(List));
'__struct__'(Map) when is_map(Map) ->
maps:fold(fun maps:update/3, '__struct__'(), Map).
%%%===================================================================
%%% CRC API functions
%%%===================================================================
crc_reflect(Reg, Width)
when (is_integer(Reg) andalso Reg >= 0)
andalso (is_integer(Width) andalso Width >= 0) ->
Res = Reg band 16#01,
do_crc_reflect(Res, Reg, 0, Width - 1).
crc_init(#{
width := Width,
poly := Poly,
init := Init,
msb_mask := MSBMask,
crc_mask := CRCMask
}) ->
CRC = Init,
do_crc_init(CRC, 0, Width, Poly, MSBMask, CRCMask).
crc_update(#{}, CRC, <<>>) ->
CRC;
crc_update(#{
poly := Poly,
refin := Refin,
msb_mask := MSBMask,
crc_mask := CRCMask
}, CRC, Rest) ->
do_crc_update(CRC, Rest, Poly, Refin, MSBMask, CRCMask).
crc_final(#{
width := Width,
poly := Poly,
refout := Refout,
xorout := Xorout,
msb_mask := MSBMask,
crc_mask := CRCMask
}, CRC) ->
do_crc_final(CRC, 0, Width, Poly, Refout, Xorout, MSBMask, CRCMask).
crc_residue(Resource = #{
'__struct__' := ?MODULE,
width := Width,
refin := Refin,
refout := Refout,
xorout := Xorout0
}) ->
Copy = Resource#{ init := 0, xorout := 0, value := 0 },
Xorout =
case Refout of
true -> crc_reflect(Xorout0, Width);
false -> Xorout0
end,
Residue = do_crc_residue(Copy, Xorout),
case Refin of
true -> crc_reflect(Residue, Width);
false -> Residue
end.
%%%===================================================================
%%% SICK API functions
%%%===================================================================
sick_init(#{
width := 16,
init := Init
}) ->
{Init, 0};
sick_init(Params) ->
erlang:error({badarg, [Params]}).
sick_update(#{ width := 16 }, {CRC, PrevByte}, <<>>) ->
{CRC, PrevByte};
sick_update(#{
width := 16,
poly := Poly,
msb_mask := MSBMask,
crc_mask := CRCMask,
crc_shift := CRCShift
}, {CRC, PrevByte}, Rest) ->
do_sick_update(CRC, Rest, PrevByte, Poly, MSBMask, CRCMask, CRCShift);
sick_update(Params, CRC, Rest) ->
erlang:error({badarg, [Params, CRC, Rest]}).
sick_final(#{ width := 16 }, CRC) ->
LowByte = (CRC band 16#ff00) bsr 8,
HighByte = (CRC band 16#00ff) bsl 8,
(LowByte bor HighByte);
sick_final(Params, CRC) ->
erlang:error({badarg, [Params, CRC]}).
%%%-------------------------------------------------------------------
%%% Internal functions
%%%-------------------------------------------------------------------
%% @private
do_crc_reflect(Res, _Reg, Max, Max) ->
Res;
do_crc_reflect(Res0, Reg0, I, Max) ->
Reg1 = Reg0 bsr 1,
Res1 = Res0 bsl 1,
Res2 = Res1 bor (Reg1 band 16#01),
do_crc_reflect(Res2, Reg1, I + 1, Max).
%% @private
do_crc_init(CRC, Width, Width, _Poly, _MSBMask, CRCMask) ->
CRC band CRCMask;
do_crc_init(CRC0, I, Width, Poly, MSBMask, CRCMask) ->
Bit = (CRC0 band 16#01),
CRC1 =
case Bit of
0 -> CRC0;
_ -> CRC0 bxor Poly
end,
CRC2 = CRC1 bsr 1,
CRC3 =
case Bit of
0 -> CRC2;
_ -> CRC2 bor MSBMask
end,
do_crc_init(CRC3, I + 1, Width, Poly, MSBMask, CRCMask).
%% @private
do_crc_update(CRC0, << Octet, Rest/binary >>, Poly, Refin = false, MSBMask, CRCMask) ->
CRC1 = do_crc_update_once(CRC0, Octet, 0, 8, Poly, MSBMask, CRCMask),
do_crc_update(CRC1, Rest, Poly, Refin, MSBMask, CRCMask);
do_crc_update(CRC0, << Octet0, Rest/binary >>, Poly, Refin = true, MSBMask, CRCMask) ->
Octet = crc_reflect(Octet0, 8),
CRC1 = do_crc_update_once(CRC0, Octet, 0, 8, Poly, MSBMask, CRCMask),
do_crc_update(CRC1, Rest, Poly, Refin, MSBMask, CRCMask);
do_crc_update(CRC, <<>>, _Poly, _Refin, _MSBMask, _CRCMask) ->
CRC.
%% @private
do_crc_update_once(CRC, _Octet, Max, Max, _Poly, _MSBMask, _CRCMask) ->
CRC;
do_crc_update_once(CRC0, Octet, I, Max, Poly, MSBMask, CRCMask) ->
Bit = (CRC0 band MSBMask),
CRC1 = ((CRC0 bsl 1) band CRCMask) bor ((Octet bsr (7 - I)) band 16#01),
CRC2 =
case Bit of
0 -> CRC1;
_ -> CRC1 bxor Poly
end,
do_crc_update_once(CRC2, Octet, I + 1, Max, Poly, MSBMask, CRCMask).
%% @private
do_crc_final(CRC0, Width, Width, _Poly, _Refout = false, Xorout, _MSBMask, CRCMask) ->
(CRC0 bxor Xorout) band CRCMask;
do_crc_final(CRC0, Width, Width, _Poly, _Refout = true, Xorout, _MSBMask, CRCMask) ->
CRC1 = crc_reflect(CRC0, Width),
(CRC1 bxor Xorout) band CRCMask;
do_crc_final(CRC0, I, Width, Poly, Refout, Xorout, MSBMask, CRCMask) ->
Bit = (CRC0 band MSBMask),
CRC1 = ((CRC0 bsl 1) band CRCMask),
CRC2 =
case Bit of
0 -> CRC1;
_ -> CRC1 bxor Poly
end,
do_crc_final(CRC2, I + 1, Width, Poly, Refout, Xorout, MSBMask, CRCMask).
%% @private
do_crc_residue(#{
width := Width,
poly := Poly,
init := Init,
xorout := Xorout,
msb_mask := MSBMask,
crc_mask := CRCMask
}, Message) ->
do_crc_residue(Init bxor Message, 0, Width, Poly, Xorout, MSBMask, CRCMask).
%% @private
do_crc_residue(Rem, Max, Max, _Poly, Xorout, _MSBMask, CRCMask) ->
(Rem bxor Xorout) band CRCMask;
do_crc_residue(Rem, I, Max, Poly, Xorout, MSBMask, CRCMask) ->
case Rem band MSBMask of
0 ->
do_crc_residue(Rem bsl 1, I + 1, Max, Poly, Xorout, MSBMask, CRCMask);
_ ->
do_crc_residue((Rem bsl 1) bxor Poly, I + 1, Max, Poly, Xorout, MSBMask, CRCMask)
end.
%% @private
do_sick_update(CRC0, << NextByte0, Rest/binary >>, PrevByte0, Poly, MSBMask, CRCMask, CRCShift) ->
NextByte1 = 16#00ff band NextByte0,
CRC1 =
case (CRC0 band (MSBMask bsl CRCShift)) of
0 ->
CRC0 bsl 1;
_ ->
(CRC0 bsl 1) bxor (Poly bsl CRCShift)
end,
CRC2 = CRC1 bxor (NextByte1 bor PrevByte0),
PrevByte1 = NextByte1 bsl 8,
do_sick_update(CRC2, Rest, PrevByte1, Poly, MSBMask, CRCMask, CRCShift);
do_sick_update(CRC, <<>>, PrevByte, _Poly, _MSBMask, CRCMask, _CRCShift) ->
{CRC band CRCMask, PrevByte}.