Files
redis-go/datastruct/list/quicklist.go

391 lines
8.2 KiB
Go

package list
import "container/list"
// pageSize must be even
const pageSize = 1024
// QuickList is a linked list of page (which type is []interface{})
// QuickList has better performance than LinkedList of Add, Range and memory usage
type QuickList struct {
data *list.List // list of []interface{}
size int
}
// iterator of QuickList, move between [-1, ql.Len()]
type iterator struct {
node *list.Element
offset int
ql *QuickList
}
func NewQuickList() *QuickList {
l := &QuickList{
data: list.New(),
}
return l
}
// Add adds value to the tail
func (ql *QuickList) Add(val interface{}) {
ql.size++
if ql.data.Len() == 0 { // empty list
page := make([]interface{}, 0, pageSize)
page = append(page, val)
ql.data.PushBack(page)
return
}
// assert list.data.Back() != nil
backNode := ql.data.Back()
backPage := backNode.Value.([]interface{})
if len(backPage) == cap(backPage) { // full page, create new page
page := make([]interface{}, 0, pageSize)
page = append(page, val)
ql.data.PushBack(page)
return
}
// append into page
backPage = append(backPage, val)
backNode.Value = backPage
}
// find returns page and in-page-offset of given index
func (ql *QuickList) find(index int) *iterator {
if ql == nil {
panic("list is nil")
}
if index < 0 || index >= ql.size {
panic("index out of bound")
}
var n *list.Element
var page []interface{}
var pageBeg int
if index < ql.size/2 {
// search from front
n = ql.data.Front()
pageBeg = 0
for {
// assert: n != nil
page = n.Value.([]interface{})
if pageBeg+len(page) > index {
break
}
pageBeg += len(page)
n = n.Next()
}
} else {
// search from back
n = ql.data.Back()
pageBeg = ql.size
for {
page = n.Value.([]interface{})
pageBeg -= len(page)
if pageBeg <= index {
break
}
n = n.Prev()
}
}
pageOffset := index - pageBeg
return &iterator{
node: n,
offset: pageOffset,
ql: ql,
}
}
func (iter *iterator) get() interface{} {
return iter.page()[iter.offset]
}
func (iter *iterator) page() []interface{} {
return iter.node.Value.([]interface{})
}
// next returns whether iter is in bound
func (iter *iterator) next() bool {
page := iter.page()
if iter.offset < len(page)-1 {
iter.offset++
return true
}
// move to next page
if iter.node == iter.ql.data.Back() {
// already at last node
iter.offset = len(page)
return false
}
iter.offset = 0
iter.node = iter.node.Next()
return true
}
// prev returns whether iter is in bound
func (iter *iterator) prev() bool {
if iter.offset > 0 {
iter.offset--
return true
}
// move to prev page
if iter.node == iter.ql.data.Front() {
// already at first page
iter.offset = -1
return false
}
iter.node = iter.node.Prev()
prevPage := iter.node.Value.([]interface{})
iter.offset = len(prevPage) - 1
return true
}
func (iter *iterator) atEnd() bool {
if iter.ql.data.Len() == 0 {
return true
}
if iter.node != iter.ql.data.Back() {
return false
}
page := iter.page()
return iter.offset == len(page)
}
func (iter *iterator) atBegin() bool {
if iter.ql.data.Len() == 0 {
return true
}
if iter.node != iter.ql.data.Front() {
return false
}
return iter.offset == -1
}
// Get returns value at the given index
func (ql *QuickList) Get(index int) (val interface{}) {
iter := ql.find(index)
return iter.get()
}
func (iter *iterator) set(val interface{}) {
page := iter.page()
page[iter.offset] = val
}
// Set updates value at the given index, the index should between [0, list.size]
func (ql *QuickList) Set(index int, val interface{}) {
iter := ql.find(index)
iter.set(val)
}
func (ql *QuickList) Insert(index int, val interface{}) {
if index == ql.size { // insert at
ql.Add(val)
return
}
iter := ql.find(index)
page := iter.node.Value.([]interface{})
if len(page) < pageSize {
// insert into not full page
page = append(page[:iter.offset+1], page[iter.offset:]...)
page[iter.offset] = val
iter.node.Value = page
ql.size++
return
}
// insert into a full page may cause memory copy, so we split a full page into two half pages
var nextPage []interface{}
nextPage = append(nextPage, page[pageSize/2:]...) // pageSize must be even
page = page[:pageSize/2]
if iter.offset < len(page) {
page = append(page[:iter.offset+1], page[iter.offset:]...)
page[iter.offset] = val
} else {
i := iter.offset - pageSize/2
nextPage = append(nextPage[:i+1], nextPage[i:]...)
nextPage[i] = val
}
// store current page and next page
iter.node.Value = page
ql.data.InsertAfter(nextPage, iter.node)
ql.size++
}
func (iter *iterator) remove() interface{} {
page := iter.page()
val := page[iter.offset]
page = append(page[:iter.offset], page[iter.offset+1:]...)
if len(page) > 0 {
// page is not empty, update iter.offset only
iter.node.Value = page
if iter.offset == len(page) {
// removed page[-1], node should move to next page
if iter.node != iter.ql.data.Back() {
iter.node = iter.node.Next()
iter.offset = 0
}
// else: assert iter.atEnd() == true
}
} else {
// page is empty, update iter.node and iter.offset
if iter.node == iter.ql.data.Back() {
// move iter so iter.atEnd() can be true
if prevNode := iter.node.Prev(); prevNode != nil {
iter.ql.data.Remove(iter.node)
iter.node = prevNode
iter.offset = len(prevNode.Value.([]interface{}))
} else {
// removed last element, ql is empty now
iter.ql.data.Remove(iter.node)
iter.node = nil
iter.offset = 0
}
} else {
nextNode := iter.node.Next()
iter.ql.data.Remove(iter.node)
iter.node = nextNode
iter.offset = 0
}
}
iter.ql.size--
return val
}
// Remove removes value at the given index
func (ql *QuickList) Remove(index int) interface{} {
iter := ql.find(index)
return iter.remove()
}
// Len returns the number of elements in list
func (ql *QuickList) Len() int {
return ql.size
}
// RemoveLast removes the last element and returns its value
func (ql *QuickList) RemoveLast() interface{} {
if ql.Len() == 0 {
return nil
}
ql.size--
lastNode := ql.data.Back()
lastPage := lastNode.Value.([]interface{})
if len(lastPage) == 1 {
ql.data.Remove(lastNode)
return lastPage[0]
}
val := lastPage[len(lastPage)-1]
lastPage = lastPage[:len(lastPage)-1]
lastNode.Value = lastPage
return val
}
// RemoveAllByVal removes all elements with the given val
func (ql *QuickList) RemoveAllByVal(expected Expected) int {
iter := ql.find(0)
removed := 0
for !iter.atEnd() {
if expected(iter.get()) {
iter.remove()
removed++
} else {
iter.next()
}
}
return removed
}
// RemoveByVal removes at most `count` values of the specified value in this list
// scan from left to right
func (ql *QuickList) RemoveByVal(expected Expected, count int) int {
if ql.size == 0 {
return 0
}
iter := ql.find(0)
removed := 0
for !iter.atEnd() {
if expected(iter.get()) {
iter.remove()
removed++
if removed == count {
break
}
} else {
iter.next()
}
}
return removed
}
func (ql *QuickList) ReverseRemoveByVal(expected Expected, count int) int {
if ql.size == 0 {
return 0
}
iter := ql.find(ql.size - 1)
removed := 0
for !iter.atBegin() {
if expected(iter.get()) {
iter.remove()
removed++
if removed == count {
break
}
}
iter.prev()
}
return removed
}
// ForEach visits each element in the list
// if the consumer returns false, the loop will be break
func (ql *QuickList) ForEach(consumer Consumer) {
if ql == nil {
panic("list is nil")
}
if ql.Len() == 0 {
return
}
iter := ql.find(0)
i := 0
for {
goNext := consumer(i, iter.get())
if !goNext {
break
}
i++
if !iter.next() {
break
}
}
}
func (ql *QuickList) Contains(expected Expected) bool {
contains := false
ql.ForEach(func(i int, actual interface{}) bool {
if expected(actual) {
contains = true
return false
}
return true
})
return contains
}
// Range returns elements which index within [start, stop)
func (ql *QuickList) Range(start int, stop int) []interface{} {
if start < 0 || start >= ql.Len() {
panic("`start` out of range")
}
if stop < start || stop > ql.Len() {
panic("`stop` out of range")
}
sliceSize := stop - start
slice := make([]interface{}, 0, sliceSize)
iter := ql.find(start)
i := 0
for i < sliceSize {
slice = append(slice, iter.get())
iter.next()
i++
}
return slice
}