diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-16 19:23:18 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-16 19:23:18 +0000 |
commit | 43a123c1ae6613b3efeed291fa552ecd909d3acf (patch) | |
tree | fd92518b7024bc74031f78a1cf9e454b65e73665 /src/container/ring | |
parent | Initial commit. (diff) | |
download | golang-1.20-43a123c1ae6613b3efeed291fa552ecd909d3acf.tar.xz golang-1.20-43a123c1ae6613b3efeed291fa552ecd909d3acf.zip |
Adding upstream version 1.20.14.upstream/1.20.14upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/container/ring')
-rw-r--r-- | src/container/ring/example_test.go | 193 | ||||
-rw-r--r-- | src/container/ring/ring.go | 136 | ||||
-rw-r--r-- | src/container/ring/ring_test.go | 228 |
3 files changed, 557 insertions, 0 deletions
diff --git a/src/container/ring/example_test.go b/src/container/ring/example_test.go new file mode 100644 index 0000000..4b659d2 --- /dev/null +++ b/src/container/ring/example_test.go @@ -0,0 +1,193 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ring_test + +import ( + "container/ring" + "fmt" +) + +func ExampleRing_Len() { + // Create a new ring of size 4 + r := ring.New(4) + + // Print out its length + fmt.Println(r.Len()) + + // Output: + // 4 +} + +func ExampleRing_Next() { + // Create a new ring of size 5 + r := ring.New(5) + + // Get the length of the ring + n := r.Len() + + // Initialize the ring with some integer values + for i := 0; i < n; i++ { + r.Value = i + r = r.Next() + } + + // Iterate through the ring and print its contents + for j := 0; j < n; j++ { + fmt.Println(r.Value) + r = r.Next() + } + + // Output: + // 0 + // 1 + // 2 + // 3 + // 4 +} + +func ExampleRing_Prev() { + // Create a new ring of size 5 + r := ring.New(5) + + // Get the length of the ring + n := r.Len() + + // Initialize the ring with some integer values + for i := 0; i < n; i++ { + r.Value = i + r = r.Next() + } + + // Iterate through the ring backwards and print its contents + for j := 0; j < n; j++ { + r = r.Prev() + fmt.Println(r.Value) + } + + // Output: + // 4 + // 3 + // 2 + // 1 + // 0 +} + +func ExampleRing_Do() { + // Create a new ring of size 5 + r := ring.New(5) + + // Get the length of the ring + n := r.Len() + + // Initialize the ring with some integer values + for i := 0; i < n; i++ { + r.Value = i + r = r.Next() + } + + // Iterate through the ring and print its contents + r.Do(func(p any) { + fmt.Println(p.(int)) + }) + + // Output: + // 0 + // 1 + // 2 + // 3 + // 4 +} + +func ExampleRing_Move() { + // Create a new ring of size 5 + r := ring.New(5) + + // Get the length of the ring + n := r.Len() + + // Initialize the ring with some integer values + for i := 0; i < n; i++ { + r.Value = i + r = r.Next() + } + + // Move the pointer forward by three steps + r = r.Move(3) + + // Iterate through the ring and print its contents + r.Do(func(p any) { + fmt.Println(p.(int)) + }) + + // Output: + // 3 + // 4 + // 0 + // 1 + // 2 +} + +func ExampleRing_Link() { + // Create two rings, r and s, of size 2 + r := ring.New(2) + s := ring.New(2) + + // Get the length of the ring + lr := r.Len() + ls := s.Len() + + // Initialize r with 0s + for i := 0; i < lr; i++ { + r.Value = 0 + r = r.Next() + } + + // Initialize s with 1s + for j := 0; j < ls; j++ { + s.Value = 1 + s = s.Next() + } + + // Link ring r and ring s + rs := r.Link(s) + + // Iterate through the combined ring and print its contents + rs.Do(func(p any) { + fmt.Println(p.(int)) + }) + + // Output: + // 0 + // 0 + // 1 + // 1 +} + +func ExampleRing_Unlink() { + // Create a new ring of size 6 + r := ring.New(6) + + // Get the length of the ring + n := r.Len() + + // Initialize the ring with some integer values + for i := 0; i < n; i++ { + r.Value = i + r = r.Next() + } + + // Unlink three elements from r, starting from r.Next() + r.Unlink(3) + + // Iterate through the remaining ring and print its contents + r.Do(func(p any) { + fmt.Println(p.(int)) + }) + + // Output: + // 0 + // 4 + // 5 +} diff --git a/src/container/ring/ring.go b/src/container/ring/ring.go new file mode 100644 index 0000000..268670b --- /dev/null +++ b/src/container/ring/ring.go @@ -0,0 +1,136 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package ring implements operations on circular lists. +package ring + +// A Ring is an element of a circular list, or ring. +// Rings do not have a beginning or end; a pointer to any ring element +// serves as reference to the entire ring. Empty rings are represented +// as nil Ring pointers. The zero value for a Ring is a one-element +// ring with a nil Value. +type Ring struct { + next, prev *Ring + Value any // for use by client; untouched by this library +} + +func (r *Ring) init() *Ring { + r.next = r + r.prev = r + return r +} + +// Next returns the next ring element. r must not be empty. +func (r *Ring) Next() *Ring { + if r.next == nil { + return r.init() + } + return r.next +} + +// Prev returns the previous ring element. r must not be empty. +func (r *Ring) Prev() *Ring { + if r.next == nil { + return r.init() + } + return r.prev +} + +// Move moves n % r.Len() elements backward (n < 0) or forward (n >= 0) +// in the ring and returns that ring element. r must not be empty. +func (r *Ring) Move(n int) *Ring { + if r.next == nil { + return r.init() + } + switch { + case n < 0: + for ; n < 0; n++ { + r = r.prev + } + case n > 0: + for ; n > 0; n-- { + r = r.next + } + } + return r +} + +// New creates a ring of n elements. +func New(n int) *Ring { + if n <= 0 { + return nil + } + r := new(Ring) + p := r + for i := 1; i < n; i++ { + p.next = &Ring{prev: p} + p = p.next + } + p.next = r + r.prev = p + return r +} + +// Link connects ring r with ring s such that r.Next() +// becomes s and returns the original value for r.Next(). +// r must not be empty. +// +// If r and s point to the same ring, linking +// them removes the elements between r and s from the ring. +// The removed elements form a subring and the result is a +// reference to that subring (if no elements were removed, +// the result is still the original value for r.Next(), +// and not nil). +// +// If r and s point to different rings, linking +// them creates a single ring with the elements of s inserted +// after r. The result points to the element following the +// last element of s after insertion. +func (r *Ring) Link(s *Ring) *Ring { + n := r.Next() + if s != nil { + p := s.Prev() + // Note: Cannot use multiple assignment because + // evaluation order of LHS is not specified. + r.next = s + s.prev = r + n.prev = p + p.next = n + } + return n +} + +// Unlink removes n % r.Len() elements from the ring r, starting +// at r.Next(). If n % r.Len() == 0, r remains unchanged. +// The result is the removed subring. r must not be empty. +func (r *Ring) Unlink(n int) *Ring { + if n <= 0 { + return nil + } + return r.Link(r.Move(n + 1)) +} + +// Len computes the number of elements in ring r. +// It executes in time proportional to the number of elements. +func (r *Ring) Len() int { + n := 0 + if r != nil { + n = 1 + for p := r.Next(); p != r; p = p.next { + n++ + } + } + return n +} + +// Do calls function f on each element of the ring, in forward order. +// The behavior of Do is undefined if f changes *r. +func (r *Ring) Do(f func(any)) { + if r != nil { + f(r.Value) + for p := r.Next(); p != r; p = p.next { + f(p.Value) + } + } +} diff --git a/src/container/ring/ring_test.go b/src/container/ring/ring_test.go new file mode 100644 index 0000000..28acbbc --- /dev/null +++ b/src/container/ring/ring_test.go @@ -0,0 +1,228 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ring + +import ( + "fmt" + "testing" +) + +// For debugging - keep around. +func dump(r *Ring) { + if r == nil { + fmt.Println("empty") + return + } + i, n := 0, r.Len() + for p := r; i < n; p = p.next { + fmt.Printf("%4d: %p = {<- %p | %p ->}\n", i, p, p.prev, p.next) + i++ + } + fmt.Println() +} + +func verify(t *testing.T, r *Ring, N int, sum int) { + // Len + n := r.Len() + if n != N { + t.Errorf("r.Len() == %d; expected %d", n, N) + } + + // iteration + n = 0 + s := 0 + r.Do(func(p any) { + n++ + if p != nil { + s += p.(int) + } + }) + if n != N { + t.Errorf("number of forward iterations == %d; expected %d", n, N) + } + if sum >= 0 && s != sum { + t.Errorf("forward ring sum = %d; expected %d", s, sum) + } + + if r == nil { + return + } + + // connections + if r.next != nil { + var p *Ring // previous element + for q := r; p == nil || q != r; q = q.next { + if p != nil && p != q.prev { + t.Errorf("prev = %p, expected q.prev = %p\n", p, q.prev) + } + p = q + } + if p != r.prev { + t.Errorf("prev = %p, expected r.prev = %p\n", p, r.prev) + } + } + + // Next, Prev + if r.Next() != r.next { + t.Errorf("r.Next() != r.next") + } + if r.Prev() != r.prev { + t.Errorf("r.Prev() != r.prev") + } + + // Move + if r.Move(0) != r { + t.Errorf("r.Move(0) != r") + } + if r.Move(N) != r { + t.Errorf("r.Move(%d) != r", N) + } + if r.Move(-N) != r { + t.Errorf("r.Move(%d) != r", -N) + } + for i := 0; i < 10; i++ { + ni := N + i + mi := ni % N + if r.Move(ni) != r.Move(mi) { + t.Errorf("r.Move(%d) != r.Move(%d)", ni, mi) + } + if r.Move(-ni) != r.Move(-mi) { + t.Errorf("r.Move(%d) != r.Move(%d)", -ni, -mi) + } + } +} + +func TestCornerCases(t *testing.T) { + var ( + r0 *Ring + r1 Ring + ) + // Basics + verify(t, r0, 0, 0) + verify(t, &r1, 1, 0) + // Insert + r1.Link(r0) + verify(t, r0, 0, 0) + verify(t, &r1, 1, 0) + // Insert + r1.Link(r0) + verify(t, r0, 0, 0) + verify(t, &r1, 1, 0) + // Unlink + r1.Unlink(0) + verify(t, &r1, 1, 0) +} + +func makeN(n int) *Ring { + r := New(n) + for i := 1; i <= n; i++ { + r.Value = i + r = r.Next() + } + return r +} + +func sumN(n int) int { return (n*n + n) / 2 } + +func TestNew(t *testing.T) { + for i := 0; i < 10; i++ { + r := New(i) + verify(t, r, i, -1) + } + for i := 0; i < 10; i++ { + r := makeN(i) + verify(t, r, i, sumN(i)) + } +} + +func TestLink1(t *testing.T) { + r1a := makeN(1) + var r1b Ring + r2a := r1a.Link(&r1b) + verify(t, r2a, 2, 1) + if r2a != r1a { + t.Errorf("a) 2-element link failed") + } + + r2b := r2a.Link(r2a.Next()) + verify(t, r2b, 2, 1) + if r2b != r2a.Next() { + t.Errorf("b) 2-element link failed") + } + + r1c := r2b.Link(r2b) + verify(t, r1c, 1, 1) + verify(t, r2b, 1, 0) +} + +func TestLink2(t *testing.T) { + var r0 *Ring + r1a := &Ring{Value: 42} + r1b := &Ring{Value: 77} + r10 := makeN(10) + + r1a.Link(r0) + verify(t, r1a, 1, 42) + + r1a.Link(r1b) + verify(t, r1a, 2, 42+77) + + r10.Link(r0) + verify(t, r10, 10, sumN(10)) + + r10.Link(r1a) + verify(t, r10, 12, sumN(10)+42+77) +} + +func TestLink3(t *testing.T) { + var r Ring + n := 1 + for i := 1; i < 10; i++ { + n += i + verify(t, r.Link(New(i)), n, -1) + } +} + +func TestUnlink(t *testing.T) { + r10 := makeN(10) + s10 := r10.Move(6) + + sum10 := sumN(10) + + verify(t, r10, 10, sum10) + verify(t, s10, 10, sum10) + + r0 := r10.Unlink(0) + verify(t, r0, 0, 0) + + r1 := r10.Unlink(1) + verify(t, r1, 1, 2) + verify(t, r10, 9, sum10-2) + + r9 := r10.Unlink(9) + verify(t, r9, 9, sum10-2) + verify(t, r10, 9, sum10-2) +} + +func TestLinkUnlink(t *testing.T) { + for i := 1; i < 4; i++ { + ri := New(i) + for j := 0; j < i; j++ { + rj := ri.Unlink(j) + verify(t, rj, j, -1) + verify(t, ri, i-j, -1) + ri.Link(rj) + verify(t, ri, i, -1) + } + } +} + +// Test that calling Move() on an empty Ring initializes it. +func TestMoveEmptyRing(t *testing.T) { + var r Ring + + r.Move(1) + verify(t, &r, 1, 0) +} |