go_study/go-for-study/vendor/github.com/go-ego/cedar/api.go

246 lines
6.1 KiB
Go

package cedar
// Status reports the following statistics of the cedar:
// keys: number of keys that are in the cedar,
// nodes: number of trie nodes (slots in the base array) has been taken,
// size: the size of the base array used by the cedar,
// capacity: the capicity of the base array used by the cedar.
func (da *Cedar) Status() (keys, nodes, size, capacity int) {
for i := 0; i < da.Size; i++ {
n := da.Array[i]
if n.Check >= 0 {
nodes++
if n.Value >= 0 {
keys++
}
}
}
return keys, nodes, da.Size, da.Capacity
}
// Jump travels from a node `from` to another node
// `to` by following the path `path`.
// For example, if the following keys were inserted:
// id key
// 19 abc
// 23 ab
// 37 abcd
// then
// Jump([]byte("ab"), 0) = 23, nil // reach "ab" from root
// Jump([]byte("c"), 23) = 19, nil // reach "abc" from "ab"
// Jump([]byte("cd"), 23) = 37, nil // reach "abcd" from "ab"
func (da *Cedar) Jump(path []byte, from int) (to int, err error) {
for _, b := range path {
if da.Array[from].Value >= 0 {
return from, ErrNoPath
}
to = da.Array[from].base() ^ int(b)
if da.Array[to].Check != from {
return from, ErrNoPath
}
from = to
}
return to, nil
}
// Key returns the key of the node with the given `id`.
// It will return ErrNoPath, if the node does not exist.
func (da *Cedar) Key(id int) (key []byte, err error) {
for id > 0 {
from := da.Array[id].Check
if from < 0 {
return nil, ErrNoPath
}
if char := byte(da.Array[from].base() ^ id); char != 0 {
key = append(key, char)
}
id = from
}
if id != 0 || len(key) == 0 {
return nil, ErrInvalidKey
}
for i := 0; i < len(key)/2; i++ {
key[i], key[len(key)-i-1] = key[len(key)-i-1], key[i]
}
return key, nil
}
// Value returns the value of the node with the given `id`.
// It will return ErrNoValue, if the node does not have a value.
func (da *Cedar) Value(id int) (value int, err error) {
value = da.Array[id].Value
if value >= 0 {
return value, nil
}
to := da.Array[id].base()
if da.Array[to].Check == id && da.Array[to].Value >= 0 {
return da.Array[to].Value, nil
}
return 0, ErrNoValue
}
// Insert adds a key-value pair into the cedar.
// It will return ErrInvalidValue, if value < 0 or >= ValueLimit.
func (da *Cedar) Insert(key []byte, value int) error {
if value < 0 || value >= ValueLimit {
return ErrInvalidValue
}
p := da.get(key, 0, 0)
*p = value
return nil
}
// Update increases the value associated with the `key`.
// The `key` will be inserted if it is not in the cedar.
// It will return ErrInvalidValue, if the updated value < 0 or >= ValueLimit.
func (da *Cedar) Update(key []byte, value int) error {
p := da.get(key, 0, 0)
// key was not inserted
if *p == ValueLimit {
*p = value
return nil
}
// key was inserted before
if *p+value < 0 || *p+value >= ValueLimit {
return ErrInvalidValue
}
*p += value
return nil
}
// Delete removes a key-value pair from the cedar.
// It will return ErrNoPath, if the key has not been added.
func (da *Cedar) Delete(key []byte) error {
// if the path does not exist, or the end is not a leaf,
// nothing to delete
to, err := da.Jump(key, 0)
if err != nil {
return ErrNoPath
}
if da.Array[to].Value < 0 {
base := da.Array[to].base()
if da.Array[base].Check == to {
to = base
}
}
for to > 0 {
from := da.Array[to].Check
base := da.Array[from].base()
label := byte(to ^ base)
// if `to` has sibling, remove `to` from the sibling list, then stop
if da.Ninfos[to].Sibling != 0 || da.Ninfos[from].Child != label {
// delete the label from the child ring first
da.popSibling(from, base, label)
// then release the current node `to` to the empty node ring
da.pushEnode(to)
break
}
// otherwise, just release the current node `to` to the empty node ring
da.pushEnode(to)
// then check its parent node
to = from
}
return nil
}
// Get returns the value associated with the given `key`.
// It is equivalent to
// id, err1 = Jump(key)
// value, err2 = Value(id)
// Thus, it may return ErrNoPath or ErrNoValue,
func (da *Cedar) Get(key []byte) (value int, err error) {
to, err := da.Jump(key, 0)
if err != nil {
return 0, err
}
return da.Value(to)
}
// PrefixMatch returns a list of at most `num` nodes
// which match the prefix of the key.
// If `num` is 0, it returns all matches.
// For example, if the following keys were inserted:
// id key
// 19 abc
// 23 ab
// 37 abcd
// then
// PrefixMatch([]byte("abc"), 1) = [ 23 ] // match ["ab"]
// PrefixMatch([]byte("abcd"), 0) = [ 23, 19, 37]
// match ["ab", "abc", "abcd"]
func (da *Cedar) PrefixMatch(key []byte, num int) (ids []int) {
for from, i := 0, 0; i < len(key); i++ {
to, err := da.Jump(key[i:i+1], from)
if err != nil {
break
}
if _, err := da.Value(to); err == nil {
ids = append(ids, to)
num--
if num == 0 {
return
}
}
from = to
}
return
}
// PrefixPredict returns a list of at most `num` nodes
// which has the key as their prefix.
// These nodes are ordered by their keys.
// If `num` is 0, it returns all matches.
// For example, if the following keys were inserted:
// id key
// 19 abc
// 23 ab
// 37 abcd
// then
// PrefixPredict([]byte("ab"), 2) = [ 23, 19 ] // predict ["ab", "abc"]
// PrefixPredict([]byte("ab"), 0) = [ 23, 19, 37 ]
// predict ["ab", "abc", "abcd"]
func (da *Cedar) PrefixPredict(key []byte, num int) (ids []int) {
root, err := da.Jump(key, 0)
if err != nil {
return
}
for from, err := da.begin(root); err == nil; from, err = da.next(from, root) {
ids = append(ids, from)
num--
if num == 0 {
return
}
}
return
}
func (da *Cedar) begin(from int) (to int, err error) {
for c := da.Ninfos[from].Child; c != 0; {
to = da.Array[from].base() ^ int(c)
c = da.Ninfos[to].Child
from = to
}
if da.Array[from].base() > 0 {
return da.Array[from].base(), nil
}
return from, nil
}
func (da *Cedar) next(from int, root int) (to int, err error) {
c := da.Ninfos[from].Sibling
for c == 0 && from != root && da.Array[from].Check >= 0 {
from = da.Array[from].Check
c = da.Ninfos[from].Sibling
}
if from == root {
return 0, ErrNoPath
}
from = da.Array[da.Array[from].Check].base() ^ int(c)
return da.begin(from)
}