Current section
Files
Jump to
Current section
Files
src/p1_queue.erl
%%%-------------------------------------------------------------------
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
%%% @copyright (C) 2017-2019 Evgeny Khramtsov
%%% @doc
%%%
%%% @end
%%% Created : 8 Mar 2017 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%-------------------------------------------------------------------
-module(p1_queue).
%% API
-export([new/0, new/1, new/2, is_queue/1, len/1, is_empty/1, in/2, out/1,
peek/1, drop/1, from_list/1, from_list/2, from_list/3,
to_list/1, clear/1, foreach/2, foldl/3, dropwhile/2, type/1,
format_error/1, ram_to_file/1, file_to_ram/1, get_limit/1,
set_limit/2]).
-export([start/1, stop/0]).
-type limit() :: non_neg_integer() | unlimited.
-type rqueue() :: {queue:queue(), non_neg_integer(), limit()}.
-type fqueue() :: p1_file_queue:queue().
-type queue() :: rqueue() | fqueue().
-type queue_type() :: ram | file.
-type error_reason() :: p1_file_queue:error_reason().
-export_type([queue/0, queue_type/0, error_reason/0]).
%%%===================================================================
%%% API
%%%===================================================================
-spec start(file:filename()) -> ok | {error, any()}.
start(Dir) ->
application:ensure_all_started(p1_utils),
case p1_file_queue:start(Dir) of
{ok, _} -> ok;
{error, {already_started, _}} -> ok;
Err -> Err
end.
-spec stop() -> ok | {error, any()}.
stop() ->
p1_file_queue:stop().
-spec new() -> rqueue().
new() ->
new(ram).
-spec new(ram) -> rqueue();
(file) -> fqueue().
new(Type) ->
new(Type, unlimited).
-spec new(ram, limit()) -> rqueue();
(file, limit()) -> fqueue().
new(ram, Limit) ->
{queue:new(), 0, Limit};
new(file, Limit) ->
p1_file_queue:new(Limit).
-spec type(queue()) -> ram | {file, file:filename()}.
type({_, _, _}) ->
ram;
type(Q) ->
{file, p1_file_queue:path(Q)}.
-spec is_queue(any()) -> boolean().
is_queue({Q, Len, _}) when is_integer(Len), Len >= 0 ->
queue:is_queue(Q);
is_queue(Q) ->
p1_file_queue:is_queue(Q).
-spec len(queue()) -> non_neg_integer().
len({_, Len, _}) ->
Len;
len(Q) ->
p1_file_queue:len(Q).
-spec is_empty(queue()) -> boolean().
is_empty({_, Len, _}) ->
Len == 0;
is_empty(Q) ->
p1_file_queue:is_empty(Q).
-spec get_limit(queue()) -> limit().
get_limit({_, _, Limit}) ->
Limit;
get_limit(Q) ->
p1_file_queue:get_limit(Q).
-spec set_limit(rqueue(), limit()) -> rqueue();
(fqueue(), limit()) -> fqueue().
set_limit({Q, Len, _}, Limit) ->
{Q, Len, Limit};
set_limit(Q, Limit) ->
p1_file_queue:set_limit(Q, Limit).
-spec in(term(), rqueue()) -> rqueue();
(term(), fqueue()) -> fqueue().
in(Item, {Q, Len, Limit}) ->
if Len < Limit ->
{queue:in(Item, Q), Len+1, Limit};
true ->
erlang:error(full)
end;
in(Item, Q) ->
p1_file_queue:in(Item, Q).
-spec out(rqueue()) -> {{value, term()}, rqueue()} | {empty, rqueue()};
(fqueue()) -> {{value, term()}, fqueue()} | {empty, fqueue()}.
out({Q, 0, Limit}) ->
{empty, {Q, 0, Limit}};
out({Q, Len, Limit}) ->
{{value, Item}, Q1} = queue:out(Q),
{{value, Item}, {Q1, Len-1, Limit}};
out(Q) ->
p1_file_queue:out(Q).
-spec peek(queue()) -> empty | {value, term()}.
peek({Q, _, _}) ->
queue:peek(Q);
peek(Q) ->
p1_file_queue:peek(Q).
-spec drop(rqueue()) -> rqueue();
(fqueue()) -> fqueue().
drop({Q, Len, Limit}) ->
{queue:drop(Q), Len-1, Limit};
drop(Q) ->
p1_file_queue:drop(Q).
-spec from_list(list()) -> rqueue().
from_list(L) ->
from_list(L, ram, unlimited).
-spec from_list(list(), ram) -> rqueue();
(list(), file) -> fqueue().
from_list(L, Type) ->
from_list(L, Type, unlimited).
-spec from_list(list(), ram, limit()) -> rqueue();
(list(), file, limit()) -> fqueue().
from_list(L, ram, Limit) ->
Len = length(L),
if Len =< Limit ->
{queue:from_list(L), Len, Limit};
true ->
erlang:error(full)
end;
from_list(L, file, Limit) ->
p1_file_queue:from_list(L, Limit).
-spec to_list(queue()) -> list().
to_list({Q, _, _}) ->
queue:to_list(Q);
to_list(Q) ->
p1_file_queue:to_list(Q).
-spec foreach(fun((term()) -> term()), fqueue()) -> ok.
foreach(F, {Q, Len, Limit}) ->
case queue:out(Q) of
{{value, Item}, Q1} ->
F(Item),
foreach(F, {Q1, Len-1, Limit});
{empty, _} ->
ok
end;
foreach(F, Q) ->
p1_file_queue:foreach(F, Q).
-spec foldl(fun((term(), T) -> T), T, queue()) -> T.
foldl(F, Acc, {Q, Len, Limit}) ->
case queue:out(Q) of
{{value, Item}, Q1} ->
Acc1 = F(Item, Acc),
foldl(F, Acc1, {Q1, Len-1, Limit});
{empty, _} ->
Acc
end;
foldl(F, Acc, Q) ->
p1_file_queue:foldl(F, Acc, Q).
-spec dropwhile(fun((term()) -> boolean()), rqueue()) -> rqueue();
(fun((term()) -> boolean()), fqueue()) -> fqueue().
dropwhile(_, {_, 0, _} = Q) ->
Q;
dropwhile(F, {Q, Len, Limit}) ->
{value, Item} = queue:peek(Q),
case F(Item) of
true ->
dropwhile(F, {queue:drop(Q), Len-1, Limit});
_ ->
{Q, Len, Limit}
end;
dropwhile(F, Q) ->
p1_file_queue:dropwhile(F, Q).
-spec clear(rqueue()) -> rqueue();
(fqueue()) -> fqueue().
clear({_, _, Limit}) ->
{queue:new(), 0, Limit};
clear(Q) ->
p1_file_queue:clear(Q).
-spec ram_to_file(queue()) -> fqueue().
ram_to_file({_, _, Limit} = Q) ->
foldl(fun p1_file_queue:in/2, new(file, Limit), Q);
ram_to_file(Q) ->
Q.
-spec file_to_ram(queue()) -> rqueue().
file_to_ram({_, _, _} = Q) ->
Q;
file_to_ram(Q) ->
Limit = p1_file_queue:get_limit(Q),
p1_file_queue:foldl(fun in/2, new(ram, Limit), Q).
-spec format_error(error_reason()) -> string().
format_error(Reason) ->
p1_file_queue:format_error(Reason).
%%%===================================================================
%%% Internal functions
%%%===================================================================