Skip to content

Commit 20634d1

Browse files
committed
Implement all functionality for ringlist
1 parent 680ba5c commit 20634d1

File tree

2 files changed

+312
-1
lines changed

2 files changed

+312
-1
lines changed

ringlist/ringlist.go

+99-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ func (l *List[T, E]) Back() E {
4242
return l.tail
4343
}
4444

45-
// PushBack inserts a new element at the back of the list.
45+
// PushBack inserts a new element at the back of list l.
4646
func (l *List[T, E]) PushBack(e E) {
4747
if l.tail != nil {
4848
l.tail.Link(e)
@@ -51,6 +51,104 @@ func (l *List[T, E]) PushBack(e E) {
5151
l.len++
5252
}
5353

54+
// PushFront inserts a new element at the front of list l.
55+
func (l *List[T, E]) PushFront(e E) {
56+
if l.tail != nil {
57+
l.tail.Link(e)
58+
} else {
59+
l.tail = e
60+
}
61+
l.len++
62+
}
63+
64+
// Do calls function f on each element of the list, in forward order.
65+
// If f returns false, Do stops the iteration.
66+
// f must not change l.
67+
func (l *List[T, E]) Do(f func(e E) bool) {
68+
e := l.Front()
69+
if e == nil {
70+
return
71+
}
72+
73+
if !f(e) {
74+
return
75+
}
76+
77+
for p := e.Next(); p != e; p = p.Next() {
78+
if !f(p) {
79+
return
80+
}
81+
}
82+
}
83+
84+
// MoveAfter moves an element to its new position after mark.
85+
func (l *List[T, E]) MoveAfter(e, mark E) {
86+
l.Remove(e)
87+
88+
mark.Link(e)
89+
l.len++
90+
91+
if mark == l.tail {
92+
l.tail = e
93+
}
94+
}
95+
96+
// MoveBefore moves an element to its new position before mark.
97+
func (l *List[T, E]) MoveBefore(e, mark E) {
98+
l.Remove(e)
99+
100+
mark.Prev().Link(e)
101+
102+
l.len++
103+
}
104+
105+
// MoveToFront moves the element to the front of list l.
106+
func (l *List[T, E]) MoveToFront(e E) {
107+
l.MoveBefore(e, l.Front())
108+
}
109+
110+
// MoveToBack moves the element to the back of list l.
111+
func (l *List[T, E]) MoveToBack(e E) {
112+
l.MoveAfter(e, l.Back())
113+
}
114+
115+
// Move moves element e forward or backwards by at most delta positions
116+
// or until the element becomes the front or back element in the list.
117+
func (l *List[T, E]) Move(e E, delta int) {
118+
if l.tail == nil {
119+
panic("ringlist: invalid element")
120+
}
121+
122+
if l.len == 1 && e != l.tail {
123+
panic("ringlist: invalid element")
124+
}
125+
126+
mark := e
127+
128+
switch {
129+
case delta == 0:
130+
return
131+
132+
case delta > 0:
133+
for i := 0; i < delta; i++ {
134+
if mark = mark.Next(); mark == l.tail {
135+
break
136+
}
137+
}
138+
139+
l.MoveAfter(e, mark)
140+
141+
case delta < 0:
142+
for i := 0; i > delta; i-- {
143+
if mark = mark.Prev(); mark == l.tail.Next() {
144+
break
145+
}
146+
}
147+
148+
l.MoveBefore(e, mark)
149+
}
150+
}
151+
54152
// Remove an element from the list.
55153
func (l *List[T, E]) Remove(e E) {
56154
if e == l.tail {

ringlist/ringlist_test.go

+213
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
package ringlist_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/mgnsk/evcache/v3/ringlist"
7+
. "github.com/onsi/gomega"
8+
)
9+
10+
func TestPushFront(t *testing.T) {
11+
var list ringlist.ElementList[int]
12+
13+
g := NewWithT(t)
14+
15+
list.PushFront(ringlist.NewElement(0))
16+
g.Expect(list.Len()).To(Equal(1))
17+
18+
list.PushFront(ringlist.NewElement(1))
19+
g.Expect(list.Len()).To(Equal(2))
20+
21+
expectValidRing(g, &list)
22+
}
23+
24+
func TestPushBack(t *testing.T) {
25+
var list ringlist.ElementList[int]
26+
27+
g := NewWithT(t)
28+
29+
list.PushFront(ringlist.NewElement(0))
30+
g.Expect(list.Len()).To(Equal(1))
31+
32+
list.PushFront(ringlist.NewElement(1))
33+
g.Expect(list.Len()).To(Equal(2))
34+
35+
expectValidRing(g, &list)
36+
}
37+
38+
func TestMoveToFront(t *testing.T) {
39+
t.Run("moving the back element", func(t *testing.T) {
40+
var list ringlist.ElementList[string]
41+
42+
g := NewWithT(t)
43+
44+
list.PushBack(ringlist.NewElement("one"))
45+
list.PushBack(ringlist.NewElement("two"))
46+
list.MoveToFront(list.Back())
47+
48+
expectValidRing(g, &list)
49+
g.Expect(list.Front().Value).To(Equal("two"))
50+
g.Expect(list.Back().Value).To(Equal("one"))
51+
})
52+
53+
t.Run("moving the middle element", func(t *testing.T) {
54+
var list ringlist.ElementList[string]
55+
56+
g := NewWithT(t)
57+
58+
list.PushBack(ringlist.NewElement("one"))
59+
list.PushBack(ringlist.NewElement("two"))
60+
list.PushBack(ringlist.NewElement("three"))
61+
list.MoveToFront(list.Front().Next())
62+
63+
expectValidRing(g, &list)
64+
g.Expect(list.Front().Value).To(Equal("two"))
65+
g.Expect(list.Back().Value).To(Equal("three"))
66+
})
67+
}
68+
69+
func TestMoveToBack(t *testing.T) {
70+
t.Run("moving the front element", func(t *testing.T) {
71+
var list ringlist.ElementList[string]
72+
73+
g := NewWithT(t)
74+
75+
list.PushBack(ringlist.NewElement("one"))
76+
list.PushBack(ringlist.NewElement("two"))
77+
list.MoveToBack(list.Front())
78+
79+
expectValidRing(g, &list)
80+
g.Expect(list.Front().Value).To(Equal("two"))
81+
g.Expect(list.Back().Value).To(Equal("one"))
82+
})
83+
84+
t.Run("moving the middle element", func(t *testing.T) {
85+
var list ringlist.ElementList[string]
86+
87+
g := NewWithT(t)
88+
89+
list.PushBack(ringlist.NewElement("one"))
90+
list.PushBack(ringlist.NewElement("two"))
91+
list.PushBack(ringlist.NewElement("three"))
92+
list.MoveToBack(list.Front().Next())
93+
94+
expectValidRing(g, &list)
95+
g.Expect(list.Front().Value).To(Equal("one"))
96+
g.Expect(list.Back().Value).To(Equal("two"))
97+
})
98+
}
99+
100+
func TestMoveForward(t *testing.T) {
101+
t.Run("overflow", func(t *testing.T) {
102+
var list ringlist.ElementList[string]
103+
104+
g := NewWithT(t)
105+
106+
list.PushBack(ringlist.NewElement("one"))
107+
list.PushBack(ringlist.NewElement("two"))
108+
list.Move(list.Front(), 3)
109+
110+
expectValidRing(g, &list)
111+
g.Expect(list.Front().Value).To(Equal("two"))
112+
g.Expect(list.Back().Value).To(Equal("one"))
113+
})
114+
115+
t.Run("not overflow", func(t *testing.T) {
116+
var list ringlist.ElementList[string]
117+
118+
g := NewWithT(t)
119+
120+
list.PushBack(ringlist.NewElement("one"))
121+
list.PushBack(ringlist.NewElement("two"))
122+
list.PushBack(ringlist.NewElement("three"))
123+
list.Move(list.Front(), 1)
124+
125+
expectValidRing(g, &list)
126+
g.Expect(list.Front().Value).To(Equal("two"))
127+
g.Expect(list.Front().Next().Value).To(Equal("one"))
128+
g.Expect(list.Back().Value).To(Equal("three"))
129+
})
130+
}
131+
132+
func TestMoveBackwards(t *testing.T) {
133+
t.Run("overflow", func(t *testing.T) {
134+
var list ringlist.ElementList[string]
135+
136+
g := NewWithT(t)
137+
138+
list.PushBack(ringlist.NewElement("one"))
139+
list.PushBack(ringlist.NewElement("two"))
140+
list.Move(list.Back(), -3)
141+
142+
expectValidRing(g, &list)
143+
g.Expect(list.Front().Value).To(Equal("two"))
144+
g.Expect(list.Back().Value).To(Equal("one"))
145+
})
146+
147+
t.Run("not overflow", func(t *testing.T) {
148+
var list ringlist.ElementList[string]
149+
150+
g := NewWithT(t)
151+
152+
list.PushBack(ringlist.NewElement("one"))
153+
list.PushBack(ringlist.NewElement("two"))
154+
list.PushBack(ringlist.NewElement("three"))
155+
list.Move(list.Back(), -1)
156+
157+
expectValidRing(g, &list)
158+
g.Expect(list.Front().Value).To(Equal("one"))
159+
g.Expect(list.Front().Next().Value).To(Equal("three"))
160+
g.Expect(list.Back().Value).To(Equal("two"))
161+
})
162+
}
163+
164+
func TestDo(t *testing.T) {
165+
var list ringlist.ElementList[string]
166+
167+
g := NewWithT(t)
168+
169+
list.PushBack(ringlist.NewElement("one"))
170+
list.PushBack(ringlist.NewElement("two"))
171+
list.PushBack(ringlist.NewElement("three"))
172+
173+
g.Expect(list.Len()).To(Equal(3))
174+
expectValidRing(g, &list)
175+
176+
var elems []string
177+
list.Do(func(e *ringlist.Element[string]) bool {
178+
elems = append(elems, e.Value)
179+
return true
180+
})
181+
182+
g.Expect(elems).To(Equal([]string{"one", "two", "three"}))
183+
}
184+
185+
func expectValidRing[T any](g *WithT, list *ringlist.ElementList[T]) {
186+
g.Expect(list.Len()).To(BeNumerically(">", 0))
187+
g.Expect(list.Front()).To(Equal(list.Back().Next()))
188+
g.Expect(list.Back()).To(Equal(list.Front().Prev()))
189+
190+
{
191+
expectedFront := list.Front()
192+
193+
front := list.Front()
194+
195+
for i := 0; i < list.Len(); i++ {
196+
front = front.Next()
197+
}
198+
199+
g.Expect(front).To(Equal(expectedFront))
200+
}
201+
202+
{
203+
expectedBack := list.Back()
204+
205+
back := list.Back()
206+
207+
for i := 0; i < list.Len(); i++ {
208+
back = back.Prev()
209+
}
210+
211+
g.Expect(back).To(Equal(expectedBack))
212+
}
213+
}

0 commit comments

Comments
 (0)