You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
472 lines
9.1 KiB
472 lines
9.1 KiB
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 |
|
}
|
|
|