11defmodule CircularBuffer do
22 @ moduledoc """
3- Circular Buffer built around erlang's queue module.
3+ Circular Buffer
44
55 When creating a circular buffer you must specify the max size:
66
77 ```
88 cb = CircularBuffer.new(10)
99 ```
10+
11+ CircularBuffers are implemented as Okasaki queues like Erlang's `:queue`
12+ module, but with additional optimizations thanks to the reduced set
13+ of operations.
1014 """
1115
12- defstruct [ :q , :max_size , :count ]
16+ defstruct [ :a , :b , :max_size , :count ]
1317
1418 alias __MODULE__ , as: CB
1519
1620 @ doc """
1721 Creates a new circular buffer with a given size.
1822 """
1923 def new ( size ) when is_integer ( size ) and size > 0 do
20- % CB { q: :queue . new ( ) , max_size: size , count: 0 }
24+ % CB { a: [ ] , b: [ ] , max_size: size , count: 0 }
2125 end
2226
2327 @ doc """
2428 Inserts a new item into the next location of the circular buffer
2529 """
26- def insert ( % CB { } = cb , item ) do
27- if cb . count < cb . max_size do
28- % { cb | q: :queue . cons ( item , cb . q ) , count: cb . count + 1 }
29- else
30- new_q =
31- cb . q
32- |> :queue . drop_r
33- |> ( fn q -> :queue . cons ( item , q ) end ) . ( )
34-
35- % CB { cb | q: new_q }
30+ def insert ( % CB { b: b } = cb , item ) when b != [ ] do
31+ % CB { cb | a: [ item | cb . a ] , b: tl ( b ) }
32+ end
33+
34+ def insert ( % CB { count: count , max_size: max_size } = cb , item ) when count < max_size do
35+ % CB { cb | a: [ item | cb . a ] , count: cb . count + 1 }
3636 end
37+
38+ def insert ( % CB { b: [ ] } = cb , item ) do
39+ new_b = cb . a |> Enum . reverse ( ) |> tl ( )
40+ % CB { cb | a: [ item ] , b: new_b }
3741 end
3842
3943 @ doc """
4044 Converts a circular buffer to a list. The list is ordered from oldest to newest
4145 elements based on their insertion order.
4246 """
43- def to_list ( % CB { } = cb ) do
44- cb . q
45- |> :queue . reverse
46- |> :queue . to_list
47+ def to_list ( % CB { } = cb ) do
48+ cb . b ++ Enum . reverse ( cb . a )
4749 end
4850
4951 @ doc """
5052 Returns the newest element in the buffer
5153 """
52- def newest ( % CB { } = cb ) do
53- case :queue . peek ( cb . q ) do
54- { _ , val } -> val
55- :empty -> nil
56- end
57- end
54+ def newest ( % CB { a: [ newest | _rest ] } ) , do: newest
55+ def newest ( % CB { b: [ ] } ) , do: nil
5856
5957 @ doc """
6058 Returns the oldest element in the buffer
6159 """
62- def oldest ( % CB { } = cb ) do
63- case :queue . peek_r ( cb . q ) do
64- { _ , val } -> val
65- :empty -> nil
66- end
67- end
60+ def oldest ( % CB { b: [ oldest | _rest ] } ) , do: oldest
61+ def oldest ( % CB { a: a } ) , do: List . last ( a )
6862
6963 @ doc """
7064 Checks the buffer to see if its empty
7165 """
72- def empty? ( % CB { } = cb ) do
73- :queue . is_empty ( cb . q )
66+ def empty? ( % CB { } = cb ) do
67+ cb . count == 0
7468 end
7569
7670 defimpl Enumerable do
@@ -79,7 +73,7 @@ defmodule CircularBuffer do
7973 end
8074
8175 def member? ( cb , element ) do
82- { :ok , :queue . member ( element , cb . q ) }
76+ { :ok , Enum . member? ( cb . a , element ) or Enum . member? ( cb . b , element ) }
8377 end
8478
8579 def reduce ( cb , acc , fun ) do
0 commit comments