package trie
import (
"fmt"
"strconv"
"strings"
"github.com/dgraph-io/badger/v4/pb"
"github.com/dgraph-io/badger/v4/y"
)
type node struct {
children map [byte ]*node
ignore *node
ids []uint64
}
func (n *node ) isEmpty () bool {
return len (n .children ) == 0 && len (n .ids ) == 0 && n .ignore == nil
}
func newNode() *node {
return &node {
children : make (map [byte ]*node ),
ids : []uint64 {},
}
}
type Trie struct {
root *node
}
func NewTrie () *Trie {
return &Trie {
root : newNode (),
}
}
func parseIgnoreBytes(ig string ) ([]bool , error ) {
var out []bool
if ig == "" {
return out , nil
}
for _ , each := range strings .Split (strings .TrimSpace (ig ), "," ) {
r := strings .Split (strings .TrimSpace (each ), "-" )
if len (r ) == 0 || len (r ) > 2 {
return out , fmt .Errorf ("Invalid range: %s" , each )
}
start , end := -1 , -1
if len (r ) == 2 {
idx , err := strconv .Atoi (strings .TrimSpace (r [1 ]))
if err != nil {
return out , err
}
end = idx
}
{
idx , err := strconv .Atoi (strings .TrimSpace (r [0 ]))
if err != nil {
return out , err
}
start = idx
}
if start == -1 {
return out , fmt .Errorf ("Invalid range: %s" , each )
}
for start >= len (out ) {
out = append (out , false )
}
for end >= len (out ) {
out = append (out , false )
}
if end == -1 {
out [start ] = true
} else {
for i := start ; i <= end ; i ++ {
out [i ] = true
}
}
}
return out , nil
}
func (t *Trie ) Add (prefix []byte , id uint64 ) {
m := pb .Match {
Prefix : prefix ,
}
y .Check (t .AddMatch (m , id ))
}
func (t *Trie ) AddMatch (m pb .Match , id uint64 ) error {
return t .fix (m , id , set )
}
const (
set = iota
del
)
func (t *Trie ) fix (m pb .Match , id uint64 , op int ) error {
curNode := t .root
ignore , err := parseIgnoreBytes (m .IgnoreBytes )
if err != nil {
return fmt .Errorf ("while parsing ignore bytes: %s: %w" , m .IgnoreBytes , err )
}
for len (ignore ) < len (m .Prefix ) {
ignore = append (ignore , false )
}
for idx , byt := range m .Prefix {
var child *node
if ignore [idx ] {
child = curNode .ignore
if child == nil {
if op == del {
return nil
}
child = newNode ()
curNode .ignore = child
}
} else {
child = curNode .children [byt ]
if child == nil {
if op == del {
return nil
}
child = newNode ()
curNode .children [byt ] = child
}
}
curNode = child
}
if op == set {
curNode .ids = append (curNode .ids , id )
} else if op == del {
out := curNode .ids [:0 ]
for _ , cid := range curNode .ids {
if id != cid {
out = append (out , cid )
}
}
curNode .ids = out
} else {
y .AssertTrue (false )
}
return nil
}
func (t *Trie ) Get (key []byte ) map [uint64 ]struct {} {
return t .get (t .root , key )
}
func (t *Trie ) get (curNode *node , key []byte ) map [uint64 ]struct {} {
y .AssertTrue (curNode != nil )
out := make (map [uint64 ]struct {})
for _ , i := range curNode .ids {
out [i ] = struct {}{}
}
if len (key ) == 0 {
return out
}
if curNode .ignore != nil {
res := t .get (curNode .ignore , key [1 :])
for id := range res {
out [id ] = struct {}{}
}
}
if child := curNode .children [key [0 ]]; child != nil {
res := t .get (child , key [1 :])
for id := range res {
out [id ] = struct {}{}
}
}
return out
}
func removeEmpty(curNode *node ) bool {
if curNode .ignore != nil {
if empty := removeEmpty (curNode .ignore ); empty {
curNode .ignore = nil
}
}
for byt , n := range curNode .children {
if empty := removeEmpty (n ); empty {
delete (curNode .children , byt )
}
}
return curNode .isEmpty ()
}
func (t *Trie ) Delete (prefix []byte , id uint64 ) error {
return t .DeleteMatch (pb .Match {Prefix : prefix }, id )
}
func (t *Trie ) DeleteMatch (m pb .Match , id uint64 ) error {
if err := t .fix (m , id , del ); err != nil {
return err
}
removeEmpty (t .root )
return nil
}
func numNodes(curNode *node ) int {
if curNode == nil {
return 0
}
num := numNodes (curNode .ignore )
for _ , n := range curNode .children {
num += numNodes (n )
}
return num + 1
}
The pages are generated with Golds v0.8.4 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .