|
@@ -13,6 +13,11 @@ import (
|
|
// Trie
|
|
// Trie
|
|
//------------------------------------------------------------------------------
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
+const (
|
|
|
|
+ DefaultMaxPrefixPerNode = 10
|
|
|
|
+ DefaultMaxChildrenPerSparseNode = 8
|
|
|
|
+)
|
|
|
|
+
|
|
type (
|
|
type (
|
|
Prefix []byte
|
|
Prefix []byte
|
|
Item interface{}
|
|
Item interface{}
|
|
@@ -27,15 +32,44 @@ type Trie struct {
|
|
prefix Prefix
|
|
prefix Prefix
|
|
item Item
|
|
item Item
|
|
|
|
|
|
|
|
+ maxPrefixPerNode int
|
|
|
|
+ maxChildrenPerSparseNode int
|
|
|
|
+
|
|
children childList
|
|
children childList
|
|
}
|
|
}
|
|
|
|
|
|
// Public API ------------------------------------------------------------------
|
|
// Public API ------------------------------------------------------------------
|
|
|
|
|
|
|
|
+type Option func(*Trie)
|
|
|
|
+
|
|
// Trie constructor.
|
|
// Trie constructor.
|
|
-func NewTrie() *Trie {
|
|
|
|
- return &Trie{
|
|
|
|
- children: newSparseChildList(),
|
|
|
|
|
|
+func NewTrie(options ...Option) *Trie {
|
|
|
|
+ trie := &Trie{}
|
|
|
|
+
|
|
|
|
+ for _, opt := range options {
|
|
|
|
+ opt(trie)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if trie.maxPrefixPerNode <= 0 {
|
|
|
|
+ trie.maxPrefixPerNode = DefaultMaxPrefixPerNode
|
|
|
|
+ }
|
|
|
|
+ if trie.maxChildrenPerSparseNode <= 0 {
|
|
|
|
+ trie.maxChildrenPerSparseNode = DefaultMaxChildrenPerSparseNode
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ trie.children = newSparseChildList(trie.maxChildrenPerSparseNode)
|
|
|
|
+ return trie
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func MaxPrefixPerNode(value int) Option {
|
|
|
|
+ return func(trie *Trie) {
|
|
|
|
+ trie.maxPrefixPerNode = value
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func MaxChildrenPerSparseNode(value int) Option {
|
|
|
|
+ return func(trie *Trie) {
|
|
|
|
+ trie.maxChildrenPerSparseNode = value
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -85,7 +119,8 @@ func (trie *Trie) MatchSubtree(key Prefix) (matched bool) {
|
|
return
|
|
return
|
|
}
|
|
}
|
|
|
|
|
|
-// Visit calls visitor on every node containing a non-nil item.
|
|
|
|
|
|
+// Visit calls visitor on every node containing a non-nil item
|
|
|
|
+// in alphabetical order.
|
|
//
|
|
//
|
|
// If an error is returned from visitor, the function stops visiting the tree
|
|
// If an error is returned from visitor, the function stops visiting the tree
|
|
// and returns that error, unless it is a special error - SkipSubtree. In that
|
|
// and returns that error, unless it is a special error - SkipSubtree. In that
|
|
@@ -233,7 +268,7 @@ func (trie *Trie) DeleteSubtree(prefix Prefix) (deleted bool) {
|
|
// If we are in the root of the trie, reset the trie.
|
|
// If we are in the root of the trie, reset the trie.
|
|
if parent == nil {
|
|
if parent == nil {
|
|
root.prefix = nil
|
|
root.prefix = nil
|
|
- root.children = newSparseChildList()
|
|
|
|
|
|
+ root.children = newSparseChildList(trie.maxPrefixPerNode)
|
|
return true
|
|
return true
|
|
}
|
|
}
|
|
|
|
|
|
@@ -257,12 +292,12 @@ func (trie *Trie) put(key Prefix, item Item, replace bool) (inserted bool) {
|
|
)
|
|
)
|
|
|
|
|
|
if node.prefix == nil {
|
|
if node.prefix == nil {
|
|
- if len(key) <= MaxPrefixPerNode {
|
|
|
|
|
|
+ if len(key) <= trie.maxPrefixPerNode {
|
|
node.prefix = key
|
|
node.prefix = key
|
|
goto InsertItem
|
|
goto InsertItem
|
|
}
|
|
}
|
|
- node.prefix = key[:MaxPrefixPerNode]
|
|
|
|
- key = key[MaxPrefixPerNode:]
|
|
|
|
|
|
+ node.prefix = key[:trie.maxPrefixPerNode]
|
|
|
|
+ key = key[trie.maxPrefixPerNode:]
|
|
goto AppendChild
|
|
goto AppendChild
|
|
}
|
|
}
|
|
|
|
|
|
@@ -306,14 +341,14 @@ AppendChild:
|
|
// This loop starts with empty node.prefix that needs to be filled.
|
|
// This loop starts with empty node.prefix that needs to be filled.
|
|
for len(key) != 0 {
|
|
for len(key) != 0 {
|
|
child := NewTrie()
|
|
child := NewTrie()
|
|
- if len(key) <= MaxPrefixPerNode {
|
|
|
|
|
|
+ if len(key) <= trie.maxPrefixPerNode {
|
|
child.prefix = key
|
|
child.prefix = key
|
|
node.children = node.children.add(child)
|
|
node.children = node.children.add(child)
|
|
node = child
|
|
node = child
|
|
goto InsertItem
|
|
goto InsertItem
|
|
} else {
|
|
} else {
|
|
- child.prefix = key[:MaxPrefixPerNode]
|
|
|
|
- key = key[MaxPrefixPerNode:]
|
|
|
|
|
|
+ child.prefix = key[:trie.maxPrefixPerNode]
|
|
|
|
+ key = key[trie.maxPrefixPerNode:]
|
|
node.children = node.children.add(child)
|
|
node.children = node.children.add(child)
|
|
node = child
|
|
node = child
|
|
}
|
|
}
|
|
@@ -344,7 +379,7 @@ func (trie *Trie) compact() *Trie {
|
|
}
|
|
}
|
|
|
|
|
|
// Make sure the combined prefixes fit into a single node.
|
|
// Make sure the combined prefixes fit into a single node.
|
|
- if len(trie.prefix)+len(child.prefix) > MaxPrefixPerNode {
|
|
|
|
|
|
+ if len(trie.prefix)+len(child.prefix) > trie.maxPrefixPerNode {
|
|
return trie
|
|
return trie
|
|
}
|
|
}
|
|
|
|
|