Skip to content

Commit

Permalink
cache: Add List (#67)
Browse files Browse the repository at this point in the history
  • Loading branch information
sunshineplan authored Jul 2, 2024
1 parent 72c92bb commit 1fb478e
Show file tree
Hide file tree
Showing 3 changed files with 337 additions and 0 deletions.
134 changes: 134 additions & 0 deletions cache/list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package cache

import "container/list"

// Element is an element of a linked list.
type Element[T any] struct {
e *list.Element
}

// Value returns the value stored with this element.
func (e *Element[T]) Value() T { return e.e.Value.(T) }

// Next returns the next list element or nil.
func (e *Element[T]) Next() *Element[T] {
if e := e.e.Next(); e != nil {
return &Element[T]{e}
}
return nil
}

// Prev returns the previous list element or nil.
func (e *Element[T]) Prev() *Element[T] {
if e := e.e.Prev(); e != nil {
return &Element[T]{e}
}
return nil
}

// List represents a doubly linked list.
// The zero value for List is an empty list ready to use.
type List[T any] struct {
l list.List
}

// Init initializes or clears list l.
func (l *List[T]) Init() *List[T] {
l.l.Init()
return l
}

// New returns an initialized list.
func NewList[T any]() *List[T] { return new(List[T]).Init() }

// Len returns the number of elements of list l. The complexity is O(1).
func (l *List[T]) Len() int {
return l.l.Len()
}

// Front returns the first element of list l or nil if the list is empty.
func (l *List[T]) Front() *Element[T] {
if e := l.l.Front(); e != nil {
return &Element[T]{e}
}
return nil
}

// Back returns the last element of list l or nil if the list is empty.
func (l *List[T]) Back() *Element[T] {
if e := l.l.Back(); e != nil {
return &Element[T]{e}
}
return nil
}

// Remove removes e from l if e is an element of list l.
// It returns the element value e.Value.
// The element must not be nil.
func (l *List[T]) Remove(e *Element[T]) T {
return l.l.Remove(e.e).(T)
}

// PushFront inserts a new element e with value v at the front of list l and returns e.
func (l *List[T]) PushFront(v T) *Element[T] {
return &Element[T]{l.l.PushFront(v)}
}

// PushBack inserts a new element e with value v at the back of list l and returns e.
func (l *List[T]) PushBack(v T) *Element[T] {
return &Element[T]{l.l.PushBack(v)}
}

// InsertBefore inserts a new element e with value v immediately before mark and returns e.
// If mark is not an element of l, the list is not modified.
// The mark must not be nil.
func (l *List[T]) InsertBefore(v T, mark *Element[T]) *Element[T] {
return &Element[T]{l.l.InsertBefore(v, mark.e)}
}

// InsertAfter inserts a new element e with value v immediately after mark and returns e.
// If mark is not an element of l, the list is not modified.
// The mark must not be nil.
func (l *List[T]) InsertAfter(v T, mark *Element[T]) *Element[T] {
return &Element[T]{l.l.InsertAfter(v, mark.e)}
}

// MoveToFront moves element e to the front of list l.
// If e is not an element of l, the list is not modified.
// The element must not be nil.
func (l *List[T]) MoveToFront(e *Element[T]) {
l.l.MoveToFront(e.e)
}

// MoveToBack moves element e to the back of list l.
// If e is not an element of l, the list is not modified.
// The element must not be nil.
func (l *List[T]) MoveToBack(e *Element[T]) {
l.l.MoveToBack(e.e)
}

// MoveBefore moves element e to its new position before mark.
// If e or mark is not an element of l, or e == mark, the list is not modified.
// The element and mark must not be nil.
func (l *List[T]) MoveBefore(e, mark *Element[T]) {
l.l.MoveBefore(e.e, mark.e)
}

// MoveAfter moves element e to its new position after mark.
// If e or mark is not an element of l, or e == mark, the list is not modified.
// The element and mark must not be nil.
func (l *List[T]) MoveAfter(e, mark *Element[T]) {
l.l.MoveAfter(e.e, mark.e)
}

// PushBackList inserts a copy of another list at the back of list l.
// The lists l and other may be the same. They must not be nil.
func (l *List[T]) PushBackList(other *List[T]) {
l.l.PushBackList(&other.l)
}

// PushFrontList inserts a copy of another list at the front of list l.
// The lists l and other may be the same. They must not be nil.
func (l *List[T]) PushFrontList(other *List[T]) {
l.l.PushFrontList(&other.l)
}
172 changes: 172 additions & 0 deletions cache/list_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
// 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 cache

import (
"container/list"
"testing"
)

func checkListLen[T int](t *testing.T, l *List[T], len int) bool {
if n := l.Len(); n != len {
t.Errorf("l.Len() = %d, want %d", n, len)
return false
}
return true
}

func checkList[T int](t *testing.T, l *List[T], es []T) {
if !checkListLen(t, l, len(es)) {
return
}

i := 0
for e := l.Front(); e != nil; e = e.Next() {
if v := e.Value(); v != es[i] {
t.Errorf("elt[%d].Value = %v, want %v", i, v, es[i])
}
i++
}
}

func TestExtending(t *testing.T) {
l1 := NewList[int]()
l2 := NewList[int]()

l1.PushBack(1)
l1.PushBack(2)
l1.PushBack(3)

l2.PushBack(4)
l2.PushBack(5)

l3 := NewList[int]()
l3.PushBackList(l1)
checkList(t, l3, []int{1, 2, 3})
l3.PushBackList(l2)
checkList(t, l3, []int{1, 2, 3, 4, 5})

l3 = NewList[int]()
l3.PushFrontList(l2)
checkList(t, l3, []int{4, 5})
l3.PushFrontList(l1)
checkList(t, l3, []int{1, 2, 3, 4, 5})

checkList(t, l1, []int{1, 2, 3})
checkList(t, l2, []int{4, 5})

l3 = NewList[int]()
l3.PushBackList(l1)
checkList(t, l3, []int{1, 2, 3})
l3.PushBackList(l3)
checkList(t, l3, []int{1, 2, 3, 1, 2, 3})

l3 = NewList[int]()
l3.PushFrontList(l1)
checkList(t, l3, []int{1, 2, 3})
l3.PushFrontList(l3)
checkList(t, l3, []int{1, 2, 3, 1, 2, 3})

l3 = NewList[int]()
l1.PushBackList(l3)
checkList(t, l1, []int{1, 2, 3})
l1.PushFrontList(l3)
checkList(t, l1, []int{1, 2, 3})
}

func TestIssue4103(t *testing.T) {
l1 := NewList[int]()
l1.PushBack(1)
l1.PushBack(2)

l2 := NewList[int]()
l2.PushBack(3)
l2.PushBack(4)

e := l1.Front()
l2.Remove(e) // l2 should not change because e is not an element of l2
if n := l2.Len(); n != 2 {
t.Errorf("l2.Len() = %d, want 2", n)
}

l1.InsertBefore(8, e)
if n := l1.Len(); n != 3 {
t.Errorf("l1.Len() = %d, want 3", n)
}
}

func TestIssue6349(t *testing.T) {
l := NewList[int]()
l.PushBack(1)
l.PushBack(2)

e := l.Front()
l.Remove(e)
if v := e.Value(); v != 1 {
t.Errorf("e.value = %d, want 1", v)
}
if e.Next() != nil {
t.Errorf("e.Next() != nil")
}
if e.Prev() != nil {
t.Errorf("e.Prev() != nil")
}
}

// Test PushFront, PushBack, PushFrontList, PushBackList with uninitialized List
func TestZeroList(t *testing.T) {
var l1 = new(List[int])
l1.PushFront(1)
checkList(t, l1, []int{1})

var l2 = new(List[int])
l2.PushBack(1)
checkList(t, l2, []int{1})

var l3 = new(List[int])
l3.PushFrontList(l1)
checkList(t, l3, []int{1})

var l4 = new(List[int])
l4.PushBackList(l2)
checkList(t, l4, []int{1})
}

// Test that a list l is not modified when calling InsertBefore with a mark that is not an element of l.
func TestInsertBeforeUnknownMark(t *testing.T) {
var l List[int]
l.PushBack(1)
l.PushBack(2)
l.PushBack(3)
l.InsertBefore(1, &Element[int]{new(list.Element)})
checkList(t, &l, []int{1, 2, 3})
}

// Test that a list l is not modified when calling InsertAfter with a mark that is not an element of l.
func TestInsertAfterUnknownMark(t *testing.T) {
var l List[int]
l.PushBack(1)
l.PushBack(2)
l.PushBack(3)
l.InsertAfter(1, &Element[int]{new(list.Element)})
checkList(t, &l, []int{1, 2, 3})
}

// Test that a list l is not modified when calling MoveAfter or MoveBefore with a mark that is not an element of l.
func TestMoveUnknownMark(t *testing.T) {
var l1 List[int]
e1 := l1.PushBack(1)

var l2 List[int]
e2 := l2.PushBack(2)

l1.MoveAfter(e1, e2)
checkList(t, &l1, []int{1})
checkList(t, &l2, []int{2})

l1.MoveBefore(e1, e2)
checkList(t, &l1, []int{1})
checkList(t, &l2, []int{2})
}
31 changes: 31 additions & 0 deletions cache/map.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ func NewMap[Key, Value any]() *Map[Key, Value] {
return &Map[Key, Value]{}
}

// Load returns the value stored in the map for a key, or nil if no
// value is present.
// The ok result indicates whether value was found in the map.
func (m *Map[Key, Value]) Load(key Key) (value Value, ok bool) {
var v any
if v, ok = m.m.Load(key); ok && v != nil {
Expand All @@ -18,10 +21,14 @@ func (m *Map[Key, Value]) Load(key Key) (value Value, ok bool) {
return
}

// Store sets the value for a key.
func (m *Map[Key, Value]) Store(key Key, value Value) {
m.m.Store(key, value)
}

// LoadOrStore returns the existing value for the key if present.
// Otherwise, it stores and returns the given value.
// The loaded result is true if the value was loaded, false if stored.
func (m *Map[Key, Value]) LoadOrStore(key Key, value Value) (actual Value, loaded bool) {
var v any
if v, loaded = m.m.LoadOrStore(key, value); v != nil {
Expand All @@ -30,6 +37,8 @@ func (m *Map[Key, Value]) LoadOrStore(key Key, value Value) (actual Value, loade
return
}

// LoadAndDelete deletes the value for a key, returning the previous value if any.
// The loaded result reports whether the key was present.
func (m *Map[Key, Value]) LoadAndDelete(key Key) (value Value, loaded bool) {
var v any
if v, loaded = m.m.LoadAndDelete(key); loaded && v != nil {
Expand All @@ -38,10 +47,13 @@ func (m *Map[Key, Value]) LoadAndDelete(key Key) (value Value, loaded bool) {
return
}

// Delete deletes the value for a key.
func (m *Map[Key, Value]) Delete(key Key) {
m.m.Delete(key)
}

// Swap swaps the value for a key and returns the previous value if any.
// The loaded result reports whether the key was present.
func (m *Map[Key, Value]) Swap(key Key, value Value) (previous Value, loaded bool) {
var v any
if v, loaded = m.m.Swap(key, value); loaded && v != nil {
Expand All @@ -50,14 +62,33 @@ func (m *Map[Key, Value]) Swap(key Key, value Value) (previous Value, loaded boo
return
}

// CompareAndSwap swaps the old and new values for key
// if the value stored in the map is equal to old.
// The old value must be of a comparable type.
func (m *Map[Key, Value]) CompareAndSwap(key Key, old Value, new Value) bool {
return m.m.CompareAndSwap(key, old, new)
}

// CompareAndDelete deletes the entry for key if its value is equal to old.
// The old value must be of a comparable type.
//
// If there is no current value for key in the map, CompareAndDelete
// returns false (even if the old value is the nil interface value).
func (m *Map[Key, Value]) CompareAndDelete(key Key, old Value) (deleted bool) {
return m.m.CompareAndDelete(key, old)
}

// Range calls f sequentially for each key and value present in the map.
// If f returns false, range stops the iteration.
//
// Range does not necessarily correspond to any consistent snapshot of the Map's
// contents: no key will be visited more than once, but if the value for any key
// is stored or deleted concurrently (including by f), Range may reflect any
// mapping for that key from any point during the Range call. Range does not
// block other methods on the receiver; even f itself may call any method on m.
//
// Range may be O(N) with the number of elements in the map even if f returns
// false after a constant number of calls.
func (m *Map[Key, Value]) Range(f func(Key, Value) bool) {
m.m.Range(func(key, value any) bool {
var k Key
Expand Down

0 comments on commit 1fb478e

Please sign in to comment.