aboutsummaryrefslogtreecommitdiff
path: root/src/misc.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/misc.erl')
-rw-r--r--src/misc.erl135
1 files changed, 126 insertions, 9 deletions
diff --git a/src/misc.erl b/src/misc.erl
index 4f683a431..158c11455 100644
--- a/src/misc.erl
+++ b/src/misc.erl
@@ -39,7 +39,8 @@
css_dir/0, img_dir/0, js_dir/0, msgs_dir/0, sql_dir/0, lua_dir/0,
read_css/1, read_img/1, read_js/1, read_lua/1, try_url/1,
intersection/2, format_val/1, cancel_timer/1, unique_timestamp/0,
- is_mucsub_message/1, best_match/2]).
+ is_mucsub_message/1, best_match/2, pmap/2, peach/2, format_exception/4,
+ parse_ip_mask/1, match_ip_mask/3]).
%% Deprecated functions
-export([decode_base64/1, encode_base64/1]).
@@ -50,6 +51,8 @@
-include("xmpp.hrl").
-include_lib("kernel/include/file.hrl").
+-type distance_cache() :: #{{string(), string()} => non_neg_integer()}.
+
%%%===================================================================
%%% API
%%%===================================================================
@@ -206,10 +209,10 @@ hex_to_base64(Hex) ->
url_encode(A) ->
url_encode(A, <<>>).
--spec expand_keyword(binary(), binary(), binary()) -> binary().
+-spec expand_keyword(iodata(), iodata(), iodata()) -> binary().
expand_keyword(Keyword, Input, Replacement) ->
- Parts = binary:split(Input, Keyword, [global]),
- str:join(Parts, Replacement).
+ re:replace(Input, Keyword, Replacement,
+ [{return, binary}, global]).
binary_to_atom(Bin) ->
erlang:binary_to_atom(Bin, utf8).
@@ -412,7 +415,7 @@ format_val(Term) ->
_ -> [io_lib:nl(), S]
end.
--spec cancel_timer(reference()) -> ok.
+-spec cancel_timer(reference() | undefined) -> ok.
cancel_timer(TRef) when is_reference(TRef) ->
case erlang:cancel_timer(TRef) of
false ->
@@ -425,18 +428,124 @@ cancel_timer(TRef) when is_reference(TRef) ->
cancel_timer(_) ->
ok.
--spec best_match(atom(), [atom()]) -> atom().
+-spec best_match(atom(), [atom()]) -> atom();
+ (binary(), [binary()]) -> binary().
best_match(Pattern, []) ->
Pattern;
best_match(Pattern, Opts) ->
- String = atom_to_list(Pattern),
+ F = if is_atom(Pattern) -> fun atom_to_list/1;
+ is_binary(Pattern) -> fun binary_to_list/1
+ end,
+ String = F(Pattern),
{Ds, _} = lists:mapfoldl(
fun(Opt, Cache) ->
- {Distance, Cache1} = ld(String, atom_to_list(Opt), Cache),
+ {Distance, Cache1} = ld(String, F(Opt), Cache),
{{Distance, Opt}, Cache1}
end, #{}, Opts),
element(2, lists:min(Ds)).
+-spec pmap(fun((T1) -> T2), [T1]) -> [T2].
+pmap(Fun, [_,_|_] = List) ->
+ case erlang:system_info(logical_processors) of
+ 1 -> lists:map(Fun, List);
+ _ ->
+ Self = self(),
+ lists:map(
+ fun({Pid, Ref}) ->
+ receive
+ {Pid, Ret} ->
+ receive
+ {'DOWN', Ref, _, _, _} ->
+ Ret
+ end;
+ {'DOWN', Ref, _, _, Reason} ->
+ exit(Reason)
+ end
+ end, [spawn_monitor(
+ fun() -> Self ! {self(), Fun(X)} end)
+ || X <- List])
+ end;
+pmap(Fun, List) ->
+ lists:map(Fun, List).
+
+-spec peach(fun((T) -> any()), [T]) -> ok.
+peach(Fun, [_,_|_] = List) ->
+ case erlang:system_info(logical_processors) of
+ 1 -> lists:foreach(Fun, List);
+ _ ->
+ Self = self(),
+ lists:foreach(
+ fun({Pid, Ref}) ->
+ receive
+ Pid ->
+ receive
+ {'DOWN', Ref, _, _, _} ->
+ ok
+ end;
+ {'DOWN', Ref, _, _, Reason} ->
+ exit(Reason)
+ end
+ end, [spawn_monitor(
+ fun() -> Fun(X), Self ! self() end)
+ || X <- List])
+ end;
+peach(Fun, List) ->
+ lists:foreach(Fun, List).
+
+-ifdef(HAVE_ERL_ERROR).
+format_exception(Level, Class, Reason, Stacktrace) ->
+ erl_error:format_exception(
+ Level, Class, Reason, Stacktrace,
+ fun(_M, _F, _A) -> false end,
+ fun(Term, I) ->
+ io_lib:print(Term, I, 80, -1)
+ end).
+-else.
+format_exception(Level, Class, Reason, Stacktrace) ->
+ lib:format_exception(
+ Level, Class, Reason, Stacktrace,
+ fun(_M, _F, _A) -> false end,
+ fun(Term, I) ->
+ io_lib:print(Term, I, 80, -1)
+ end).
+-endif.
+
+-spec parse_ip_mask(binary()) -> {ok, {inet:ip4_address(), 0..32}} |
+ {ok, {inet:ip6_address(), 0..128}} |
+ error.
+parse_ip_mask(S) ->
+ case econf:validate(econf:ip_mask(), S) of
+ {ok, _} = Ret -> Ret;
+ _ -> error
+ end.
+
+-spec match_ip_mask(inet:ip_address(), inet:ip_address(), 0..128) -> boolean().
+match_ip_mask({_, _, _, _} = IP, {_, _, _, _} = Net, Mask) ->
+ IPInt = ip_to_integer(IP),
+ NetInt = ip_to_integer(Net),
+ M = bnot (1 bsl (32 - Mask) - 1),
+ IPInt band M =:= NetInt band M;
+match_ip_mask({_, _, _, _, _, _, _, _} = IP,
+ {_, _, _, _, _, _, _, _} = Net, Mask) ->
+ IPInt = ip_to_integer(IP),
+ NetInt = ip_to_integer(Net),
+ M = bnot (1 bsl (128 - Mask) - 1),
+ IPInt band M =:= NetInt band M;
+match_ip_mask({_, _, _, _} = IP,
+ {0, 0, 0, 0, 0, 16#FFFF, _, _} = Net, Mask) ->
+ IPInt = ip_to_integer({0, 0, 0, 0, 0, 16#FFFF, 0, 0}) + ip_to_integer(IP),
+ NetInt = ip_to_integer(Net),
+ M = bnot (1 bsl (128 - Mask) - 1),
+ IPInt band M =:= NetInt band M;
+match_ip_mask({0, 0, 0, 0, 0, 16#FFFF, _, _} = IP,
+ {_, _, _, _} = Net, Mask) ->
+ IPInt = ip_to_integer(IP) - ip_to_integer({0, 0, 0, 0, 0, 16#FFFF, 0, 0}),
+ NetInt = ip_to_integer(Net),
+ M = bnot (1 bsl (32 - Mask) - 1),
+ IPInt band M =:= NetInt band M;
+match_ip_mask(_, _, _) ->
+ false.
+
%%%===================================================================
%%% Internal functions
%%%===================================================================
@@ -499,7 +608,7 @@ unique_timestamp() ->
{MS, S, erlang:unique_integer([positive, monotonic]) rem 1000000}.
%% Levenshtein distance
--spec ld(string(), string(), map()) -> {non_neg_integer(), map()}.
+-spec ld(string(), string(), distance_cache()) -> {non_neg_integer(), distance_cache()}.
ld([] = S, T, Cache) ->
{length(T), maps:put({S, T}, length(T), Cache)};
ld(S, [] = T, Cache) ->
@@ -515,3 +624,11 @@ ld([_|ST] = S, [_|TT] = T, Cache) ->
L = 1 + lists:min([L1, L2, L3]),
{L, maps:put({S, T}, L, C3)}
end.
+
+-spec ip_to_integer(inet:ip_address()) -> non_neg_integer().
+ip_to_integer({IP1, IP2, IP3, IP4}) ->
+ IP1 bsl 8 bor IP2 bsl 8 bor IP3 bsl 8 bor IP4;
+ip_to_integer({IP1, IP2, IP3, IP4, IP5, IP6, IP7,
+ IP8}) ->
+ IP1 bsl 16 bor IP2 bsl 16 bor IP3 bsl 16 bor IP4 bsl 16
+ bor IP5 bsl 16 bor IP6 bsl 16 bor IP7 bsl 16 bor IP8.