Browse Source

Merge pull request #1 from aybabtme/get-index

Add a mean to get an index, allowing iterations of the queue.
Evan Huus 11 năm trước cách đây
mục cha
commit
e423872a64
2 tập tin đã thay đổi với 105 bổ sung10 xóa
  1. 21 8
      queue.go
  2. 84 2
      queue_test.go

+ 21 - 8
queue.go

@@ -3,9 +3,7 @@ Package queue provides a fast, ring-buffer queue based on the version suggested
 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 @@ func (q *Queue) Add(elem interface{}) {
 	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--

+ 84 - 2
queue_test.go

@@ -12,17 +12,88 @@ func TestQueueLength(t *testing.T) {
 	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
 // iterations until the benchmarks take a reasonable amount of time to run; memory usage
 // is *NOT* considered. On my machine, these benchmarks hit around ~1GB before they've had
@@ -38,6 +109,17 @@ func BenchmarkQueueSerial(b *testing.B) {
 	}
 }
 
+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++ {