Skip to content
This repository has been archived by the owner on Oct 22, 2021. It is now read-only.

Commit

Permalink
add range support for read.
Browse files Browse the repository at this point in the history
  • Loading branch information
Robert Newson committed Feb 17, 2011
1 parent 889e791 commit 3ec18b6
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 12 deletions.
5 changes: 4 additions & 1 deletion src/monic.erl
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

-module(monic).
-export([start/0, stop/0]).
-export([write/2, read/2, read/3, delete/2]).
-export([write/2, read/2, read/3, read/4, delete/2]).
-include("monic.hrl").

start() ->
Expand All @@ -34,6 +34,9 @@ read(Group, #handle{}=Handle) ->
read(Group, #handle{}=Handle, Fun) when is_function(Fun) ->
call(Group, {read, Handle, Fun}).

read(Group, #handle{}=Handle, Ranges, Fun) when is_function(Fun) ->
call(Group, {read, Handle, Ranges, Fun}).

delete(Group, #handle{}=Handle) ->
call(Group, {delete, Handle}).

Expand Down
47 changes: 36 additions & 11 deletions src/monic_file.erl
Original file line number Diff line number Diff line change
Expand Up @@ -129,19 +129,34 @@ handle_call({read, #handle{location=Location,cookie=Cookie}}, _From, #state{fd=F
Else ->
{reply, {error, Else}, State}
end;
handle_call({read, #handle{location=Location,cookie=Cookie}, Fun}, _From, #state{fd=Fd}=State) ->
handle_call({read, Handle, Fun}, From, State) ->
handle_call({read, Handle, [], Fun}, From, State);
handle_call({read, #handle{location=Location,cookie=Cookie}, Ranges, Fun}, _From, #state{fd=Fd}=State) ->
case read_item_header(Fd, Location) of
{ok, #item_header{cookie=Cookie,len=Len}} ->
case stream_out(Fd, Fun, Location + ?ITEM_HEADER_SIZE, Len) of
{ok, CalculatedSha} ->
case read_item_footer(Fd, Location + ?ITEM_HEADER_SIZE + Len) of
{ok, #item_footer{sha=RecordedSha}} ->
case RecordedSha of
CalculatedSha -> Fun({checksum, valid});
_ -> Fun({checksum, invalid})
end,
Fun(eof),
{reply, ok, State};
Ranges1 = case Ranges of
[] -> [{0, Len}];
_ -> Ranges
end,
case validate_ranges(Ranges1, Len) of
ok ->
Start = Location + ?ITEM_HEADER_SIZE,
case lists:foldl(fun({RangeOff, RangeLen}, _) ->
stream_out(Fd, Fun, Start + RangeOff, RangeLen)
end, fail, Ranges1) of
{ok, CalculatedSha} ->
case read_item_footer(Fd, Location + ?ITEM_HEADER_SIZE + Len) of
{ok, #item_footer{sha=RecordedSha}} ->
case {Ranges1, RecordedSha} of
{[{0, Len}], CalculatedSha} -> Fun({checksum, valid});
{[{0, Len}], _OtherSha} -> Fun({checksum, invalid});
_ -> ok
end,
Fun(eof),
{reply, ok, State};
Else ->
{reply, {error, Else}, State}
end;
Else ->
{reply, {error, Else}, State}
end;
Expand Down Expand Up @@ -299,3 +314,13 @@ stream_out(Fd, Fun, Location, Remaining, Sha) when Remaining > 0 ->
Else ->
Else
end.

validate_ranges([], _MaxLen) ->
ok;
validate_ranges([{From, Len}|T], MaxLen) ->
case From >= 0 andalso (From+Len) =< MaxLen of
true ->
validate_ranges(T, MaxLen);
false ->
{error, invalid_range}
end.
2 changes: 2 additions & 0 deletions src/monic_group.erl
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ choose_worker({write, _}, [H|_]) ->
choose_worker({read, #handle{uuid=UUID}}, Workers) ->
choose_worker_by_uuid(UUID, Workers);
choose_worker({read, #handle{uuid=UUID}, _}, Workers) ->
choose_worker_by_uuid(UUID, Workers);
choose_worker({read, #handle{uuid=UUID}, _, _}, Workers) ->
choose_worker_by_uuid(UUID, Workers).

choose_worker_by_uuid(UUID, Workers) ->
Expand Down
13 changes: 13 additions & 0 deletions test/monic_test.erl
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ all_test_() ->
{timeout, 30, fun() -> write_bin() end},
{timeout, 30, fun() -> write_fun() end},
{timeout, 30, fun() -> read_fun() end},
{timeout, 30, fun() -> range_read() end},
{timeout, 30, fun() -> unique_cookies() end},
{timeout, 30, fun() -> unforgeable_cookie() end},
{timeout, 30, fun() -> balanced_writers() end}
Expand Down Expand Up @@ -63,6 +64,18 @@ read_fun() ->
put(last, R) end,
ok = monic:read("foo", Handle, Fun).

range_read() ->
{ok, Handle} = monic:write("foo", <<"foobar">>),
Fun = fun(R) ->
case get(last) of
undefined ->
?assertEqual({ok, <<"ooba">>}, R);
{ok, <<"ooba">>} ->
?assertEqual(eof, R)
end,
put(last, R) end,
ok = monic:read("foo", Handle, [{1, 4}], Fun).

unique_cookies() ->
Bin = <<"hello this is a quick test">>,
{ok, Handle1} = monic:write("foo", Bin),
Expand Down

0 comments on commit 3ec18b6

Please sign in to comment.