Current section
Files
Jump to
Current section
Files
src/punycode.erl
-module(punycode).
-export([encode/1,
decode/1]).
%%============================================================================
%% Constants
%%============================================================================
-define(BASE, 36).
-define(TMIN, 1).
-define(TMAX, 26).
-define(SKEW, 38).
-define(DAMP, 700).
-define(INITIAL_BIAS, 72).
-define(INITIAL_N, 128).
-define(DELIMITER, $-).
%%============================================================================
%% Encoding algorithm state
%%============================================================================
-record(encode, {n=?INITIAL_N, delta=0, bias=?INITIAL_BIAS, h, b}).
-record(decode, {n=?INITIAL_N, delta=0, bias=?INITIAL_BIAS, i=0, k, w}).
%%============================================================================
%% API
%%============================================================================
encode(Input) ->
encode(Input, lists:reverse(lists:filter(fun(C) -> C < 16#80 end, Input))).
decode(Input) ->
decode(Input, [], []).
%%============================================================================
%% Helper functions
%%============================================================================
encode(Input, Basic) ->
case length(Basic) of
0 -> encode_whileloop(Input, [], #encode{h=0, b=0});
N -> encode_whileloop(Input, [?DELIMITER|Basic], #encode{h=N, b=N})
end.
encode_whileloop(Input, Output, State=#encode{h=H}) when H < length(Input) ->
N = State#encode.n,
M = lists:min(lists:filter(fun(C) -> C >= N end, Input)),
Delta = State#encode.delta + (M - N) * (H + 1),
{Output2, State2=#encode{delta=Delta2, n=N2}} = encode_foreachloop(Input, Output, State#encode{delta=Delta, n=M}),
encode_whileloop(Input, Output2, State2#encode{delta=Delta2 + 1, n=N2 + 1});
encode_whileloop(_, Output, _) ->
lists:reverse(Output).
encode_foreachloop([], Output, State) ->
{Output, State};
encode_foreachloop([C|Input], Output, State=#encode{n=N, delta=Delta}) when C < N ->
encode_foreachloop(Input, Output, State#encode{delta=Delta + 1});
encode_foreachloop([C|Input], Output, State=#encode{n=N, delta=Delta, h=H, b=B, bias=Bias}) when C =:= N ->
{Output2, Q} = encode_forloop(Output, ?BASE, Delta, Bias),
Bias2 = adapt(Delta, H + 1, H =:= B),
encode_foreachloop(Input, [encode_digit(Q)|Output2], State#encode{delta=0, h=H + 1, bias=Bias2});
encode_foreachloop([_|Input], Output, State) ->
encode_foreachloop(Input, Output, State).
encode_forloop(Output, K, Q, Bias) ->
T = case K =< Bias of
true ->
?TMIN;
false ->
case K >= (Bias + ?TMAX) of true -> ?TMAX; false -> (K - Bias) end
end,
case Q < T of
true ->
{Output, Q};
false ->
Digit = encode_digit(T + ((Q - T) rem (?BASE - T))),
encode_forloop([Digit|Output], K + ?BASE, (Q - T) div (?BASE - T), Bias)
end.
encode_digit(N) when N < 26 ->
N + 22 + 75;
encode_digit(N) ->
N + 22.
% decode
decode([], Head, Tail) ->
decode_whileloop(Tail, Head, #decode{});
% If we have a repeated ?DELIMITER, pass one through into Tail (Output)
decode([?DELIMITER, ?DELIMITER|Input], [], Tail) ->
decode([?DELIMITER|Input], Tail, [?DELIMITER]);
decode([?DELIMITER|Input], [], Tail) ->
decode(Input, Tail, []);
decode([?DELIMITER|Input], Head, Tail) ->
decode(Input, Head ++ [?DELIMITER|Tail], []);
decode([C|Input], Head, Tail) ->
decode(Input, Head, Tail ++ [C]).
decode_whileloop([], Output, _) -> Output;
decode_whileloop(Input, Output, State=#decode{n = N, i = I}) ->
{Input2, State2=#decode{i = I2}} = decode_forloop(Input, State#decode{k = ?BASE, w = 1}),
X = 1 + length(Output),
N2 = N + I2 div X,
I3 = I2 rem X,
{Head, Tail} = lists:split(I3, Output),
decode_whileloop(Input2, Head ++ [N2] ++ Tail, State2#decode{n = N2, i = I3 + 1, bias = adapt(I2 - I, X, I == 0)}).
decode_forloop([C|Input], State=#decode{bias = Bias, i = I, k = K, w = W}) ->
D = decode_digit(C),
I2 = I + D * W,
case threshold(K, Bias) of
T when D < T -> {Input, State#decode{i = I2}};
T -> decode_forloop(Input, State#decode{i = I2, k = K + ?BASE, w = W * (?BASE - T)})
end.
threshold(K, Bias) when K =< Bias + ?TMIN -> ?TMIN;
threshold(K, Bias) when K >= Bias + ?TMAX -> ?TMAX;
threshold(K, Bias) -> K - Bias.
decode_digit(N) when N >= $0 andalso N =< $9 -> N - 22;
decode_digit(N) -> N - 22 - 75.
adapt(Delta, Numpoints, Firsttime) ->
Delta2 = case Firsttime of true -> Delta div ?DAMP; false -> Delta bsr 1 end,
adapt_whileloop(Delta2 + (Delta2 div Numpoints), 0).
adapt_whileloop(Delta, K) ->
case Delta > (((?BASE - ?TMIN) * ?TMAX) bsr 1) of
true ->
adapt_whileloop(Delta div (?BASE - ?TMIN), K + ?BASE);
false ->
K + (((?BASE - ?TMIN + 1) * Delta) div (Delta + ?SKEW))
end.