diff options
Diffstat (limited to 'pkg/ilist')
-rw-r--r-- | pkg/ilist/BUILD | 66 | ||||
-rw-r--r-- | pkg/ilist/list.go | 171 | ||||
-rw-r--r-- | pkg/ilist/list_test.go | 233 |
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 + } + } +} |