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.
245 lines
6.1 KiB
245 lines
6.1 KiB
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) |
|
}
|
|
|