summaryrefslogtreecommitdiff
path: root/src/index.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/index.erl')
-rw-r--r--src/index.erl81
1 files changed, 81 insertions, 0 deletions
diff --git a/src/index.erl b/src/index.erl
new file mode 100644
index 0000000..5382485
--- /dev/null
+++ b/src/index.erl
@@ -0,0 +1,81 @@
+%%
+%% Copyright (c) 2014 Kungliga Tekniska Högskolan
+%% (KTH Royal Institute of Technology, Stockholm, Sweden).
+%%
+
+%% Implements an interface to a file pair (basename and basename.chksum)
+%% that stores an ordered list of fixed-size entries. Entries can be
+%% added at the end and are retrieved by index. The list can also be
+%% truncated.
+
+-module(index).
+-export([get/2, add/3, addlast/2, truncate/2]).
+
+-define(ENTRYSIZE, 32).
+-define(ENTRYSIZEINFILE, (?ENTRYSIZE*2+1)).
+
+-spec add(string(), integer() | last, binary()) -> ok.
+add(Basepath, Index, Entry) when is_binary(Entry), size(Entry) == ?ENTRYSIZE ->
+ case file:open(Basepath, [read, write, binary]) of
+ {ok, File} ->
+ {ok, Position} = file:position(File, eof),
+ case Index of
+ last when Position rem ?ENTRYSIZEINFILE == 0 ->
+ ok;
+ Index when is_integer(Index),
+ Index * ?ENTRYSIZEINFILE == Position ->
+ ok
+ end,
+ EntryText = hex:bin_to_hexstr(Entry) ++ "\n",
+ ok = file:write(File, EntryText),
+ file:close(File);
+ {error, Error} ->
+ util:exit_with_error(Error, writefile,
+ "Error opening file for writing")
+ end.
+
+truncate(Basepath, Index) ->
+ case file:open(Basepath, [read, write, binary]) of
+ {ok, File} ->
+ {ok, _Position} = file:position(File, Index * ?ENTRYSIZEINFILE),
+ ok = file:truncate(File),
+ file:close(File);
+ {error, Error} ->
+ util:exit_with_error(Error, writefile,
+ "Error opening file for writing")
+ end.
+
+
+-spec addlast(string(), integer()) -> ok.
+addlast(Basepath, Entry) ->
+ add(Basepath, last, Entry).
+
+decodedata(EntryText) when length(EntryText) == ?ENTRYSIZEINFILE ->
+ case [lists:last(EntryText)] of
+ "\n" ->
+ hex:hexstr_to_bin(lists:droplast(EntryText));
+ _ ->
+ util:exit_with_error(badformat, readindex,
+ "Index line not ending with linefeed")
+ end.
+
+-spec get(string(), integer()) -> binary().
+get(Basepath, Index) ->
+ case file:open(Basepath, [read, binary]) of
+ {ok, File} ->
+ {ok, Filesize} = file:position(File, eof),
+ if
+ Index * ?ENTRYSIZEINFILE + ?ENTRYSIZEINFILE =< Filesize ->
+ {ok, _Position} = file:position(File,
+ Index * ?ENTRYSIZEINFILE),
+ {ok, EntryText} = file:read(File, ?ENTRYSIZEINFILE),
+ Entry = decodedata(binary_to_list(EntryText)),
+ file:close(File),
+ Entry;
+ true ->
+ noentry
+ end;
+ {error, Error} ->
+ util:exit_with_error(Error, readfile,
+ "Error opening file for reading")
+ end.