Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ CircularBuffer provides a general-purpose circular buffer data structure.
```elixir
# Create a new circular buffer that holds 5 elements
iex> cb = CircularBuffer.new(5)
#CircularBuffer<[]>
CircularBuffer.new([], 5)

# Fill it up
iex> cb = Enum.into(1..5, cb)
#CircularBuffer<[1, 2, 3, 4, 5]>
CircularBuffer.new([1, 2, 3, 4, 5], 5)

# Verify that 1 is the oldest and 5 is the newest element in the buffer
iex> CircularBuffer.oldest(cb)
Expand All @@ -29,7 +29,7 @@ iex> CircularBuffer.newest(cb)

# Add another element. 1 gets pushed out.
iex> cb = CircularBuffer.insert(cb, 6)
#CircularBuffer<[2, 3, 4, 5, 6]>
CircularBuffer.new([2, 3, 4, 5, 6], 5)

# CircularBuffer implements Enumerable so all Enum.* functions work
iex> Enum.sum(cb)
Expand Down
23 changes: 21 additions & 2 deletions lib/circular_buffer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ defmodule CircularBuffer do
like the following works:

iex> cb = Enum.into([1, 2, 3, 4], CircularBuffer.new(3))
#CircularBuffer<[2, 3, 4]>
iex> Enum.map(cb, fn x -> x * 2 end)
[4, 6, 8]
"""
Expand All @@ -48,6 +47,14 @@ defmodule CircularBuffer do
%CB{a: [], b: [], max_size: size, count: 0}
end

@doc """
Creates a new circular buffer with a given size and contents
"""
@spec new(Enumerable.t(), non_neg_integer()) :: t()
def new(enumerable, size) when is_integer(size) and size >= 0 do
Enum.reduce(enumerable, new(size), &insert(&2, &1))
end

@doc """
Inserts a new item into the next location of the circular buffer
"""
Expand Down Expand Up @@ -120,6 +127,12 @@ defmodule CircularBuffer do
cb.count == 0
end

@doc """
Return the max size of the buffer
"""
@spec max_size(t()) :: non_neg_integer()
def max_size(%CB{max_size: max_size}), do: max_size

defimpl Enumerable do
def count(cb) do
{:ok, cb.count}
Expand Down Expand Up @@ -160,7 +173,13 @@ defmodule CircularBuffer do
import Inspect.Algebra

def inspect(cb, opts) do
concat(["#CircularBuffer<", to_doc(CB.to_list(cb), opts), ">"])
concat([
"CircularBuffer.new(",
to_doc(CB.to_list(cb), opts),
", ",
to_doc(CB.max_size(cb), opts),
")"
])
end
end
end
20 changes: 17 additions & 3 deletions test/circular_buffer_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,23 @@ defmodule CircularBufferTest do
end
end

test "can be inspected" do
str = inspect(Enum.into([1, 2, 3, 4], CB.new(4)))
assert str == "#CircularBuffer<[1, 2, 3, 4]>"
property "inspect matches contents" do
forall {size, is} <- size_and_list() do
buffer = Enum.reduce(is, CB.new(size), fn i, cb -> CB.insert(cb, i) end)
{inspected, _} = Code.eval_string(inspect(buffer, limit: :infinity))

buffer.max_size == inspected.max_size and CB.to_list(inspected) == CB.to_list(buffer)
end
end

test "inspecting zero-length buffer" do
str = inspect(CB.new(0))
assert str == "CircularBuffer.new([], 0)"
end

test "max_size matches created size" do
cb = CB.new(15)
assert CB.max_size(cb) == 15
end

test "Enum.slice" do
Expand Down