1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
|
// Copyright 2015 Canonical Ltd.
// Licensed under the LGPLv3, see LICENCE file for details.
package deque
import "container/list"
// Deque implements an efficient double-ended queue.
//
// Internally it is composed of a doubly-linked list (list.List) of
// blocks. Each block is a slice that holds 0 to blockLen items. The
// Deque starts with one block. Blocks are added to the front and back
// when the edge blocks fill up as items are pushed onto the
// deque. Edge blocks are removed when blocks become empty as items
// are popped off the Deque.
//
// Only the front and back blocks may contain less than blockLen items.
// The first element in the Deque is d.blocks.Front().Value[d.frontIdx].
// The last element in the Deque is d.blocks.Back().Value[d.backIdx].
//
// This approach is more efficient than using a standard doubly-linked
// list for a queue because memory allocation is only required every
// blockLen items, instead of for each pushed item. Conversely, fewer
// memory deallocations are required when popping items. Bookkeeping
// overhead per item is also reduced.
type Deque struct {
maxLen int
blocks list.List
frontIdx, backIdx int
len int
}
// blockLen can be any value above 1. Raising the blockLen decreases
// the average number of memory allocations per item, but increases
// the amount of memory "wasted". Raising blockLen also doesn't
// necessarily make Deque operations faster. 64 is used by Python's
// deque and seems to be a sweet spot on the author's machine too.
const blockLen = 64
const blockCenter = (blockLen - 1) / 2
type blockT []interface{}
// New returns a new Deque instance.
func New() *Deque {
return NewWithMaxLen(0)
}
// New returns a new Deque instance which is limited to a certain
// length. Pushes which cause the length to exceed the specified size
// will cause an item to be dropped from the opposing side.
//
// A maxLen of 0 means that there is no maximum length limit in place.
func NewWithMaxLen(maxLen int) *Deque {
d := Deque{maxLen: maxLen}
d.blocks.PushBack(newBlock())
d.recenter()
return &d
}
func newBlock() blockT {
return make(blockT, blockLen)
}
func (d *Deque) recenter() {
// The indexes start crossed at the middle of the block so that
// the first push on either side has both indexes pointing at the
// first item.
d.frontIdx = blockCenter + 1
d.backIdx = blockCenter
}
// Len returns the number of items stored in the queue.
func (d *Deque) Len() int {
return d.len
}
// PushBack adds an item to the back of the queue.
func (d *Deque) PushBack(item interface{}) {
var block blockT
if d.backIdx == blockLen-1 {
// The current back block is full so add another.
block = newBlock()
d.blocks.PushBack(block)
d.backIdx = -1
} else {
block = d.blocks.Back().Value.(blockT)
}
d.backIdx++
block[d.backIdx] = item
d.len++
if d.maxLen > 0 && d.len > d.maxLen {
d.PopFront()
}
}
// PushFront adds an item to the front of the queue.
func (d *Deque) PushFront(item interface{}) {
var block blockT
if d.frontIdx == 0 {
// The current front block is full so add another.
block = newBlock()
d.blocks.PushFront(block)
d.frontIdx = blockLen
} else {
block = d.blocks.Front().Value.(blockT)
}
d.frontIdx--
block[d.frontIdx] = item
d.len++
if d.maxLen > 0 && d.len > d.maxLen {
d.PopBack()
}
}
// PopBack removes an item from the back of the queue and returns
// it. The returned flag is true unless there were no items left in
// the queue.
func (d *Deque) PopBack() (interface{}, bool) {
if d.len < 1 {
return nil, false
}
elem := d.blocks.Back()
block := elem.Value.(blockT)
item := block[d.backIdx]
block[d.backIdx] = nil
d.backIdx--
d.len--
if d.backIdx == -1 {
// The back block is now empty.
if d.len == 0 {
d.recenter() // Deque is empty so reset.
} else {
d.blocks.Remove(elem)
d.backIdx = blockLen - 1
}
}
return item, true
}
// PopFront removes an item from the front of the queue and returns
// it. The returned flag is true unless there were no items left in
// the queue.
func (d *Deque) PopFront() (interface{}, bool) {
if d.len < 1 {
return nil, false
}
elem := d.blocks.Front()
block := elem.Value.(blockT)
item := block[d.frontIdx]
block[d.frontIdx] = nil
d.frontIdx++
d.len--
if d.frontIdx == blockLen {
// The front block is now empty.
if d.len == 0 {
d.recenter() // Deque is empty so reset.
} else {
d.blocks.Remove(elem)
d.frontIdx = 0
}
}
return item, true
}
|