Files
higress/plugins/wasm-go/extensions/ai-load-balancer/utils/queue.go

176 lines
3.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package utils
import (
"errors"
)
// FixedQueue 实现了一个固定容量的环形缓冲区队列
// 当队列满时,新元素会覆盖最旧的元素
type FixedQueue[T any] struct {
data []T
head int
tail int
size int
cap int
}
// NewFixed 创建一个指定容量的固定队列
func NewFixedQueue[T any](capacity int) *FixedQueue[T] {
if capacity <= 0 {
capacity = 16
}
return &FixedQueue[T]{
data: make([]T, capacity),
head: 0,
tail: 0,
size: 0,
cap: capacity,
}
}
// Enqueue 入队操作
// 如果队列已满,会覆盖最旧的元素
func (q *FixedQueue[T]) Enqueue(item T) {
if q.size < q.cap {
// 队列未满,正常插入
q.data[q.tail] = item
q.tail = (q.tail + 1) % q.cap
q.size++
} else {
// 队列已满,覆盖最旧元素
q.data[q.tail] = item
q.head = (q.head + 1) % q.cap // 移动head丢弃最旧元素
q.tail = (q.tail + 1) % q.cap // tail正常移动
// size保持不变仍然是cap
}
}
// Dequeue 出队操作
func (q *FixedQueue[T]) Dequeue() (T, error) {
var zero T
if q.size == 0 {
return zero, errors.New("queue is empty")
}
item := q.data[q.head]
// 清除引用,避免内存泄漏
var zeroVal T
q.data[q.head] = zeroVal
q.head = (q.head + 1) % q.cap
q.size--
return item, nil
}
// Peek 查看队头元素但不移除
func (q *FixedQueue[T]) Peek() (T, error) {
var zero T
if q.size == 0 {
return zero, errors.New("queue is empty")
}
return q.data[q.head], nil
}
// Size 返回队列中元素的数量
func (q *FixedQueue[T]) Size() int {
return q.size
}
// Capacity 返回队列的固定容量
func (q *FixedQueue[T]) Capacity() int {
return q.cap
}
// IsEmpty 判断队列是否为空
func (q *FixedQueue[T]) IsEmpty() bool {
return q.size == 0
}
// IsFull 判断队列是否已满
func (q *FixedQueue[T]) IsFull() bool {
return q.size == q.cap
}
// OverwriteCount 返回被覆盖的元素数量
// 注意:这个实现中我们不直接跟踪覆盖次数,
// 但可以通过其他方式计算(如果需要的话)
func (q *FixedQueue[T]) OverwriteCount() int {
// 如果需要跟踪覆盖次数,可以添加一个字段
// 目前这个实现不提供此功能
return 0
}
// Clear 清空队列
func (q *FixedQueue[T]) Clear() {
// 清除所有引用
for i := 0; i < q.size; i++ {
idx := (q.head + i) % q.cap
var zero T
q.data[idx] = zero
}
q.head = 0
q.tail = 0
q.size = 0
}
// ToSlice 返回队列元素的切片副本(按队列顺序,从最旧到最新)
func (q *FixedQueue[T]) ToSlice() []T {
if q.size == 0 {
return []T{}
}
result := make([]T, q.size)
if q.head <= q.tail || q.size == q.cap {
if q.head < q.tail {
// 数据连续且未满
copy(result, q.data[q.head:q.tail])
} else {
// 数据连续但已满head == tail
// 或者数据跨越边界
if q.head == q.tail && q.size == q.cap {
// 已满且head == tail的情况
copy(result, q.data[q.head:])
if len(result) > q.cap-q.head {
copy(result[q.cap-q.head:], q.data[:q.tail])
}
} else {
// 跨越边界
copy(result, q.data[q.head:])
copy(result[q.cap-q.head:], q.data[:q.tail])
}
}
} else {
// 跨越边界的情况
copy(result, q.data[q.head:])
copy(result[q.cap-q.head:], q.data[:q.tail])
}
return result
}
// Oldest 返回最旧的元素(队头)
func (q *FixedQueue[T]) Oldest() (T, error) {
return q.Peek()
}
// Newest 返回最新的元素(队尾的前一个元素)
func (q *FixedQueue[T]) Newest() (T, error) {
var zero T
if q.size == 0 {
return zero, errors.New("queue is empty")
}
// tail指向下一个插入位置所以最新元素在 (tail - 1 + cap) % cap
newestIndex := (q.tail - 1 + q.cap) % q.cap
return q.data[newestIndex], nil
}
// ForEach 对队列中的每个元素执行回调函数
func (q *FixedQueue[T]) ForEach(fn func(index int, item T)) {
for i := 0; i < q.size; i++ {
idx := (q.head + i) % q.cap
fn(i, q.data[idx])
}
}