Packages
ra
1.1.2
3.1.9
3.1.8
3.1.7
3.1.6
3.1.5
3.1.4
3.1.3
3.1.2
3.1.1
3.1.0
3.0.2
3.0.1
3.0.0
3.0.0-beta.1
2.17.3
2.17.2
2.17.1
2.17.0
2.16.13
2.16.12
2.16.11
2.16.10
2.16.9
2.16.8
2.16.7
2.16.6
2.16.5
2.16.4
2.16.3
2.16.2
2.16.1
2.16.0
2.16.0-pre.12
2.16.0-pre.11
2.16.0-pre.10
2.16.0-pre.9
2.16.0-pre.8
2.16.0-pre.7
2.16.0-pre.6
2.16.0-pre.5
2.16.0-pre.4
2.16.0-pre.3
2.16.0-pre.2
2.16.0-pre.1
2.15.4
2.15.3
2.15.2
2.15.1
2.15.0
2.14.0
2.13.6
2.13.5
2.13.4
2.13.3
2.13.2
2.13.1
2.13.0
2.13.0-pre.1
2.12.0
2.11.0
2.11.0-pre.1
2.10.2-pre.2
2.10.2-pre.1
2.10.1
2.10.0
2.10.0-pre.3
2.10.0-pre.2
2.10.0-pre.1
2.9.10-pre.1
2.9.1
2.9.1-pre.2
2.9.1-pre.1
2.9.0
2.8.0
retired
2.7.3
2.7.2
2.7.1
2.7.0
2.7.0-pre.3
2.7.0-pre.2
2.7.0-pre.1
2.6.3
2.6.2
2.6.1
2.6.0-pre.1
2.5.1
2.5.1-pre.1
2.5.0
2.4.9
2.4.8
2.4.7
2.4.6
2.4.5
2.4.4
2.4.3
2.4.2
retired
2.4.1
2.4.0
2.3.0
2.2.0
2.1.0
2.0.13
2.0.12
2.0.11
2.0.10
2.0.9
2.0.8
2.0.7
2.0.6
2.0.5
2.0.4
2.0.3
2.0.2
2.0.1
2.0.0
1.1.9
1.1.8
1.1.7
1.1.6
1.1.5
1.1.4
1.1.3
1.1.2
1.1.1
1.1.0
1.0.8
1.0.7
1.0.6
1.0.5
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
0.9.6
0.9.5
0.9.4
0.9.2
0.3.3
retired
0.3.2
retired
0.3.1
retired
Raft library
Current section
Files
Jump to
Current section
Files
src/ra_lib.erl
%% @hidden
-module(ra_lib).
-export([
ceiling/1,
default/2,
lazy_default/2,
dump/1,
dump/2,
id/1,
% maybe
iter_maybe/2,
% cohercion
to_list/1,
to_binary/1,
to_string/1,
to_atom/1,
ra_server_id_to_local_name/1,
ra_server_id_node/1,
update_element/3,
zpad_hex/1,
zpad_filename/3,
zpad_filename_incr/1,
zpad_extract_num/1,
recursive_delete/1,
make_uid/0,
make_uid/1,
make_dir/1,
derive_safe_string/2,
validate_base64uri/1,
partition_parallel/2,
partition_parallel/3,
retry/2,
retry/3,
write_file/2,
lists_chunk/2
]).
ceiling(X) when X < 0 ->
trunc(X);
ceiling(X) ->
T = trunc(X),
case X-T =:= 0 of
true -> T;
false -> T + 1
end.
default(undefined, Def) ->
Def;
default(Value, _Def) ->
Value.
-spec lazy_default(undefined | term(), fun (() -> term())) -> term().
lazy_default(undefined, DefGen) ->
DefGen();
lazy_default(Value, _DefGen) ->
Value.
dump(Term) ->
dump("Dump", Term).
dump(Prefix, Term) ->
io:format("~p: ~p~n", [Prefix, Term]),
Term.
id(X) -> X.
-spec iter_maybe(undefined | term(), fun()) -> ok.
iter_maybe(undefined, _F) ->
ok;
iter_maybe(M, F) ->
_ = F(M),
ok.
-spec to_list(atom() | binary() | list() | integer()) -> list().
to_list(A) when is_atom(A) ->
atom_to_list(A);
to_list(B) when is_binary(B) ->
binary_to_list(B);
to_list(I) when is_integer(I) ->
integer_to_list(I);
to_list(L) when is_list(L) ->
L.
-spec to_binary(atom() | binary() | list() | integer()) -> binary().
to_binary(B) when is_binary(B) ->
B;
to_binary(A) when is_atom(A) ->
atom_to_binary(A, utf8);
to_binary(I) when is_integer(I) ->
integer_to_binary(I);
to_binary(L) when is_list(L) ->
list_to_binary(L).
-spec to_string(binary() | string()) -> string().
to_string(B) when is_binary(B) ->
binary_to_list(B);
to_string(L) when is_list(L) ->
L.
-spec to_atom(atom() | list() | binary()) -> atom().
to_atom(A) when is_atom(A) ->
A;
to_atom(B) when is_binary(B) ->
list_to_atom(binary_to_list(B));
to_atom(L) when is_list(L) ->
list_to_atom(L).
ra_server_id_to_local_name({Name, _}) -> Name;
ra_server_id_to_local_name(Name) when is_atom(Name) -> Name.
ra_server_id_node({_Name, Node}) -> Node;
ra_server_id_node(Name) when is_atom(Name) -> node().
update_element(Index, T, Update) when is_tuple(T) ->
setelement(Index, T, Update(element(Index, T))).
zpad_hex(Num) ->
lists:flatten(io_lib:format("~16.16.0B", [Num])).
zpad_filename("", Ext, Num) ->
lists:flatten(io_lib:format("~8..0B.~s", [Num, Ext]));
zpad_filename(Prefix, Ext, Num) ->
lists:flatten(io_lib:format("~s_~8..0B.~s", [Prefix, Num, Ext])).
zpad_filename_incr(Fn) ->
case re:run(Fn, "(.*)([0-9]{8})(.*)", [{capture, all_but_first, list}]) of
{match, [Prefix, NumStr, Ext]} ->
Num = list_to_integer(NumStr),
lists:flatten(io_lib:format("~s~8..0B~s", [Prefix, Num+1, Ext]));
_ ->
undefined
end.
zpad_extract_num(Fn) ->
{match, [_, NumStr, _]} = re:run(Fn, "(.*)([0-9]{8})(.*)",
[{capture, all_but_first, list}]),
list_to_integer(NumStr).
recursive_delete(Dir) ->
case filelib:is_dir(Dir) of
true ->
case file:list_dir(Dir) of
{ok, Files} ->
Fun =
fun(F) -> recursive_delete(filename:join([Dir, F])) end,
lists:foreach(Fun, Files),
delete(Dir, directory);
{error, enoent} ->
ok;
{error, Reason} ->
Text = file:format_error(Reason),
throw_error("delete file ~ts: ~ts\n", [Dir, Text])
end;
false ->
delete(Dir, regular)
end.
delete(File, Type) ->
case do_delete(File, Type) of
ok ->
ok;
{error, enoent} ->
ok;
{error, Reason} ->
Text = file:format_error(Reason),
throw_error("delete file ~ts: ~ts\n", [File, Text])
end.
do_delete(File, regular) ->
file:delete(File);
do_delete(Dir, directory) ->
file:del_dir(Dir).
-spec throw_error(string(), list()) -> no_return().
throw_error(Format, Args) ->
throw({error, lists:flatten(io_lib:format(Format, Args))}).
%% "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
-define(GENERATED_UID_CHARS,
{65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78,
79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 48, 49,
50, 51, 52, 53, 54, 55, 56, 57}).
-define(BASE64_URI_CHARS,
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789_-=").
-define(UID_CHARS, "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890").
-define(UID_LENGTH, 12).
-spec make_uid() -> binary().
make_uid() ->
make_uid(<<>>).
-spec make_uid(atom() | binary() | string()) -> binary().
make_uid(Prefix0) ->
ChrsSize = size(?GENERATED_UID_CHARS),
F = fun(_, R) ->
[element(rand:uniform(ChrsSize), ?GENERATED_UID_CHARS) | R]
end,
Prefix = to_binary(Prefix0),
B = list_to_binary(lists:foldl(F, "", lists:seq(1, ?UID_LENGTH))),
<<Prefix/binary, B/binary>>.
-spec make_dir(file:name_all()) ->
ok | {error, file:posix() | badarg}.
make_dir(Dir) ->
handle_ensure_dir(filelib:ensure_dir(Dir), Dir).
handle_ensure_dir(ok, Dir) ->
handle_make_dir(file:make_dir(Dir));
handle_ensure_dir(Error, _Dir) ->
Error.
handle_make_dir(ok) ->
ok;
handle_make_dir({error, eexist}) ->
ok;
handle_make_dir(Error) ->
Error.
-spec validate_base64uri(string()) -> boolean().
validate_base64uri(Str) ->
catch
begin
[begin
case lists:member(C, ?BASE64_URI_CHARS) of
true -> ok;
false -> throw(false)
end
end || C <- string:to_graphemes(Str)],
string:is_empty(Str) == false
end.
derive_safe_string(S, Num) ->
F = fun Take([], Acc) ->
string:reverse(Acc);
Take([G | Rem], Acc) ->
case lists:member(G, ?BASE64_URI_CHARS) of
true ->
Take(string:next_grapheme(Rem), [G | Acc]);
false ->
Take(string:next_grapheme(Rem), Acc)
end
end,
string:slice(F(string:next_grapheme(S), []), 0, Num).
partition_parallel(F, Es) ->
partition_parallel(F, Es, 60000).
partition_parallel(F, Es, Timeout) ->
Parent = self(),
Running = [{spawn_monitor(fun() -> Parent ! {self(), F(E)} end), E}
|| E <- Es],
collect(Running, {[], []}, Timeout).
collect([], Acc, _Timeout) ->
Acc;
collect([{{Pid, MRef}, E} | Next], {Left, Right}, Timeout) ->
receive
{Pid, true} ->
erlang:demonitor(MRef, [flush]),
collect(Next, {[E | Left], Right}, Timeout);
{Pid, false} ->
erlang:demonitor(MRef, [flush]),
collect(Next, {Left, [E | Right]}, Timeout);
{'DOWN', MRef, process, Pid, _Reason} ->
collect(Next, {Left, [E | Right]}, Timeout)
after Timeout ->
exit(partition_parallel_timeout)
end.
retry(Func, Attempts) ->
retry(Func, Attempts, 5000).
retry(_Func, 0, _Sleep) ->
exhausted;
retry(Func, Attempt, Sleep) ->
% do not retry immediately
timer:sleep(Sleep),
case catch Func() of
ok ->
ok;
true ->
ok;
_ ->
retry(Func, Attempt - 1)
end.
write_file(Name, IOData) ->
case file:open(Name, [binary, write, raw]) of
{ok, Fd} ->
case file:write(Fd, IOData) of
ok ->
case file:sync(Fd) of
ok ->
file:close(Fd);
Err ->
_ = file:close(Fd),
Err
end;
Err ->
_ = file:close(Fd),
Err
end;
Err ->
Err
end.
lists_chunk(0, List) ->
error(invalid_size, [0, List]);
lists_chunk(Size, List) ->
lists_chunk(Size, List, []).
lists_chunk(_Size, [], Acc) ->
lists:reverse(Acc);
lists_chunk(Size, List, Acc) when length(List) < Size ->
lists:reverse([List | Acc]);
lists_chunk(Size, List, Acc) ->
{L, Rem} = lists_take(Size, List, []),
lists_chunk(Size, Rem, [L | Acc]).
lists_take(0, List, Acc) ->
{lists:reverse(Acc), List};
lists_take(_N, [], Acc) ->
{lists:reverse(Acc), []};
lists_take(N, [H | T], Acc) ->
lists_take(N-1, T, [H | Acc]).
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
lists_chink_test() ->
?assertError(invalid_size, lists_chunk(0, [a])),
?assertMatch([], lists_chunk(2, [])),
?assertMatch([[a]], lists_chunk(2, [a])),
?assertMatch([[a, b]], lists_chunk(2, [a, b])),
?assertMatch([[a, b], [c]], lists_chunk(2, [a, b, c])),
?assertMatch([[a, b], [c, d]], lists_chunk(2, [a, b, c, d])),
ok.
make_uid_test() ->
U1 = make_uid(),
U2 = make_uid(),
?debugFmt("U1 ~s U2 ~s", [U1, U2]),
?assertNotEqual(U1, U2),
<<"ABCD", _/binary>> = make_uid("ABCD"),
<<"ABCD", _/binary>> = make_uid(<<"ABCD">>),
ok.
zpad_filename_incr_test() ->
Fn = "/lib/blah/prefix_00000001.segment",
Ex = "/lib/blah/prefix_00000002.segment",
Ex = zpad_filename_incr(Fn),
undefined = zpad_filename_incr("0000001"),
ok.
derive_safe_string_test() ->
S = <<"bønana"/utf8>>,
S2 = "bønana",
[] = derive_safe_string(<<"">>, 4),
"bnan" = derive_safe_string(S, 4),
"bnan" = derive_safe_string(S2, 4),
ok.
validate_base64uri_test() ->
false = validate_base64uri(""), %% false
true = validate_base64uri(?BASE64_URI_CHARS),
false = validate_base64uri("asdføasdf"),
false = validate_base64uri("asdf/asdf"),
ok.
zeropad_test() ->
"0000000000000037" = zpad_hex(55),
ok.
-endif.