summaryrefslogtreecommitdiffhomepage
path: root/pkg/ilist
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/ilist')
-rw-r--r--pkg/ilist/BUILD66
-rw-r--r--pkg/ilist/list.go171
-rw-r--r--pkg/ilist/list_test.go233
3 files changed, 470 insertions, 0 deletions
diff --git a/pkg/ilist/BUILD b/pkg/ilist/BUILD
new file mode 100644
index 000000000..937ac3876
--- /dev/null
+++ b/pkg/ilist/BUILD
@@ -0,0 +1,66 @@
+package(licenses = ["notice"]) # BSD
+
+load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
+load("//tools/go_generics:defs.bzl", "go_template", "go_template_instance")
+load("//tools/go_stateify:defs.bzl", "go_stateify")
+
+go_stateify(
+ name = "list_state",
+ srcs = [
+ "interface_list.go",
+ ],
+ out = "interface_list_state.go",
+ package = "ilist",
+)
+
+go_library(
+ name = "ilist",
+ srcs = [
+ "interface_list.go",
+ "interface_list_state.go",
+ ],
+ importpath = "gvisor.googlesource.com/gvisor/pkg/ilist",
+ visibility = ["//visibility:public"],
+ deps = [
+ "//pkg/state",
+ ],
+)
+
+go_template_instance(
+ name = "interface_list",
+ out = "interface_list.go",
+ package = "ilist",
+ template = ":generic_list",
+ types = {},
+)
+
+# This list is used for benchmarking.
+go_template_instance(
+ name = "test_list",
+ out = "test_list.go",
+ package = "ilist",
+ prefix = "direct",
+ template = ":generic_list",
+ types = {
+ "Linker": "*direct",
+ },
+)
+
+go_test(
+ name = "list_test",
+ size = "small",
+ srcs = [
+ "list_test.go",
+ "test_list.go",
+ ],
+ embed = [":ilist"],
+)
+
+go_template(
+ name = "generic_list",
+ srcs = [
+ "list.go",
+ ],
+ opt_types = ["Linker"],
+ visibility = ["//visibility:public"],
+)
diff --git a/pkg/ilist/list.go b/pkg/ilist/list.go
new file mode 100644
index 000000000..739575f17
--- /dev/null
+++ b/pkg/ilist/list.go
@@ -0,0 +1,171 @@
+// Copyright 2016 The Netstack 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 ilist provides the implementation of intrusive linked lists.
+package ilist
+
+// Linker is the interface that objects must implement if they want to be added
+// to and/or removed from List objects.
+//
+// N.B. When substituted in a template instantiation, Linker doesn't need to
+// be an interface, and in most cases won't be.
+type Linker interface {
+ Next() Linker
+ Prev() Linker
+ SetNext(Linker)
+ SetPrev(Linker)
+}
+
+// List is an intrusive list. Entries can be added to or removed from the list
+// in O(1) time and with no additional memory allocations.
+//
+// The zero value for List is an empty list ready to use.
+//
+// To iterate over a list (where l is a List):
+// for e := l.Front(); e != nil; e = e.Next() {
+// // do something with e.
+// }
+type List struct {
+ head Linker
+ tail Linker
+}
+
+// Reset resets list l to the empty state.
+func (l *List) Reset() {
+ l.head = nil
+ l.tail = nil
+}
+
+// Empty returns true iff the list is empty.
+func (l *List) Empty() bool {
+ return l.head == nil
+}
+
+// Front returns the first element of list l or nil.
+func (l *List) Front() Linker {
+ return l.head
+}
+
+// Back returns the last element of list l or nil.
+func (l *List) Back() Linker {
+ return l.tail
+}
+
+// PushFront inserts the element e at the front of list l.
+func (l *List) PushFront(e Linker) {
+ e.SetNext(l.head)
+ e.SetPrev(nil)
+
+ if l.head != nil {
+ l.head.SetPrev(e)
+ } else {
+ l.tail = e
+ }
+
+ l.head = e
+}
+
+// PushBack inserts the element e at the back of list l.
+func (l *List) PushBack(e Linker) {
+ e.SetNext(nil)
+ e.SetPrev(l.tail)
+
+ if l.tail != nil {
+ l.tail.SetNext(e)
+ } else {
+ l.head = e
+ }
+
+ l.tail = e
+}
+
+// PushBackList inserts list m at the end of list l, emptying m.
+func (l *List) PushBackList(m *List) {
+ if l.head == nil {
+ l.head = m.head
+ l.tail = m.tail
+ } else if m.head != nil {
+ l.tail.SetNext(m.head)
+ m.head.SetPrev(l.tail)
+
+ l.tail = m.tail
+ }
+
+ m.head = nil
+ m.tail = nil
+}
+
+// InsertAfter inserts e after b.
+func (l *List) InsertAfter(b, e Linker) {
+ a := b.Next()
+ e.SetNext(a)
+ e.SetPrev(b)
+ b.SetNext(e)
+
+ if a != nil {
+ a.SetPrev(e)
+ } else {
+ l.tail = e
+ }
+}
+
+// InsertBefore inserts e before a.
+func (l *List) InsertBefore(a, e Linker) {
+ b := a.Prev()
+ e.SetNext(a)
+ e.SetPrev(b)
+ a.SetPrev(e)
+
+ if b != nil {
+ b.SetNext(e)
+ } else {
+ l.head = e
+ }
+}
+
+// Remove removes e from l.
+func (l *List) Remove(e Linker) {
+ prev := e.Prev()
+ next := e.Next()
+
+ if prev != nil {
+ prev.SetNext(next)
+ } else {
+ l.head = next
+ }
+
+ if next != nil {
+ next.SetPrev(prev)
+ } else {
+ l.tail = prev
+ }
+}
+
+// Entry is a default implementation of Linker. Users can add anonymous fields
+// of this type to their structs to make them automatically implement the
+// methods needed by List.
+type Entry struct {
+ next Linker
+ prev Linker
+}
+
+// Next returns the entry that follows e in the list.
+func (e *Entry) Next() Linker {
+ return e.next
+}
+
+// Prev returns the entry that precedes e in the list.
+func (e *Entry) Prev() Linker {
+ return e.prev
+}
+
+// SetNext assigns 'entry' as the entry that follows e in the list.
+func (e *Entry) SetNext(entry Linker) {
+ e.next = entry
+}
+
+// SetPrev assigns 'entry' as the entry that precedes e in the list.
+func (e *Entry) SetPrev(entry Linker) {
+ e.prev = entry
+}
diff --git a/pkg/ilist/list_test.go b/pkg/ilist/list_test.go
new file mode 100644
index 000000000..7f2b90e0c
--- /dev/null
+++ b/pkg/ilist/list_test.go
@@ -0,0 +1,233 @@
+// Copyright 2016 The Netstack 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 ilist
+
+import (
+ "runtime"
+ "testing"
+)
+
+type testEntry struct {
+ Entry
+ value int
+}
+
+type direct struct {
+ directEntry
+ value int
+}
+
+func verifyEquality(t *testing.T, entries []testEntry, l *List) {
+ i := 0
+ for it := l.Front(); it != nil; it = it.Next() {
+ e := it.(*testEntry)
+ if e != &entries[i] {
+ _, file, line, _ := runtime.Caller(1)
+ t.Errorf("Wrong entry at index (%s:%d): %v", file, line, i)
+ return
+ }
+ i++
+ }
+
+ if i != len(entries) {
+ _, file, line, _ := runtime.Caller(1)
+ t.Errorf("Wrong number of entries (%s:%d)", file, line)
+ return
+ }
+
+ i = 0
+ for it := l.Back(); it != nil; it = it.Prev() {
+ e := it.(*testEntry)
+ if e != &entries[len(entries)-1-i] {
+ _, file, line, _ := runtime.Caller(1)
+ t.Errorf("Wrong entry at index (%s:%d): %v", file, line, i)
+ return
+ }
+ i++
+ }
+
+ if i != len(entries) {
+ _, file, line, _ := runtime.Caller(1)
+ t.Errorf("Wrong number of entries (%s:%d)", file, line)
+ return
+ }
+}
+
+func TestZeroEmpty(t *testing.T) {
+ var l List
+ if l.Front() != nil {
+ t.Error("Front is non-nil")
+ }
+ if l.Back() != nil {
+ t.Error("Back is non-nil")
+ }
+}
+
+func TestPushBack(t *testing.T) {
+ var l List
+
+ // Test single entry insertion.
+ var entry testEntry
+ l.PushBack(&entry)
+
+ e := l.Front().(*testEntry)
+ if e != &entry {
+ t.Error("Wrong entry returned")
+ }
+
+ // Test inserting 100 entries.
+ l.Reset()
+ var entries [100]testEntry
+ for i := range entries {
+ l.PushBack(&entries[i])
+ }
+
+ verifyEquality(t, entries[:], &l)
+}
+
+func TestPushFront(t *testing.T) {
+ var l List
+
+ // Test single entry insertion.
+ var entry testEntry
+ l.PushFront(&entry)
+
+ e := l.Front().(*testEntry)
+ if e != &entry {
+ t.Error("Wrong entry returned")
+ }
+
+ // Test inserting 100 entries.
+ l.Reset()
+ var entries [100]testEntry
+ for i := range entries {
+ l.PushFront(&entries[len(entries)-1-i])
+ }
+
+ verifyEquality(t, entries[:], &l)
+}
+
+func TestRemove(t *testing.T) {
+ // Remove entry from single-element list.
+ var l List
+ var entry testEntry
+ l.PushBack(&entry)
+ l.Remove(&entry)
+ if l.Front() != nil {
+ t.Error("List is empty")
+ }
+
+ var entries [100]testEntry
+
+ // Remove single element from lists of lengths 2 to 101.
+ for n := 1; n <= len(entries); n++ {
+ for extra := 0; extra <= n; extra++ {
+ l.Reset()
+ for i := 0; i < n; i++ {
+ if extra == i {
+ l.PushBack(&entry)
+ }
+ l.PushBack(&entries[i])
+ }
+ if extra == n {
+ l.PushBack(&entry)
+ }
+
+ l.Remove(&entry)
+ verifyEquality(t, entries[:n], &l)
+ }
+ }
+}
+
+func TestReset(t *testing.T) {
+ var l List
+
+ // Resetting list of one element.
+ l.PushBack(&testEntry{})
+ if l.Front() == nil {
+ t.Error("List is empty")
+ }
+
+ l.Reset()
+ if l.Front() != nil {
+ t.Error("List is not empty")
+ }
+
+ // Resetting list of 10 elements.
+ for i := 0; i < 10; i++ {
+ l.PushBack(&testEntry{})
+ }
+
+ if l.Front() == nil {
+ t.Error("List is empty")
+ }
+
+ l.Reset()
+ if l.Front() != nil {
+ t.Error("List is not empty")
+ }
+
+ // Resetting empty list.
+ l.Reset()
+ if l.Front() != nil {
+ t.Error("List is not empty")
+ }
+}
+
+func BenchmarkIterateForward(b *testing.B) {
+ var l List
+ for i := 0; i < 1000000; i++ {
+ l.PushBack(&testEntry{value: i})
+ }
+
+ for i := b.N; i > 0; i-- {
+ tmp := 0
+ for e := l.Front(); e != nil; e = e.Next() {
+ tmp += e.(*testEntry).value
+ }
+ }
+}
+
+func BenchmarkIterateBackward(b *testing.B) {
+ var l List
+ for i := 0; i < 1000000; i++ {
+ l.PushBack(&testEntry{value: i})
+ }
+
+ for i := b.N; i > 0; i-- {
+ tmp := 0
+ for e := l.Back(); e != nil; e = e.Prev() {
+ tmp += e.(*testEntry).value
+ }
+ }
+}
+
+func BenchmarkDirectIterateForward(b *testing.B) {
+ var l directList
+ for i := 0; i < 1000000; i++ {
+ l.PushBack(&direct{value: i})
+ }
+
+ for i := b.N; i > 0; i-- {
+ tmp := 0
+ for e := l.Front(); e != nil; e = e.Next() {
+ tmp += e.value
+ }
+ }
+}
+
+func BenchmarkDirectIterateBackward(b *testing.B) {
+ var l directList
+ for i := 0; i < 1000000; i++ {
+ l.PushBack(&direct{value: i})
+ }
+
+ for i := b.N; i > 0; i-- {
+ tmp := 0
+ for e := l.Back(); e != nil; e = e.Prev() {
+ tmp += e.value
+ }
+ }
+}