diff --git a/queue.go b/queue.go index 20ee6aa..5eef029 100644 --- a/queue.go +++ b/queue.go @@ -3,9 +3,7 @@ Using this instead of other, simpler, queue implementations (slice+append or linked list) provides substantial memory and time benefits, and fewer GC pauses. -The queue implemented here is as fast as it is for two additional reasons: it is *not* thread-safe, and it -intentionally does not follow go best-practices regarding errors - if you make a mistake with this -queue (such as trying to remove an element from an empty queue) then who knows what will happen. +The queue implemented here is as fast as it is for an additional reason: it is *not* thread-safe. */ package queue @@ -53,16 +51,31 @@ q.count++ } -// Peek returns the element at the head of the queue. If the queue is empty (Length == 0), -// Peek does not panic, it simply returns garbage. +// Peek returns the element at the head of the queue. This call panics +// if the queue is empty. func (q *Queue) Peek() interface{} { + if q.Length() <= 0 { + panic("queue: empty queue") + } return q.buf[q.head] } -// Remove removes the element from the front of the queue. If you actually want the element, -// call Peek first. If the queue is empty (Length == 0), Remove will put the queue in a bad -// state and all further operations will be undefined. +// Get returns the element at index i in the queue. If the index is +// invalid, the call will panic. +func (q *Queue) Get(i int) interface{} { + if i >= q.Length() || i < 0 { + panic("queue: index out of range") + } + modi := (q.head + i) % len(q.buf) + return q.buf[modi] +} + +// Remove removes the element from the front of the queue. If you actually +// want the element, call Peek first. This call panics if the queue is empty. func (q *Queue) Remove() { + if q.Length() <= 0 { + panic("queue: empty queue") + } q.buf[q.head] = nil q.head = (q.head + 1) % len(q.buf) q.count-- diff --git a/queue_test.go b/queue_test.go index d6e613c..5cb784a 100644 --- a/queue_test.go +++ b/queue_test.go @@ -12,15 +12,86 @@ for i := 0; i < 1000; i++ { q.Add(i) if q.Length() != i+1 { - t.Error("adding: queue with", i , "elements has length", q.Length()) + t.Error("adding: queue with", i, "elements has length", q.Length()) } } for i := 0; i < 1000; i++ { q.Remove() if q.Length() != 1000-i-1 { - t.Error("removing: queue with", 1000-i-i , "elements has length", q.Length()) + t.Error("removing: queue with", 1000-i-i, "elements has length", q.Length()) } } +} + +func TestQueueGet(t *testing.T) { + q := New() + + for i := 0; i < 1000; i++ { + q.Add(i) + for j := 0; j < q.Length(); j++ { + if q.Get(j).(int) != j { + t.Errorf("index %d doesn't contain %d", j, j) + } + } + } +} + +func TestQueueGetOutOfRangePanics(t *testing.T) { + q := New() + + q.Add(1) + q.Add(2) + q.Add(3) + + assertPanics(t, "should panic when negative index", func() { + q.Get(-1) + }) + + assertPanics(t, "should panic when index greater than length", func() { + q.Get(4) + }) +} + +func TestQueuePeekOutOfRangePanics(t *testing.T) { + q := New() + + assertPanics(t, "should panic when peeking empty queue", func() { + q.Peek() + }) + + q.Add(1) + q.Remove() + + assertPanics(t, "should panic when peeking emptied queue", func() { + q.Peek() + }) +} + +func TestQueueRemoveOutOfRangePanics(t *testing.T) { + q := New() + + assertPanics(t, "should panic when removing empty queue", func() { + q.Remove() + }) + + q.Add(1) + q.Remove() + + assertPanics(t, "should panic when removing emptied queue", func() { + q.Remove() + }) +} + +func assertPanics(t *testing.T, name string, f func()) { + defer func() { + if r := recover(); r == nil { + t.Errorf("%s: didn't panic as expected", name) + } else { + t.Logf("%s: got panic as expected: %v", name, r) + } + }() + + f() } // General warning: Go's benchmark utility (go test -bench .) increases the number of @@ -38,6 +109,17 @@ } } +func BenchmarkQueueGet(b *testing.B) { + q := New() + for i := 0; i < b.N; i++ { + q.Add(i) + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + q.Get(i) + } +} + func BenchmarkQueueTickTock(b *testing.B) { q := New() for i := 0; i < b.N; i++ {