file_sorter(3)
๐ NAME ๐
file_sorter โ the file sorter module, bestie โจ
๐ DESCRIPTION ๐
okay so this module is literally your girl for sorting terms on files, merging files that are already sorted, and checking whether files are sorted. no cap. ๐ซถ
Here's the vibe: chunks containing binary terms get read from a sequence of files, sorted internally in memory, written out onto temporary files, and then those temp files get merged so you end up with one sorted file as the output. ๐ Merging is provided as an optimization โ it's faster when the files are already sorted โ but it always works to sort instead of merge, so don't stress. ๐
On a file, a term is represented by a header and a binary. Two options decide the format of terms on files:
{header, HeaderLength}โHeaderLengthdetermines the number of bytes preceding each binary and containing the length of the binary in bytes. Defaults to4. The order of the header bytes is defined like this: ifBis a binary containing a header only, sizeSizeof the binary is calculated as<<Size:HeaderLength/unit:8>> = B.{format, Format}โ optionFormatdetermines the function that is applied to binaries to create the terms to be sorted. Defaults tobinary_term, which is equivalent tofun binary_to_term/1. Valuebinaryis equivalent tofun(X) -> X end, which means that the binaries are sorted as they are โ this is the fastest format ๐ฅ. IfFormatisterm,io:read/2is called to read terms. In that case, only the default value of optionheaderis allowed. ๐
Option format also determines what is written to the sorted output file: if Format is term, then io:format/3 is called to write each term, otherwise the binary prefixed by a header is written. Notice that the binary written is the same binary that was read; the results of applying function Format are thrown away when the terms have been sorted. And fr fr, reading and writing terms using the io module is much slower than reading and writing binaries. ๐ญ
๐ so, the other options (we love options)
{order, Order}โ the default is to sort terms in ascending order, but that can be changed by valuedescendingor by specifying an ordering functionFun. An ordering function is antisymmetric, transitive, and total.Fun(A, B)is to returntrueifAcomes beforeBin the ordering, otherwisefalse. An example of a typical ordering function is less than or equal to,=</2. Using an ordering function slows down the sort considerably ๐. Functionskeysort,keymergeandkeycheckdo not accept ordering functions.{unique, boolean()}โ when sorting or merging files, only the first of a sequence of terms that compare equal (==) is output if this option is set totrue. Defaults tofalse, which implies that all terms that compare equal are output. When checking files for sortedness, a check that no pair of consecutive terms compares equal is done if this option is set totrue. โจ{tmpdir, TempDirectory}โ the directory where temporary files are put can be chosen explicitly. The default, implied by value"", is to put temporary files in the same directory as the sorted output file. If output is a function (see below), the directory returned byfile:get_cwd()is used instead. The names of temporary files are derived from the Erlang nodename (node/0), the process identifier of the current Erlang emulator (os:getpid()), and a unique integer (erlang:unique_integer([positive])). A typical name isfs_mynode@myhost_1763_4711.17, where17is a sequence number. Existing files are overwritten. Temporary files are deleted unless some uncaughtEXITsignal occurs. ๐{compressed, boolean()}โ temporary files and the output file can be compressed. Defaults tofalse, which implies that written files are not compressed. Regardless of the value of optioncompressed, compressed files can always be read. Notice that reading and writing compressed files are significantly slower than reading and writing uncompressed files. ๐ฎโ๐จ{size, Size}โ by default about512*1024bytes read from files are sorted internally. This option is rarely needed. ๐{no_files, NoFiles}โ by default16files are merged at a time. This option is rarely needed. ๐
๐ฆ plot twist: functions instead of files
As an alternative to sorting files, a function of one argument can be specified as input. When called with argument read, the function is assumed to return either of the following:
end_of_inputor{end_of_input, Value}}when there is no more input (Valueis explained below). ๐ธ{Objects, Fun}, whereObjectsis a list of binaries or terms depending on the format, andFunis a new input function. โจ
Any other value is immediately returned as the value of the current call to sort or keysort. Each input function is called exactly once. If an error occurs, the last function is called with argument close, the reply of which is ignored.
A function of one argument can also be specified as output. The results of sorting or merging the input is collected in a non-empty sequence of variable length lists of binaries or terms depending on the format. The output function is called with one list at a time, and is assumed to return a new output function. Any other return value is immediately returned as the value of the current call to the sort or merge function. Each output function is called exactly once. When some output function has been applied to all of the results, or an error occurs, the last function is called with argument close, and the reply is returned as the value of the current call to the sort or merge function. ๐
If a function is specified as input and the last input function returns {end_of_input, Value}, the function specified as output is called with argument {value, Value}. This makes it easy to initiate the sequence of output functions with a value calculated by the input functions. iconic design tbh ๐ง
๐ example: sorting terms on a disk log
As an example, consider sorting the terms on a disk log file. A function that reads chunks from the disk log and returns a list of binaries is used as input. The results are collected in a list of terms.
sort(Log) ->
{ok, _} = disk_log:open([{name,Log}, {mode,read_only}]),
Input = input(Log, start),
Output = output([]),
Reply = file_sorter:sort(Input, Output, {format,term}),
ok = disk_log:close(Log),
Reply.
input(Log, Cont) ->
fun(close) ->
ok;
(read) ->
case disk_log:chunk(Log, Cont) of
{error, Reason} ->
{error, Reason};
{Cont2, Terms} ->
{Terms, input(Log, Cont2)};
{Cont2, Terms, _Badbytes} ->
{Terms, input(Log, Cont2)};
eof ->
end_of_input
end
end.
output(L) ->
fun(close) ->
lists:append(lists:reverse(L));
(Terms) ->
output([Terms | L])
end.
For more examples of functions as input and output, see the end of the file_sorter module; the term format is implemented with functions. ๐ซถ
๐ญ the possible values of Reason when an error happens
bad_object,{bad_object, FileName}โ applying the format function failed for some binary, or the key(s) could not be extracted from some term.{bad_term, FileName}โio:read/2failed to read some term.{file_error, FileName, file:posix()}โ for an explanation offile:posix(), seefile.{premature_eof, FileName}โ end-of-file was encountered inside some binary term.
โจ DATA TYPES โจ
file_name() :: file:name().
file_names() :: [file:name()].
format() :: binary_term | term | binary | format_fun().
format_fun() :: fun((binary()) -> term()).
header_length() :: pos_integer().
i_command() :: read | close.
i_reply() ::
end_of_input |
{end_of_input, value()} |
{[object()], infun()} |
input_reply().
infun() :: fun((i_command()) -> i_reply()).
input() :: file_names() | infun().
input_reply() :: term().
key_pos() :: pos_integer() | [pos_integer()].
no_files() :: pos_integer().
o_command() :: {value, value()} | [object()] | close.
o_reply() :: outfun() | output_reply().
object() :: term() | binary().
option() ::
{compressed, boolean()} |
{header, header_length()} |
{format, format()} |
{no_files, no_files()} |
{order, order()} |
{size, size()} |
{tmpdir, tmp_directory()} |
{unique, boolean()}.
options() :: [option()] | option().
order() :: ascending | descending | order_fun().
order_fun() :: fun((term(), term()) -> boolean()).
outfun() :: fun((o_command()) -> o_reply()).
output() :: file_name() | outfun().
output_reply() :: term().
reason() ::
bad_object |
{bad_object, file_name()} |
{bad_term, file_name()} |
{file_error,
file_name(),
file:posix() | badarg | system_limit} |
{premature_eof, file_name()}.
size() :: non_neg_integer().
tmp_directory() :: [] | file:name().
value() :: term().
and that's part 1 bestie!! ๐ซถ part 2 is right there waiting, don't go anywhere โจ
๐ FUNCTIONS ๐
okay bestie, this is the part where we actually do stuff. every single one of these takes your files and either checks them, merges them, or full-on sorts them, and every single one of them has โจopinionsโจ about what happens if things go sideways. read the specs, they're not decorational.
๐ธ check(FileNames, Options) -> Reply ๐ธ
check(FileNames, Options) -> Reply
when
FileNames :: file_names(),
Options :: options(),
Reply :: {ok, [Result]} | {error, reason()},
Result :: {FileName, TermPosition, term()},
FileName :: file_name(),
TermPosition :: pos_integer().
Checks files for sortedness. If a file is not sorted, the first out-of-order element gets handed back to you โ like, the moment something's off, it snitches. and heads up: the first term on a file has position 1, so we're counting from one, not zero, iykyk. ๐ซถ
๐ฆ keycheck(KeyPos, FileNames, Options) -> Reply ๐ฆ
keycheck(KeyPos, FileNames, Options) -> Reply
when
KeyPos :: key_pos(),
FileNames :: file_names(),
Options :: options(),
Reply :: {ok, [Result]} | {error, reason()},
Result :: {FileName, TermPosition, term()},
FileName :: file_name(),
TermPosition :: pos_integer().
Checks files for sortedness. Same energy as check, honestly โ if a file isn't sorted, you get the first out-of-order element back, and the first term on a file has position 1. the difference is you're passing a KeyPos so we're checking by key. ๐
๐ keymerge(KeyPos, FileNames, Output, Options) -> Reply ๐
keymerge(KeyPos, FileNames, Output, Options) -> Reply
when
KeyPos :: key_pos(),
FileNames :: file_names(),
Output :: output(),
Options :: options(),
Reply :: ok | {error, reason()} | output_reply().
Merges tuples on files. Each input file is assumed to be sorted on key(s) โ so like, don't come crying to us if it's not, because that's the deal you made when you called this. ๐
๐ keysort(KeyPos, FileName) -> Reply ๐
keysort(KeyPos, FileName) -> Reply
when
KeyPos :: key_pos(),
FileName :: file_name(),
Reply ::
ok |
{error, reason()} |
input_reply() |
output_reply().
Sorts tuples on files. that's the whole vibe. โจ
๐ keysort(KeyPos, Input, Output, Options) -> Reply ๐
keysort(KeyPos, Input, Output, Options) -> Reply
when
KeyPos :: key_pos(),
Input :: input(),
Output :: output(),
Options :: options(),
Reply ::
ok |
{error, reason()} |
input_reply() |
output_reply().
Sorts tuples on files. The sort happens on the element(s) you named in KeyPos. and here's the cute part: if two tuples compare equal (==) on one element, then the next element according to KeyPos gets compared โ it keeps walking down the list until it can make up its mind. and the sort is stable, periodt. ๐
๐ merge(FileNames, Output, Options) -> Reply ๐
merge(FileNames, Output, Options) -> Reply
when
FileNames :: file_names(),
Output :: output(),
Options :: options(),
Reply :: ok | {error, reason()} | output_reply().
Merges terms on files. Each input file is assumed to be sorted. again: that's on you, bestie, we trust you and we do not double-check. ๐ซถ
๐ธ sort(FileName) -> Reply ๐ธ
sort(FileName) -> Reply
when
FileName :: file_name(),
Reply ::
ok |
{error, reason()} |
input_reply() |
output_reply().
Sorts terms on files. iconic behaviour, truly. โจ
๐ฆ sort(Input, Output, Options) -> Reply ๐ฆ
sort(Input, Output, Options) -> Reply
when
Input :: input(),
Output :: output(),
Options :: options(),
Reply ::
ok |
{error, reason()} |
input_reply() |
output_reply().
Sorts terms on files. yes it's the same one-liner as above, and yes we're leaving it exactly as-is because the facts do not change just because the vibe does. ๐
and that's a wrap on part two, bestie!! ๐ the whole thing came to you courtesy of Ericsson AB, running stdlib 8.0.4, and filed under file_sorter(3) โ go forth and sort responsibly, ily ๐ซถ๐
๐ฆ where this page comes from
- ubuntu stonking: erlang-doc 1:29.0.6+dfsg-1 ยท Apache-2.0
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms