init
This commit is contained in:
472
vendor/github.com/go-ego/cedar/cedar.go
generated
vendored
Normal file
472
vendor/github.com/go-ego/cedar/cedar.go
generated
vendored
Normal file
@ -0,0 +1,472 @@
|
||||
package cedar
|
||||
|
||||
const (
|
||||
// ValueLimit limit value
|
||||
ValueLimit = int(^uint(0) >> 1)
|
||||
)
|
||||
|
||||
type node struct {
|
||||
Value int
|
||||
Check int
|
||||
}
|
||||
|
||||
func (n *node) base() int {
|
||||
return -(n.Value + 1)
|
||||
}
|
||||
|
||||
type ninfo struct {
|
||||
Sibling, Child byte
|
||||
}
|
||||
|
||||
type block struct {
|
||||
Prev, Next, Num, Reject, Trial, Ehead int
|
||||
}
|
||||
|
||||
func (b *block) init() {
|
||||
b.Num = 256
|
||||
b.Reject = 257
|
||||
}
|
||||
|
||||
// Cedar cedar struct
|
||||
type Cedar struct {
|
||||
*cedar
|
||||
}
|
||||
|
||||
type cedar struct {
|
||||
Array []node
|
||||
Ninfos []ninfo
|
||||
Blocks []block
|
||||
Reject [257]int
|
||||
BheadF int
|
||||
BheadC int
|
||||
BheadO int
|
||||
Capacity int
|
||||
Size int
|
||||
Ordered bool
|
||||
MaxTrial int
|
||||
}
|
||||
|
||||
// New new cedar
|
||||
func New() *Cedar {
|
||||
da := cedar{
|
||||
Array: make([]node, 256),
|
||||
Ninfos: make([]ninfo, 256),
|
||||
Blocks: make([]block, 1),
|
||||
Capacity: 256,
|
||||
Size: 256,
|
||||
Ordered: true,
|
||||
MaxTrial: 1,
|
||||
}
|
||||
|
||||
da.Array[0] = node{-2, 0}
|
||||
for i := 1; i < 256; i++ {
|
||||
da.Array[i] = node{-(i - 1), -(i + 1)}
|
||||
}
|
||||
da.Array[1].Value = -255
|
||||
da.Array[255].Check = -1
|
||||
|
||||
da.Blocks[0].Ehead = 1
|
||||
da.Blocks[0].init()
|
||||
|
||||
for i := 0; i <= 256; i++ {
|
||||
da.Reject[i] = i + 1
|
||||
}
|
||||
|
||||
return &Cedar{&da}
|
||||
}
|
||||
|
||||
// Get value by key, insert the key if not exist
|
||||
func (da *cedar) get(key []byte, from, pos int) *int {
|
||||
for ; pos < len(key); pos++ {
|
||||
if value := da.Array[from].Value; value >= 0 && value != ValueLimit {
|
||||
to := da.follow(from, 0)
|
||||
da.Array[to].Value = value
|
||||
}
|
||||
from = da.follow(from, key[pos])
|
||||
}
|
||||
to := from
|
||||
if da.Array[from].Value < 0 {
|
||||
to = da.follow(from, 0)
|
||||
}
|
||||
return &da.Array[to].Value
|
||||
}
|
||||
|
||||
func (da *cedar) follow(from int, label byte) int {
|
||||
base := da.Array[from].base()
|
||||
to := base ^ int(label)
|
||||
|
||||
if base < 0 || da.Array[to].Check < 0 {
|
||||
hasChild := false
|
||||
if base >= 0 {
|
||||
hasChild = (da.Array[base^int(da.Ninfos[from].Child)].Check == from)
|
||||
}
|
||||
to = da.popEnode(base, label, from)
|
||||
da.pushSibling(from, to^int(label), label, hasChild)
|
||||
|
||||
return to
|
||||
}
|
||||
|
||||
if da.Array[to].Check != from {
|
||||
to = da.resolve(from, base, label)
|
||||
return to
|
||||
}
|
||||
|
||||
if da.Array[to].Check == from {
|
||||
return to
|
||||
}
|
||||
|
||||
panic("cedar: internal error, should not be here")
|
||||
// return to
|
||||
}
|
||||
|
||||
func (da *cedar) popBlock(bi int, headIn *int, last bool) {
|
||||
if last {
|
||||
*headIn = 0
|
||||
return
|
||||
}
|
||||
|
||||
b := &da.Blocks[bi]
|
||||
da.Blocks[b.Prev].Next = b.Next
|
||||
da.Blocks[b.Next].Prev = b.Prev
|
||||
if bi == *headIn {
|
||||
*headIn = b.Next
|
||||
}
|
||||
}
|
||||
|
||||
func (da *cedar) pushBlock(bi int, headOut *int, empty bool) {
|
||||
b := &da.Blocks[bi]
|
||||
if empty {
|
||||
*headOut, b.Prev, b.Next = bi, bi, bi
|
||||
} else {
|
||||
tailOut := &da.Blocks[*headOut].Prev
|
||||
b.Prev = *tailOut
|
||||
b.Next = *headOut
|
||||
*headOut, *tailOut, da.Blocks[*tailOut].Next = bi, bi, bi
|
||||
}
|
||||
}
|
||||
|
||||
func (da *cedar) addBlock() int {
|
||||
if da.Size == da.Capacity {
|
||||
da.Capacity *= 2
|
||||
|
||||
oldArray := da.Array
|
||||
da.Array = make([]node, da.Capacity)
|
||||
copy(da.Array, oldArray)
|
||||
|
||||
oldNinfo := da.Ninfos
|
||||
da.Ninfos = make([]ninfo, da.Capacity)
|
||||
copy(da.Ninfos, oldNinfo)
|
||||
|
||||
oldBlock := da.Blocks
|
||||
da.Blocks = make([]block, da.Capacity>>8)
|
||||
copy(da.Blocks, oldBlock)
|
||||
}
|
||||
|
||||
da.Blocks[da.Size>>8].init()
|
||||
da.Blocks[da.Size>>8].Ehead = da.Size
|
||||
|
||||
da.Array[da.Size] = node{-(da.Size + 255), -(da.Size + 1)}
|
||||
for i := da.Size + 1; i < da.Size+255; i++ {
|
||||
da.Array[i] = node{-(i - 1), -(i + 1)}
|
||||
}
|
||||
da.Array[da.Size+255] = node{-(da.Size + 254), -da.Size}
|
||||
|
||||
da.pushBlock(da.Size>>8, &da.BheadO, da.BheadO == 0)
|
||||
da.Size += 256
|
||||
return da.Size>>8 - 1
|
||||
}
|
||||
|
||||
func (da *cedar) transferBlock(bi int, headIn, headOut *int) {
|
||||
da.popBlock(bi, headIn, bi == da.Blocks[bi].Next)
|
||||
da.pushBlock(bi, headOut, *headOut == 0 && da.Blocks[bi].Num != 0)
|
||||
}
|
||||
|
||||
func (da *cedar) popEnode(base int, label byte, from int) int {
|
||||
e := base ^ int(label)
|
||||
if base < 0 {
|
||||
e = da.findPlace()
|
||||
}
|
||||
bi := e >> 8
|
||||
n := &da.Array[e]
|
||||
b := &da.Blocks[bi]
|
||||
b.Num--
|
||||
if b.Num == 0 {
|
||||
if bi != 0 {
|
||||
da.transferBlock(bi, &da.BheadC, &da.BheadF)
|
||||
}
|
||||
} else {
|
||||
da.Array[-n.Value].Check = n.Check
|
||||
da.Array[-n.Check].Value = n.Value
|
||||
if e == b.Ehead {
|
||||
b.Ehead = -n.Check
|
||||
}
|
||||
if bi != 0 && b.Num == 1 && b.Trial != da.MaxTrial {
|
||||
da.transferBlock(bi, &da.BheadO, &da.BheadC)
|
||||
}
|
||||
}
|
||||
|
||||
n.Value = ValueLimit
|
||||
n.Check = from
|
||||
if base < 0 {
|
||||
da.Array[from].Value = -(e ^ int(label)) - 1
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
func (da *cedar) pushEnode(e int) {
|
||||
bi := e >> 8
|
||||
b := &da.Blocks[bi]
|
||||
b.Num++
|
||||
|
||||
if b.Num == 1 {
|
||||
b.Ehead = e
|
||||
da.Array[e] = node{-e, -e}
|
||||
if bi != 0 {
|
||||
da.transferBlock(bi, &da.BheadF, &da.BheadC)
|
||||
}
|
||||
} else {
|
||||
prev := b.Ehead
|
||||
next := -da.Array[prev].Check
|
||||
da.Array[e] = node{-prev, -next}
|
||||
da.Array[prev].Check = -e
|
||||
da.Array[next].Value = -e
|
||||
if b.Num == 2 || b.Trial == da.MaxTrial {
|
||||
if bi != 0 {
|
||||
da.transferBlock(bi, &da.BheadC, &da.BheadO)
|
||||
}
|
||||
}
|
||||
b.Trial = 0
|
||||
}
|
||||
|
||||
if b.Reject < da.Reject[b.Num] {
|
||||
b.Reject = da.Reject[b.Num]
|
||||
}
|
||||
da.Ninfos[e] = ninfo{}
|
||||
}
|
||||
|
||||
// hasChild: wherether the `from` node has children
|
||||
func (da *cedar) pushSibling(from, base int, label byte, hasChild bool) {
|
||||
c := &da.Ninfos[from].Child
|
||||
keepOrder := *c == 0
|
||||
if da.Ordered {
|
||||
keepOrder = label > *c
|
||||
}
|
||||
|
||||
if hasChild && keepOrder {
|
||||
c = &da.Ninfos[base^int(*c)].Sibling
|
||||
for da.Ordered && *c != 0 && *c < label {
|
||||
c = &da.Ninfos[base^int(*c)].Sibling
|
||||
}
|
||||
}
|
||||
|
||||
da.Ninfos[base^int(label)].Sibling = *c
|
||||
*c = label
|
||||
}
|
||||
|
||||
func (da *cedar) popSibling(from, base int, label byte) {
|
||||
c := &da.Ninfos[from].Child
|
||||
for *c != label {
|
||||
c = &da.Ninfos[base^int(*c)].Sibling
|
||||
}
|
||||
*c = da.Ninfos[base^int(*c)].Sibling
|
||||
}
|
||||
|
||||
func (da *cedar) consult(baseN, baseP int, cN, cP byte) bool {
|
||||
cN = da.Ninfos[baseN^int(cN)].Sibling
|
||||
cP = da.Ninfos[baseP^int(cP)].Sibling
|
||||
for cN != 0 && cP != 0 {
|
||||
cN = da.Ninfos[baseN^int(cN)].Sibling
|
||||
cP = da.Ninfos[baseP^int(cP)].Sibling
|
||||
}
|
||||
return cP != 0
|
||||
}
|
||||
|
||||
func (da *cedar) setChild(base int, c byte, label byte, flag bool) []byte {
|
||||
child := make([]byte, 0, 257)
|
||||
if c == 0 {
|
||||
child = append(child, c)
|
||||
c = da.Ninfos[base^int(c)].Sibling
|
||||
}
|
||||
if da.Ordered {
|
||||
for c != 0 && c <= label {
|
||||
child = append(child, c)
|
||||
c = da.Ninfos[base^int(c)].Sibling
|
||||
}
|
||||
}
|
||||
if flag {
|
||||
child = append(child, label)
|
||||
}
|
||||
for c != 0 {
|
||||
child = append(child, c)
|
||||
c = da.Ninfos[base^int(c)].Sibling
|
||||
}
|
||||
return child
|
||||
}
|
||||
|
||||
func (da *cedar) findPlace() int {
|
||||
if da.BheadC != 0 {
|
||||
return da.Blocks[da.BheadC].Ehead
|
||||
}
|
||||
if da.BheadO != 0 {
|
||||
return da.Blocks[da.BheadO].Ehead
|
||||
}
|
||||
return da.addBlock() << 8
|
||||
}
|
||||
|
||||
func (da *cedar) findPlaces(child []byte) int {
|
||||
bi := da.BheadO
|
||||
if bi != 0 {
|
||||
e := da.listBi(bi, child)
|
||||
if e > 0 {
|
||||
return e
|
||||
}
|
||||
}
|
||||
return da.addBlock() << 8
|
||||
}
|
||||
|
||||
func (da *cedar) listBi(bi int, child []byte) int {
|
||||
nc := len(child)
|
||||
bz := da.Blocks[da.BheadO].Prev
|
||||
for {
|
||||
b := &da.Blocks[bi]
|
||||
if b.Num >= nc && nc < b.Reject {
|
||||
e := da.listEhead(b, child)
|
||||
if e > 0 {
|
||||
return e
|
||||
}
|
||||
}
|
||||
b.Reject = nc
|
||||
if b.Reject < da.Reject[b.Num] {
|
||||
da.Reject[b.Num] = b.Reject
|
||||
}
|
||||
|
||||
biN := b.Next
|
||||
b.Trial++
|
||||
if b.Trial == da.MaxTrial {
|
||||
da.transferBlock(bi, &da.BheadO, &da.BheadC)
|
||||
}
|
||||
if bi == bz {
|
||||
break
|
||||
}
|
||||
bi = biN
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func (da *cedar) listEhead(b *block, child []byte) int {
|
||||
for e := b.Ehead; ; {
|
||||
base := e ^ int(child[0])
|
||||
for i := 0; da.Array[base^int(child[i])].Check < 0; i++ {
|
||||
if i == len(child)-1 {
|
||||
b.Ehead = e
|
||||
// if e == 0 {
|
||||
// }
|
||||
return e
|
||||
}
|
||||
}
|
||||
e = -da.Array[e].Check
|
||||
if e == b.Ehead {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func (da *cedar) resolve(fromN, baseN int, labelN byte) int {
|
||||
toPn := baseN ^ int(labelN)
|
||||
fromP := da.Array[toPn].Check
|
||||
baseP := da.Array[fromP].base()
|
||||
flag := da.consult(baseN, baseP, da.Ninfos[fromN].Child, da.Ninfos[fromP].Child)
|
||||
|
||||
var children []byte
|
||||
if flag {
|
||||
children = da.setChild(baseN, da.Ninfos[fromN].Child, labelN, true)
|
||||
} else {
|
||||
children = da.setChild(baseP, da.Ninfos[fromP].Child, 255, false)
|
||||
}
|
||||
|
||||
var base int
|
||||
if len(children) == 1 {
|
||||
base = da.findPlace()
|
||||
} else {
|
||||
base = da.findPlaces(children)
|
||||
}
|
||||
base ^= int(children[0])
|
||||
|
||||
var (
|
||||
from int
|
||||
nbase int
|
||||
)
|
||||
|
||||
if flag {
|
||||
from = fromN
|
||||
nbase = baseN
|
||||
} else {
|
||||
from = fromP
|
||||
nbase = baseP
|
||||
}
|
||||
|
||||
if flag && children[0] == labelN {
|
||||
da.Ninfos[from].Child = labelN
|
||||
}
|
||||
|
||||
da.Array[from].Value = -base - 1
|
||||
base, labelN, toPn = da.list(base, from, nbase, fromN, toPn,
|
||||
labelN, children, flag)
|
||||
|
||||
if flag {
|
||||
return base ^ int(labelN)
|
||||
}
|
||||
|
||||
return toPn
|
||||
}
|
||||
|
||||
func (da *cedar) list(base, from, nbase, fromN, toPn int,
|
||||
labelN byte, children []byte, flag bool) (int, byte, int) {
|
||||
for i := 0; i < len(children); i++ {
|
||||
to := da.popEnode(base, children[i], from)
|
||||
newTo := nbase ^ int(children[i])
|
||||
|
||||
if i == len(children)-1 {
|
||||
da.Ninfos[to].Sibling = 0
|
||||
} else {
|
||||
da.Ninfos[to].Sibling = children[i+1]
|
||||
}
|
||||
|
||||
if flag && newTo == toPn { // new node has no child
|
||||
continue
|
||||
}
|
||||
|
||||
n := &da.Array[to]
|
||||
ns := &da.Array[newTo]
|
||||
n.Value = ns.Value
|
||||
if n.Value < 0 && children[i] != 0 {
|
||||
// this node has children, fix their check
|
||||
c := da.Ninfos[newTo].Child
|
||||
da.Ninfos[to].Child = c
|
||||
da.Array[n.base()^int(c)].Check = to
|
||||
c = da.Ninfos[n.base()^int(c)].Sibling
|
||||
for c != 0 {
|
||||
da.Array[n.base()^int(c)].Check = to
|
||||
c = da.Ninfos[n.base()^int(c)].Sibling
|
||||
}
|
||||
}
|
||||
|
||||
if !flag && newTo == fromN { // parent node moved
|
||||
fromN = to
|
||||
}
|
||||
|
||||
if !flag && newTo == toPn {
|
||||
da.pushSibling(fromN, toPn^int(labelN), labelN, true)
|
||||
da.Ninfos[newTo].Child = 0
|
||||
ns.Value = ValueLimit
|
||||
ns.Check = fromN
|
||||
} else {
|
||||
da.pushEnode(newTo)
|
||||
}
|
||||
}
|
||||
|
||||
return base, labelN, toPn
|
||||
}
|
Reference in New Issue
Block a user