Last active
August 2, 2017 00:58
-
-
Save jadeallenx/06dd3fa9c3008db490df25e8d30fffc7 to your computer and use it in GitHub Desktop.
ringbuffer interface using a normal Erlang list
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-module(ringbuffer). | |
-ifdef(TEST). | |
-include_lib("eunit/include/eunit.hrl"). | |
-compile([export_all]). | |
-else. | |
-export([ | |
new/0, | |
new/1, | |
get_head/1, | |
add/2, | |
get_all/1 | |
]). | |
-endif. | |
-define(DEFAULT_SIZE, 50). | |
-record(ringbuffer, { | |
depth = 0 :: non_neg_integer(), | |
size = 1 :: pos_integer(), | |
data = [] :: list() | |
}). | |
-type ringbuffer() :: #ringbuffer{}. | |
-spec new() -> ringbuffer(). | |
%% @doc Create a new ringbuffer with a capacity for 50 values. | |
new() -> | |
new(?DEFAULT_SIZE). | |
-spec new( Size :: pos_integer() ) -> ringbuffer() | badarg. | |
%% @doc Create a new ringbuffer with the specified size. A badarg error will be | |
%% returned for non-integers or integers less than 1. | |
new(Size) when Size < 1 -> error(badarg); | |
new(Size) when is_integer(Size) -> #ringbuffer{ size = Size }; | |
new(_Other) -> error(badarg). | |
-spec get_head( Buffer :: ringbuffer() ) -> { Value :: term() | '$empty', NewBuffer :: ringbuffer() }. | |
%% @doc Return the first value in the buffer along with the updated buffer data | |
%% structure. The atom `$empty' will be returned if the buffer is empty. | |
get_head(R = #ringbuffer{ data = [] }) -> {'$empty', R}; | |
get_head(R = #ringbuffer{ depth = 0 }) -> {'$empty', R}; | |
get_head(#ringbuffer{ size = S, depth = 1, data = [ H | _T ] }) -> | |
{H, #ringbuffer{ size = S }}; | |
get_head(R = #ringbuffer{ depth = D, data = [ H | T ] }) -> | |
{H, R#ringbuffer{ depth = D - 1, data = T }}. | |
-spec add( Element :: term(), Buffer :: ringbuffer() ) -> NewBuffer :: ringbuffer(). | |
%% @doc Add a new element to the buffer. Items are inserted at the front of | |
%% the buffer, so it can be considered a "LIFO" (last in, first out) style | |
%% stack. | |
add(Element, R = #ringbuffer{ data = L, size = S, depth = D }) when D == S -> | |
R#ringbuffer{ data = [ Element | L ] }; | |
add(Element, R = #ringbuffer{ data = L, size = S, depth = D }) when D < S -> | |
R#ringbuffer{ depth = D + 1, data = [ Element | L ] }. | |
-spec get_all( Buffer :: ringbuffer() ) -> { [term()], NewBuffer :: ringbuffer() }. | |
%% Return the entire contents of the buffer. This function | |
%% <strong>always</strong> returns a list, which may be empty. | |
get_all(#ringbuffer{ size = S, depth = D, data = L }) when length(L) > D -> | |
{L1, _Ntail} = lists:split(D, L), | |
{L1, #ringbuffer{ size = S }}; | |
get_all(#ringbuffer{ size = S, data = L }) -> | |
{L, #ringbuffer{ size = S }}. | |
-ifdef(TEST). | |
new_test_() -> | |
[ | |
?_assertEqual({ringbuffer, 0, 50, []}, new()), | |
?_assertError(badarg, new(0)), | |
?_assertError(badarg, new(-1)), | |
?_assertError(badarg, new(2.5)), | |
?_assertError(badarg, new("foo")), | |
?_assertError(badarg, new(abc)), | |
?_assertEqual({ringbuffer, 0, 42, []}, new(42)) | |
]. | |
add_test_() -> | |
Empty = new(1), | |
Pop = #ringbuffer{ size = 1, depth = 1, data = [a] }, | |
Pop1 = #ringbuffer{ size = 1, depth = 1, data = [b, a] }, | |
[ | |
?_assertEqual(#ringbuffer{ size = 1, depth = 1, data = [a] }, add(a, Empty)), | |
?_assertEqual(#ringbuffer{ size = 1, depth = 1, data = [b, a] }, add(b, Pop)), | |
?_assertEqual(#ringbuffer{ size = 1, depth = 1, data = [c, b, a] }, add(c, Pop1)) | |
]. | |
get_head_test_() -> | |
P = #ringbuffer{ size = 1, depth = 1, data = [b, a] }, | |
C = #ringbuffer{ size = 10, depth = 2, data = [b, a] }, | |
[ | |
?_assertEqual({b, new(1)}, get_head(P)), | |
?_assertEqual({b, #ringbuffer{ size = 10, depth = 1, data = [a]}}, get_head(C)) | |
]. | |
get_all_test_() -> | |
P = #ringbuffer{ size = 1, depth = 1, data = [b, a] }, | |
C = #ringbuffer{ size = 10, depth = 2, data = [b, a] }, | |
[ | |
?_assertEqual({[b], new(1)}, get_all(P)), | |
?_assertEqual({[b, a], new(10)}, get_all(C)) | |
]. | |
-endif. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment