github.com/m3db/m3@v1.5.0/src/x/generics/list/list.go (about)

     1  // Copyright (c) 2019 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  // This is a doubly linked list with its internal elements pooled. This allows
    22  // the list to be reused as a shared resource to prevent unnecessary
    23  // allocations/GC.
    24  
    25  // This implementation is a modification from Go's container/list source code.
    26  // Here is its license:
    27  
    28  // Copyright (c) 2009 The Go Authors. All rights reserved.
    29  
    30  // Redistribution and use in source and binary forms, with or without
    31  // modification, are permitted provided that the following conditions are
    32  // met:
    33  
    34  //    * Redistributions of source code must retain the above copyright
    35  // notice, this list of conditions and the following disclaimer.
    36  //    * Redistributions in binary form must reproduce the above
    37  // copyright notice, this list of conditions and the following disclaimer
    38  // in the documentation and/or other materials provided with the
    39  // distribution.
    40  //    * Neither the name of Google Inc. nor the names of its
    41  // contributors may be used to endorse or promote products derived from
    42  // this software without specific prior written permission.
    43  
    44  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    45  // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    46  // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    47  // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
    48  // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    49  // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    50  // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    51  // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    52  // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    53  // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    54  // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    55  
    56  package list
    57  
    58  import (
    59  	"sync"
    60  
    61  	"github.com/m3db/m3/src/x/pool"
    62  
    63  	"github.com/mauricelam/genny/generic"
    64  )
    65  
    66  // ValueType is the generic element type for use with the list.
    67  type ValueType generic.Type
    68  
    69  // Element is an element of a linked list.
    70  type Element struct {
    71  	// Next and previous pointers in the doubly-linked list of elements.
    72  	// To simplify the implementation, internally a list l is implemented
    73  	// as a ring, such that &l.root is both the next element of the last
    74  	// list element (l.Back()) and the previous element of the first list
    75  	// element (l.Front()).
    76  	next, prev *Element
    77  
    78  	// The list to which this element belongs.
    79  	list *List
    80  
    81  	// The value stored with this element.
    82  	Value ValueType
    83  }
    84  
    85  // Next returns the next list element or nil.
    86  func (e *Element) Next() *Element {
    87  	if p := e.next; e.list != nil && p != &e.list.root {
    88  		return p
    89  	}
    90  	return nil
    91  }
    92  
    93  // Prev returns the previous list element or nil.
    94  func (e *Element) Prev() *Element {
    95  	if p := e.prev; e.list != nil && p != &e.list.root {
    96  		return p
    97  	}
    98  	return nil
    99  }
   100  
   101  // List represents a doubly linked list.
   102  // The zero value is an empty, ready to use list.
   103  type List struct {
   104  	root Element // sentinel list element, only &root, root.prev, and root.next are used
   105  	len  int     // current list length excluding (this) sentinel element
   106  	Pool *ElementPool
   107  }
   108  
   109  // Init initializes or clears list l.
   110  func (l *List) Init() *List {
   111  	l.root.next = &l.root
   112  	l.root.prev = &l.root
   113  	l.len = 0
   114  	if l.Pool == nil {
   115  		// Use a static pool at least, otherwise each time
   116  		// we create a list with no pool we create a wholly
   117  		// new pool of finalizeables (4096 of them).
   118  		defaultElementPoolOnce.Do(initElementPool)
   119  		l.Pool = defaultElementPool
   120  	}
   121  	return l
   122  }
   123  
   124  var (
   125  	defaultElementPoolOnce sync.Once
   126  	defaultElementPool     *ElementPool
   127  )
   128  
   129  // define as a static method so lambda alloc not required
   130  // when passing function pointer to sync.Once.Do.
   131  func initElementPool() {
   132  	defaultElementPool = newElementPool(nil)
   133  }
   134  
   135  // newList returns an initialized list.
   136  func newList(p *ElementPool) *List {
   137  	l := &List{Pool: p}
   138  	return l.Init()
   139  }
   140  
   141  // Len returns the number of elements of list l.
   142  // The complexity is O(1).
   143  func (l *List) Len() int { return l.len }
   144  
   145  // Front returns the first element of list l or nil if the list is empty.
   146  func (l *List) Front() *Element {
   147  	if l.len == 0 {
   148  		return nil
   149  	}
   150  	return l.root.next
   151  }
   152  
   153  // Back returns the last element of list l or nil if the list is empty.
   154  func (l *List) Back() *Element {
   155  	if l.len == 0 {
   156  		return nil
   157  	}
   158  	return l.root.prev
   159  }
   160  
   161  // lazyInit lazily initializes a zero List value.
   162  func (l *List) lazyInit() {
   163  	if l.root.next == nil {
   164  		l.Init()
   165  	}
   166  }
   167  
   168  // insert inserts e after at, increments l.len, and returns e.
   169  func (l *List) insert(e, at *Element) *Element {
   170  	n := at.next
   171  	at.next = e
   172  	e.prev = at
   173  	e.next = n
   174  	n.prev = e
   175  	e.list = l
   176  	l.len++
   177  	return e
   178  }
   179  
   180  // insertValue is a convenience wrapper for inserting using the list's pool.
   181  func (l *List) insertValue(v ValueType, at *Element) *Element {
   182  	e := l.Pool.get()
   183  	e.Value = v
   184  	return l.insert(e, at)
   185  }
   186  
   187  // remove removes e from its list, decrements l.len, and returns e.
   188  func (l *List) remove(e *Element) *Element {
   189  	e.prev.next = e.next
   190  	e.next.prev = e.prev
   191  	e.next = nil // avoid memory leaks
   192  	e.prev = nil // avoid memory leaks
   193  	e.list = nil
   194  	l.len--
   195  	return e
   196  }
   197  
   198  // Remove removes e from l if e is an element of list l.
   199  // It returns the element value e.Value.
   200  // The element must not be nil.
   201  func (l *List) Remove(e *Element) ValueType {
   202  	// read the value before returning to the pool to avoid a data race with another goroutine getting access to the
   203  	// list after it has been put back into the pool.
   204  	v := e.Value
   205  	if e.list == l {
   206  		// if e.list == l, l must have been initialized when e was inserted
   207  		// in l or l == nil (e is a zero Element) and l.remove will crash.
   208  		l.remove(e)
   209  		l.Pool.put(e)
   210  	}
   211  	return v
   212  }
   213  
   214  // PushFront inserts a new element e with value v at the front of list l and returns e.
   215  func (l *List) PushFront(v ValueType) *Element {
   216  	l.lazyInit()
   217  	return l.insertValue(v, &l.root)
   218  }
   219  
   220  // PushBack inserts a new element e with value v at the back of list l and returns e.
   221  func (l *List) PushBack(v ValueType) *Element {
   222  	l.lazyInit()
   223  	return l.insertValue(v, l.root.prev)
   224  }
   225  
   226  // InsertBefore inserts a new element e with value v immediately before mark and returns e.
   227  // If mark is not an element of l, the list is not modified.
   228  // The mark must not be nil.
   229  func (l *List) InsertBefore(v ValueType, mark *Element) *Element {
   230  	if mark.list != l {
   231  		return nil
   232  	}
   233  	// see comment in List.Remove about initialization of l
   234  	return l.insertValue(v, mark.prev)
   235  }
   236  
   237  // InsertAfter inserts a new element e with value v immediately after mark and returns e.
   238  // If mark is not an element of l, the list is not modified.
   239  // The mark must not be nil.
   240  func (l *List) InsertAfter(v ValueType, mark *Element) *Element {
   241  	if mark.list != l {
   242  		return nil
   243  	}
   244  	// see comment in List.Remove about initialization of l
   245  	return l.insertValue(v, mark)
   246  }
   247  
   248  // MoveToFront moves element e to the front of list l.
   249  // If e is not an element of l, the list is not modified.
   250  // The element must not be nil.
   251  func (l *List) MoveToFront(e *Element) {
   252  	if e.list != l || l.root.next == e {
   253  		return
   254  	}
   255  	// see comment in List.Remove about initialization of l
   256  	l.insert(l.remove(e), &l.root)
   257  }
   258  
   259  // MoveToBack moves element e to the back of list l.
   260  // If e is not an element of l, the list is not modified.
   261  // The element must not be nil.
   262  func (l *List) MoveToBack(e *Element) {
   263  	if e.list != l || l.root.prev == e {
   264  		return
   265  	}
   266  	// see comment in List.Remove about initialization of l
   267  	l.insert(l.remove(e), l.root.prev)
   268  }
   269  
   270  // MoveBefore moves element e to its new position before mark.
   271  // If e or mark is not an element of l, or e == mark, the list is not modified.
   272  // The element and mark must not be nil.
   273  func (l *List) MoveBefore(e, mark *Element) {
   274  	if e.list != l || e == mark || mark.list != l {
   275  		return
   276  	}
   277  	l.insert(l.remove(e), mark.prev)
   278  }
   279  
   280  // MoveAfter moves element e to its new position after mark.
   281  // If e or mark is not an element of l, or e == mark, the list is not modified.
   282  // The element and mark must not be nil.
   283  func (l *List) MoveAfter(e, mark *Element) {
   284  	if e.list != l || e == mark || mark.list != l {
   285  		return
   286  	}
   287  	l.insert(l.remove(e), mark)
   288  }
   289  
   290  // PushBackList inserts a copy of an other list at the back of list l.
   291  // The lists l and other may be the same. They must not be nil.
   292  func (l *List) PushBackList(other *List) {
   293  	l.lazyInit()
   294  	for i, e := other.Len(), other.Front(); i > 0; i, e = i-1, e.Next() {
   295  		l.insertValue(e.Value, l.root.prev)
   296  	}
   297  }
   298  
   299  // PushFrontList inserts a copy of an other list at the front of list l.
   300  // The lists l and other may be the same. They must not be nil.
   301  func (l *List) PushFrontList(other *List) {
   302  	l.lazyInit()
   303  	for i, e := other.Len(), other.Back(); i > 0; i, e = i-1, e.Prev() {
   304  		l.insertValue(e.Value, &l.root)
   305  	}
   306  }
   307  
   308  // Reset resets list l for reuse and puts all elements back into the pool.
   309  func (l *List) Reset() {
   310  	for e := l.Back(); e != nil; e = l.Back() {
   311  		l.Remove(e)
   312  	}
   313  }
   314  
   315  // ElementPool provides a pool for Elements.
   316  type ElementPool struct {
   317  	pool pool.ObjectPool
   318  }
   319  
   320  // Get gets an Element from the pool.
   321  func (p *ElementPool) get() *Element {
   322  	return p.pool.Get().(*Element)
   323  }
   324  
   325  // Put puts an Element back into the pool.
   326  func (p *ElementPool) put(e *Element) {
   327  	p.pool.Put(e)
   328  }
   329  
   330  // newElementPool creates a new generic ElementPool.
   331  func newElementPool(opts pool.ObjectPoolOptions) *ElementPool {
   332  	p := &ElementPool{pool: pool.NewObjectPool(opts)}
   333  	p.pool.Init(func() interface{} {
   334  		return &Element{}
   335  	})
   336  	return p
   337  }