diff --git a/util/queue.go b/util/queue.go new file mode 100644 index 0000000000..63a2ffc16a --- /dev/null +++ b/util/queue.go @@ -0,0 +1,113 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package util + +// LIFO represents a simple LIFO queue. +type LIFO struct { + top *queueNode + size int +} + +type queueNode struct { + v T + next *queueNode +} + +// NewLIFO returns a new LIFO queue containing elements ts starting with the +// left-most argument at the bottom. +func NewLIFO(ts ...T) *LIFO { + s := &LIFO{} + for i := range ts { + s.Push(ts[i]) + } + return s +} + +// Push adds a new element onto the LIFO. +func (s *LIFO) Push(t T) { + node := &queueNode{v: t, next: s.top} + s.top = node + s.size++ +} + +// Peek returns the top of the LIFO. If LIFO is empty, returns nil, false. +func (s *LIFO) Peek() (T, bool) { + if s.top == nil { + return nil, false + } + return s.top.v, true +} + +// Pop returns the top of the LIFO and removes it. If LIFO is empty returns +// nil, false. +func (s *LIFO) Pop() (T, bool) { + if s.top == nil { + return nil, false + } + node := s.top + s.top = node.next + s.size-- + return node.v, true +} + +// Size returns the size of the LIFO. +func (s *LIFO) Size() int { + return s.size +} + +// FIFO represents a simple FIFO queue. +type FIFO struct { + front *queueNode + back *queueNode + size int +} + +// NewFIFO returns a new FIFO queue containing elements ts starting with the +// left-most argument at the front. +func NewFIFO(ts ...T) *FIFO { + s := &FIFO{} + for i := range ts { + s.Push(ts[i]) + } + return s +} + +// Push adds a new element onto the LIFO. +func (s *FIFO) Push(t T) { + node := &queueNode{v: t, next: nil} + if s.front == nil { + s.front = node + s.back = node + } else { + s.back.next = node + s.back = node + } + s.size++ +} + +// Peek returns the top of the LIFO. If LIFO is empty, returns nil, false. +func (s *FIFO) Peek() (T, bool) { + if s.front == nil { + return nil, false + } + return s.front.v, true +} + +// Pop returns the top of the LIFO and removes it. If LIFO is empty returns +// nil, false. +func (s *FIFO) Pop() (T, bool) { + if s.front == nil { + return nil, false + } + node := s.front + s.front = node.next + s.size-- + return node.v, true +} + +// Size returns the size of the LIFO. +func (s *FIFO) Size() int { + return s.size +} diff --git a/util/queue_test.go b/util/queue_test.go new file mode 100644 index 0000000000..a3f3707a03 --- /dev/null +++ b/util/queue_test.go @@ -0,0 +1,84 @@ +// Copyright 2017 The OPA Authors. All rights reserved. +// Use of this source code is governed by an Apache2 +// license that can be found in the LICENSE file. + +package util + +import "testing" + +func TestLIFO(t *testing.T) { + + lifo := NewLIFO(1, 2, 3, 4) + + if lifo.Size() != 4 { + t.Fatalf("Expected LIFO size == 4 but got: %v", lifo.Size()) + } + + for i := 4; i >= 1; i-- { + x, ok := lifo.Peek() + if !ok || x != i { + t.Fatalf("Expected peek() == %v but got: %v (ok=%v)", i, x, ok) + } + x, ok = lifo.Pop() + if !ok || x != i { + t.Fatalf("Expected pop() == %v but got: %v (ok=%v)", i, x, ok) + } + } + + x, ok := lifo.Peek() + if ok || x != nil { + t.Fatalf("Expected peek() == nil, false but got: %v (ok=%v)", x, ok) + } + + x, ok = lifo.Pop() + if ok || x != nil { + t.Fatalf("Expected pop() == nil, false but got: %v (ok=%v)", x, ok) + } + + for i := 4; i >= 1; i-- { + lifo.Push(i) + x, ok = lifo.Peek() + if !ok || x != i { + t.Fatalf("Expected peek() == %v but got: %v (ok=%v)", i, x, ok) + } + } + +} + +func TestFIFO(t *testing.T) { + fifo := NewFIFO(1, 2, 3, 4) + + if fifo.Size() != 4 { + t.Fatalf("Expected FIFO size == 1 but got: %v", fifo.Size()) + } + + for i := 1; i <= 4; i++ { + x, ok := fifo.Peek() + if !ok || x != i { + t.Fatalf("Expected peek() == %v but got: %v (ok=%v)", i, x, ok) + } + x, ok = fifo.Pop() + if !ok || x != i { + t.Fatalf("Expected pop() == %v but got: %v (ok=%v)", i, x, ok) + } + } + + x, ok := fifo.Peek() + if ok || x != nil { + t.Fatalf("Expected peek() == nil, false but got: %v (ok=%v)", x, ok) + } + + x, ok = fifo.Pop() + if ok || x != nil { + t.Fatalf("Expected pop() == nil, false but got: %v (ok=%v)", x, ok) + } + + for i := 1; i <= 4; i++ { + fifo.Push(i) + x, ok = fifo.Peek() + if !ok || x != 1 { + t.Fatalf("Expected peek() == %v but got: %v (ok=%v)", 1, x, ok) + } + } + +}