// Package queue implements a non-concurrent queue. // // # Implementation // // [Q] is a classic ring buffer. It tracks its head, tail, and length. This // makes determining whether the queue is full or empty trivial. package queue // Q is a non-concurrent queue. type Q[T any] struct { buf []T len int head int tail int } // Make creates a new queue. func Make[T any](cap int) Q[T] { buf := make([]T, cap) return Q[T]{buf: buf} } // Capacity returns the total capacity of the queue. func (b *Q[T]) Capacity() int { return cap(b.buf) } // Len returns the number of items in the queue. func (b *Q[T]) Len() int { return b.len } // CanPush returns true if the queue has space for new items. func (b *Q[T]) CanPush() bool { return cap(b.buf)-b.len != 0 } // CanPop returns true if the queue has one or more items. func (b *Q[T]) CanPop() bool { return b.len != 0 } // PopFront returns the front-most item from the queue. // // If the queue is empty, it panics. func (b *Q[T]) PopFront() T { if !b.CanPop() { panic("cannot pop from empty queue") } item := b.buf[b.head] // clear queue slot so as not to hold on to garbage var empty T b.buf[b.head] = empty b.len-- b.head++ if b.head == cap(b.buf) { b.head = 0 } return item } // PushBack adds an item to the end of the queue. // // If the queue is full, it panics. func (b *Q[T]) PushBack(value T) { if !b.CanPush() { panic("cannot push back to full queue") } b.buf[b.tail] = value b.len++ b.tail++ if b.tail == cap(b.buf) { b.tail = 0 } }