|
|
@@ -13,6 +13,13 @@ import (
|
|
|
"github.com/syndtr/goleveldb/leveldb/util"
|
|
|
)
|
|
|
|
|
|
+// The LLRB implementation were taken from https://github.com/petar/GoLLRB,
|
|
|
+// which conatins the following header:
|
|
|
+//
|
|
|
+// Copyright 2010 Petar Maymounkov. All rights reserved.
|
|
|
+// Use of this source code is governed by a BSD-style
|
|
|
+// license that can be found in the LICENSE file.
|
|
|
+
|
|
|
// lruCache represent a LRU cache state.
|
|
|
type lruCache struct {
|
|
|
mu sync.Mutex
|
|
|
@@ -74,11 +81,7 @@ func (c *lruCache) GetNamespace(id uint64) Namespace {
|
|
|
return ns
|
|
|
}
|
|
|
|
|
|
- ns := &lruNs{
|
|
|
- lru: c,
|
|
|
- id: id,
|
|
|
- table: make(map[uint64]*lruNode),
|
|
|
- }
|
|
|
+ ns := &lruNs{lru: c, id: id}
|
|
|
c.table[id] = ns
|
|
|
return ns
|
|
|
}
|
|
|
@@ -130,130 +133,267 @@ func (c *lruCache) evict() {
|
|
|
}
|
|
|
|
|
|
type lruNs struct {
|
|
|
- lru *lruCache
|
|
|
- id uint64
|
|
|
- table map[uint64]*lruNode
|
|
|
- state nsState
|
|
|
+ lru *lruCache
|
|
|
+ id uint64
|
|
|
+ rbRoot *lruNode
|
|
|
+ state nsState
|
|
|
}
|
|
|
|
|
|
-func (ns *lruNs) Get(key uint64, setf SetFunc) Handle {
|
|
|
- ns.lru.mu.Lock()
|
|
|
+func (ns *lruNs) rbGetOrCreateNode(h *lruNode, key uint64) (hn, n *lruNode) {
|
|
|
+ if h == nil {
|
|
|
+ n = &lruNode{ns: ns, key: key}
|
|
|
+ return n, n
|
|
|
+ }
|
|
|
|
|
|
- if ns.state != nsEffective {
|
|
|
- ns.lru.mu.Unlock()
|
|
|
+ if key < h.key {
|
|
|
+ hn, n = ns.rbGetOrCreateNode(h.rbLeft, key)
|
|
|
+ if hn != nil {
|
|
|
+ h.rbLeft = hn
|
|
|
+ } else {
|
|
|
+ return nil, n
|
|
|
+ }
|
|
|
+ } else if key > h.key {
|
|
|
+ hn, n = ns.rbGetOrCreateNode(h.rbRight, key)
|
|
|
+ if hn != nil {
|
|
|
+ h.rbRight = hn
|
|
|
+ } else {
|
|
|
+ return nil, n
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ return nil, h
|
|
|
+ }
|
|
|
+
|
|
|
+ if rbIsRed(h.rbRight) && !rbIsRed(h.rbLeft) {
|
|
|
+ h = rbRotLeft(h)
|
|
|
+ }
|
|
|
+ if rbIsRed(h.rbLeft) && rbIsRed(h.rbLeft.rbLeft) {
|
|
|
+ h = rbRotRight(h)
|
|
|
+ }
|
|
|
+ if rbIsRed(h.rbLeft) && rbIsRed(h.rbRight) {
|
|
|
+ rbFlip(h)
|
|
|
+ }
|
|
|
+ return h, n
|
|
|
+}
|
|
|
+
|
|
|
+func (ns *lruNs) getOrCreateNode(key uint64) *lruNode {
|
|
|
+ hn, n := ns.rbGetOrCreateNode(ns.rbRoot, key)
|
|
|
+ if hn != nil {
|
|
|
+ ns.rbRoot = hn
|
|
|
+ ns.rbRoot.rbBlack = true
|
|
|
+ }
|
|
|
+ return n
|
|
|
+}
|
|
|
+
|
|
|
+func (ns *lruNs) rbGetNode(key uint64) *lruNode {
|
|
|
+ h := ns.rbRoot
|
|
|
+ for h != nil {
|
|
|
+ switch {
|
|
|
+ case key < h.key:
|
|
|
+ h = h.rbLeft
|
|
|
+ case key > h.key:
|
|
|
+ h = h.rbRight
|
|
|
+ default:
|
|
|
+ return h
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func (ns *lruNs) getNode(key uint64) *lruNode {
|
|
|
+ return ns.rbGetNode(key)
|
|
|
+}
|
|
|
+
|
|
|
+func (ns *lruNs) rbDeleteNode(h *lruNode, key uint64) *lruNode {
|
|
|
+ if h == nil {
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
- node, ok := ns.table[key]
|
|
|
- if ok {
|
|
|
- switch node.state {
|
|
|
- case nodeEvicted:
|
|
|
- // Insert to recent list.
|
|
|
- node.state = nodeEffective
|
|
|
- node.ref++
|
|
|
- ns.lru.used += node.charge
|
|
|
- ns.lru.evict()
|
|
|
- fallthrough
|
|
|
- case nodeEffective:
|
|
|
- // Bump to front.
|
|
|
- node.rRemove()
|
|
|
- node.rInsert(&ns.lru.recent)
|
|
|
+ if key < h.key {
|
|
|
+ if h.rbLeft == nil { // key not present. Nothing to delete
|
|
|
+ return h
|
|
|
+ }
|
|
|
+ if !rbIsRed(h.rbLeft) && !rbIsRed(h.rbLeft.rbLeft) {
|
|
|
+ h = rbMoveLeft(h)
|
|
|
}
|
|
|
- node.ref++
|
|
|
+ h.rbLeft = ns.rbDeleteNode(h.rbLeft, key)
|
|
|
} else {
|
|
|
- if setf == nil {
|
|
|
- ns.lru.mu.Unlock()
|
|
|
+ if rbIsRed(h.rbLeft) {
|
|
|
+ h = rbRotRight(h)
|
|
|
+ }
|
|
|
+ // If @key equals @h.key and no right children at @h
|
|
|
+ if h.key == key && h.rbRight == nil {
|
|
|
return nil
|
|
|
}
|
|
|
+ if h.rbRight != nil && !rbIsRed(h.rbRight) && !rbIsRed(h.rbRight.rbLeft) {
|
|
|
+ h = rbMoveRight(h)
|
|
|
+ }
|
|
|
+ // If @key equals @h.key, and (from above) 'h.Right != nil'
|
|
|
+ if h.key == key {
|
|
|
+ var x *lruNode
|
|
|
+ h.rbRight, x = rbDeleteMin(h.rbRight)
|
|
|
+ if x == nil {
|
|
|
+ panic("logic")
|
|
|
+ }
|
|
|
+ x.rbLeft, h.rbLeft = h.rbLeft, nil
|
|
|
+ x.rbRight, h.rbRight = h.rbRight, nil
|
|
|
+ x.rbBlack = h.rbBlack
|
|
|
+ h = x
|
|
|
+ } else { // Else, @key is bigger than @h.key
|
|
|
+ h.rbRight = ns.rbDeleteNode(h.rbRight, key)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return rbFixup(h)
|
|
|
+}
|
|
|
+
|
|
|
+func (ns *lruNs) deleteNode(key uint64) {
|
|
|
+ ns.rbRoot = ns.rbDeleteNode(ns.rbRoot, key)
|
|
|
+ if ns.rbRoot != nil {
|
|
|
+ ns.rbRoot.rbBlack = true
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
+func (ns *lruNs) rbIterateNodes(h *lruNode, pivot uint64, iter func(n *lruNode) bool) bool {
|
|
|
+ if h == nil {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ if h.key >= pivot {
|
|
|
+ if !ns.rbIterateNodes(h.rbLeft, pivot, iter) {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ if !iter(h) {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return ns.rbIterateNodes(h.rbRight, pivot, iter)
|
|
|
+}
|
|
|
+
|
|
|
+func (ns *lruNs) iterateNodes(iter func(n *lruNode) bool) {
|
|
|
+ ns.rbIterateNodes(ns.rbRoot, 0, iter)
|
|
|
+}
|
|
|
+
|
|
|
+func (ns *lruNs) Get(key uint64, setf SetFunc) Handle {
|
|
|
+ ns.lru.mu.Lock()
|
|
|
+ defer ns.lru.mu.Unlock()
|
|
|
+
|
|
|
+ if ns.state != nsEffective {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+
|
|
|
+ var n *lruNode
|
|
|
+ if setf == nil {
|
|
|
+ n = ns.getNode(key)
|
|
|
+ if n == nil {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ n = ns.getOrCreateNode(key)
|
|
|
+ }
|
|
|
+ switch n.state {
|
|
|
+ case nodeZero:
|
|
|
charge, value := setf()
|
|
|
if value == nil {
|
|
|
- ns.lru.mu.Unlock()
|
|
|
+ ns.deleteNode(key)
|
|
|
return nil
|
|
|
}
|
|
|
-
|
|
|
- node = &lruNode{
|
|
|
- ns: ns,
|
|
|
- key: key,
|
|
|
- value: value,
|
|
|
- charge: charge,
|
|
|
- ref: 1,
|
|
|
+ if charge < 0 {
|
|
|
+ charge = 0
|
|
|
}
|
|
|
- ns.table[key] = node
|
|
|
+
|
|
|
+ n.value = value
|
|
|
+ n.charge = charge
|
|
|
+ n.state = nodeEvicted
|
|
|
|
|
|
ns.lru.size += charge
|
|
|
ns.lru.alive++
|
|
|
- if charge > 0 {
|
|
|
- node.ref++
|
|
|
- node.rInsert(&ns.lru.recent)
|
|
|
- ns.lru.used += charge
|
|
|
- ns.lru.evict()
|
|
|
+
|
|
|
+ fallthrough
|
|
|
+ case nodeEvicted:
|
|
|
+ if n.charge == 0 {
|
|
|
+ break
|
|
|
}
|
|
|
+
|
|
|
+ // Insert to recent list.
|
|
|
+ n.state = nodeEffective
|
|
|
+ n.ref++
|
|
|
+ ns.lru.used += n.charge
|
|
|
+ ns.lru.evict()
|
|
|
+
|
|
|
+ fallthrough
|
|
|
+ case nodeEffective:
|
|
|
+ // Bump to front.
|
|
|
+ n.rRemove()
|
|
|
+ n.rInsert(&ns.lru.recent)
|
|
|
}
|
|
|
+ n.ref++
|
|
|
|
|
|
- ns.lru.mu.Unlock()
|
|
|
- return &lruHandle{node: node}
|
|
|
+ return &lruHandle{node: n}
|
|
|
}
|
|
|
|
|
|
func (ns *lruNs) Delete(key uint64, fin DelFin) bool {
|
|
|
ns.lru.mu.Lock()
|
|
|
+ defer ns.lru.mu.Unlock()
|
|
|
|
|
|
if ns.state != nsEffective {
|
|
|
if fin != nil {
|
|
|
fin(false, false)
|
|
|
}
|
|
|
- ns.lru.mu.Unlock()
|
|
|
return false
|
|
|
}
|
|
|
|
|
|
- node, exist := ns.table[key]
|
|
|
- if !exist {
|
|
|
+ n := ns.getNode(key)
|
|
|
+ if n == nil {
|
|
|
if fin != nil {
|
|
|
fin(false, false)
|
|
|
}
|
|
|
- ns.lru.mu.Unlock()
|
|
|
return false
|
|
|
+
|
|
|
}
|
|
|
|
|
|
- switch node.state {
|
|
|
+ switch n.state {
|
|
|
+ case nodeEffective:
|
|
|
+ ns.lru.used -= n.charge
|
|
|
+ n.state = nodeDeleted
|
|
|
+ n.delfin = fin
|
|
|
+ n.rRemove()
|
|
|
+ n.derefNB()
|
|
|
+ case nodeEvicted:
|
|
|
+ n.state = nodeDeleted
|
|
|
+ n.delfin = fin
|
|
|
case nodeDeleted:
|
|
|
if fin != nil {
|
|
|
fin(true, true)
|
|
|
}
|
|
|
- ns.lru.mu.Unlock()
|
|
|
return false
|
|
|
- case nodeEffective:
|
|
|
- ns.lru.used -= node.charge
|
|
|
- node.state = nodeDeleted
|
|
|
- node.delfin = fin
|
|
|
- node.rRemove()
|
|
|
- node.derefNB()
|
|
|
default:
|
|
|
- node.state = nodeDeleted
|
|
|
- node.delfin = fin
|
|
|
+ panic("invalid state")
|
|
|
}
|
|
|
|
|
|
- ns.lru.mu.Unlock()
|
|
|
return true
|
|
|
}
|
|
|
|
|
|
func (ns *lruNs) purgeNB(fin PurgeFin) {
|
|
|
- if ns.state != nsEffective {
|
|
|
- return
|
|
|
- }
|
|
|
-
|
|
|
- for _, node := range ns.table {
|
|
|
- switch node.state {
|
|
|
- case nodeDeleted:
|
|
|
- case nodeEffective:
|
|
|
- ns.lru.used -= node.charge
|
|
|
- node.state = nodeDeleted
|
|
|
- node.purgefin = fin
|
|
|
- node.rRemove()
|
|
|
- node.derefNB()
|
|
|
- default:
|
|
|
- node.state = nodeDeleted
|
|
|
- node.purgefin = fin
|
|
|
+ if ns.state == nsEffective {
|
|
|
+ var nodes []*lruNode
|
|
|
+ ns.iterateNodes(func(n *lruNode) bool {
|
|
|
+ nodes = append(nodes, n)
|
|
|
+ return true
|
|
|
+ })
|
|
|
+ for _, n := range nodes {
|
|
|
+ switch n.state {
|
|
|
+ case nodeEffective:
|
|
|
+ ns.lru.used -= n.charge
|
|
|
+ n.state = nodeDeleted
|
|
|
+ n.purgefin = fin
|
|
|
+ n.rRemove()
|
|
|
+ n.derefNB()
|
|
|
+ case nodeEvicted:
|
|
|
+ n.state = nodeDeleted
|
|
|
+ n.purgefin = fin
|
|
|
+ case nodeDeleted:
|
|
|
+ default:
|
|
|
+ panic("invalid state")
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -265,22 +405,22 @@ func (ns *lruNs) Purge(fin PurgeFin) {
|
|
|
}
|
|
|
|
|
|
func (ns *lruNs) zapNB() {
|
|
|
- if ns.state != nsEffective {
|
|
|
- return
|
|
|
- }
|
|
|
-
|
|
|
- ns.state = nsZapped
|
|
|
+ if ns.state == nsEffective {
|
|
|
+ ns.state = nsZapped
|
|
|
+
|
|
|
+ ns.iterateNodes(func(n *lruNode) bool {
|
|
|
+ if n.state == nodeEffective {
|
|
|
+ ns.lru.used -= n.charge
|
|
|
+ n.rRemove()
|
|
|
+ }
|
|
|
+ ns.lru.size -= n.charge
|
|
|
+ n.state = nodeDeleted
|
|
|
+ n.fin()
|
|
|
|
|
|
- for _, node := range ns.table {
|
|
|
- if node.state == nodeEffective {
|
|
|
- ns.lru.used -= node.charge
|
|
|
- node.rRemove()
|
|
|
- }
|
|
|
- ns.lru.size -= node.charge
|
|
|
- node.state = nodeDeleted
|
|
|
- node.fin()
|
|
|
+ return true
|
|
|
+ })
|
|
|
+ ns.rbRoot = nil
|
|
|
}
|
|
|
- ns.table = nil
|
|
|
}
|
|
|
|
|
|
func (ns *lruNs) Zap() {
|
|
|
@@ -293,7 +433,9 @@ func (ns *lruNs) Zap() {
|
|
|
type lruNode struct {
|
|
|
ns *lruNs
|
|
|
|
|
|
- rNext, rPrev *lruNode
|
|
|
+ rNext, rPrev *lruNode
|
|
|
+ rbLeft, rbRight *lruNode
|
|
|
+ rbBlack bool
|
|
|
|
|
|
key uint64
|
|
|
value interface{}
|
|
|
@@ -344,7 +486,7 @@ func (n *lruNode) derefNB() {
|
|
|
if n.ref == 0 {
|
|
|
if n.ns.state == nsEffective {
|
|
|
// Remove elemement.
|
|
|
- delete(n.ns.table, n.key)
|
|
|
+ n.ns.deleteNode(n.key)
|
|
|
n.ns.lru.size -= n.charge
|
|
|
n.ns.lru.alive--
|
|
|
n.fin()
|
|
|
@@ -380,3 +522,92 @@ func (h *lruHandle) Release() {
|
|
|
h.node.deref()
|
|
|
h.node = nil
|
|
|
}
|
|
|
+
|
|
|
+func rbIsRed(h *lruNode) bool {
|
|
|
+ if h == nil {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ return !h.rbBlack
|
|
|
+}
|
|
|
+
|
|
|
+func rbRotLeft(h *lruNode) *lruNode {
|
|
|
+ x := h.rbRight
|
|
|
+ if x.rbBlack {
|
|
|
+ panic("rotating a black link")
|
|
|
+ }
|
|
|
+ h.rbRight = x.rbLeft
|
|
|
+ x.rbLeft = h
|
|
|
+ x.rbBlack = h.rbBlack
|
|
|
+ h.rbBlack = false
|
|
|
+ return x
|
|
|
+}
|
|
|
+
|
|
|
+func rbRotRight(h *lruNode) *lruNode {
|
|
|
+ x := h.rbLeft
|
|
|
+ if x.rbBlack {
|
|
|
+ panic("rotating a black link")
|
|
|
+ }
|
|
|
+ h.rbLeft = x.rbRight
|
|
|
+ x.rbRight = h
|
|
|
+ x.rbBlack = h.rbBlack
|
|
|
+ h.rbBlack = false
|
|
|
+ return x
|
|
|
+}
|
|
|
+
|
|
|
+func rbFlip(h *lruNode) {
|
|
|
+ h.rbBlack = !h.rbBlack
|
|
|
+ h.rbLeft.rbBlack = !h.rbLeft.rbBlack
|
|
|
+ h.rbRight.rbBlack = !h.rbRight.rbBlack
|
|
|
+}
|
|
|
+
|
|
|
+func rbMoveLeft(h *lruNode) *lruNode {
|
|
|
+ rbFlip(h)
|
|
|
+ if rbIsRed(h.rbRight.rbLeft) {
|
|
|
+ h.rbRight = rbRotRight(h.rbRight)
|
|
|
+ h = rbRotLeft(h)
|
|
|
+ rbFlip(h)
|
|
|
+ }
|
|
|
+ return h
|
|
|
+}
|
|
|
+
|
|
|
+func rbMoveRight(h *lruNode) *lruNode {
|
|
|
+ rbFlip(h)
|
|
|
+ if rbIsRed(h.rbLeft.rbLeft) {
|
|
|
+ h = rbRotRight(h)
|
|
|
+ rbFlip(h)
|
|
|
+ }
|
|
|
+ return h
|
|
|
+}
|
|
|
+
|
|
|
+func rbFixup(h *lruNode) *lruNode {
|
|
|
+ if rbIsRed(h.rbRight) {
|
|
|
+ h = rbRotLeft(h)
|
|
|
+ }
|
|
|
+
|
|
|
+ if rbIsRed(h.rbLeft) && rbIsRed(h.rbLeft.rbLeft) {
|
|
|
+ h = rbRotRight(h)
|
|
|
+ }
|
|
|
+
|
|
|
+ if rbIsRed(h.rbLeft) && rbIsRed(h.rbRight) {
|
|
|
+ rbFlip(h)
|
|
|
+ }
|
|
|
+
|
|
|
+ return h
|
|
|
+}
|
|
|
+
|
|
|
+func rbDeleteMin(h *lruNode) (hn, n *lruNode) {
|
|
|
+ if h == nil {
|
|
|
+ return nil, nil
|
|
|
+ }
|
|
|
+ if h.rbLeft == nil {
|
|
|
+ return nil, h
|
|
|
+ }
|
|
|
+
|
|
|
+ if !rbIsRed(h.rbLeft) && !rbIsRed(h.rbLeft.rbLeft) {
|
|
|
+ h = rbMoveLeft(h)
|
|
|
+ }
|
|
|
+
|
|
|
+ h.rbLeft, n = rbDeleteMin(h.rbLeft)
|
|
|
+
|
|
|
+ return rbFixup(h), n
|
|
|
+}
|