diff --git a/ring.go b/ring.go new file mode 100644 index 0000000..1352449 --- /dev/null +++ b/ring.go @@ -0,0 +1,102 @@ +package kcp + +import "fmt" + +type Ring struct { + head int + tail int + elements []interface{} +} + +func NewRing(size int) *Ring { + if size <= 8 { + size = 8 // Ensure a minimum size + } + return &Ring{ + head: 0, + tail: 0, + elements: make([]interface{}, size), + } +} + +func (r *Ring) Len() int { + if r.head <= r.tail { + return r.tail - r.head + } + return len(r.elements) - r.head + r.tail +} + +func (r *Ring) Push(v interface{}) { + if r.IsFull() { + r.grow() + } + + r.elements[r.tail] = v + r.tail = (r.tail + 1) % len(r.elements) +} + +func (r *Ring) Pop() (interface{}, bool) { + if r.Len() == 0 { + return nil, false + } + + value := r.elements[r.head] + r.elements[r.head] = nil // Clear the value to avoid memory leak + r.head = (r.head + 1) % len(r.elements) + return value, true +} + +func (r *Ring) Peek() interface{} { + if r.Len() == 0 { + return nil + } + return r.elements[r.head] +} + +func (r *Ring) Clear() { + r.head = 0 + r.tail = 0 + r.elements = make([]interface{}, 0, 8) +} + +func (r *Ring) IsEmpty() bool { + return r.Len() == 0 +} + +func (r *Ring) Size() int { + return len(r.elements) +} + +func (r *Ring) IsFull() bool { + if (r.tail+1)%len(r.elements) == r.head { + return true + } + return false +} + +// Grow increases the size of the ring buffer if it is full. +// It copies the existing elements to a new larger slice. +// If the ring is empty, it initializes it with a minimum size. +func (r *Ring) grow() { + newSize := len(r.elements) * 2 + if newSize == 0 { + newSize = 8 // Minimum size + } + newElements := make([]interface{}, newSize) + + if r.head < r.tail { + // | ... head ..... tail ... | + copy(newElements, r.elements[r.head:r.tail]) + } else { + // elements: |... tail head ... | + // newElements: | head ..... tail 00000000 | + copy(newElements, r.elements[r.head:]) + copy(newElements[len(r.elements)-r.head:], r.elements[:r.tail]) + } + + r.elements = newElements + r.head = 0 + r.tail = r.Len() + + fmt.Println("growed to size:", newSize, "head:", r.head, "tail:", r.tail) +} diff --git a/ring_test.go b/ring_test.go new file mode 100644 index 0000000..2f5f1f9 --- /dev/null +++ b/ring_test.go @@ -0,0 +1,37 @@ +package kcp + +import "testing" + +func TestRing(t *testing.T) { + r := NewRing(1) + for i := 0; i < 64; i++ { + r.Push(i) + if r.Len() != i+1 { + t.Errorf("Expected length %d, got %d", i+1, r.Len()) + } + } + + for i := 0; i < 32; i++ { + val, ok := r.Pop() + if !ok || val != i { + t.Errorf("Expected to pop %d, got %d (ok: %v)", i, val, ok) + } + } + + if r.Len() != 32 { + t.Errorf("Expected length 32 after popping 32 elements, got %d", r.Len()) + } + + // Push more elements to test the ring's behavior + size := r.Len() + for i := 0; i < 32; i++ { + r.Push(i) + if r.Len() != i+1+size { + t.Errorf("Expected length %d, got %d", i+1+size, r.Len()) + } + } + + if r.Len() != 64 { + t.Errorf("Expected length 64 after pushing 32 more elements, got %d", r.Len()) + } +}