Skip to content

Commit

Permalink
Move the trie configuration into the struct itself
Browse files Browse the repository at this point in the history
Right now the important constants are global. These are
MaxPrefixPerNode and MaxChildrenPerSparseNode. It makes sense, though,
and it's a much better practice, to put this config into each trie
instance separately so that people can create tries behaving differently
without using any global state.
  • Loading branch information
tchap committed Feb 27, 2015
1 parent f64d0a6 commit 341a370
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 21 deletions.
5 changes: 4 additions & 1 deletion README.md
Expand Up @@ -50,9 +50,12 @@ printItem := func(prefix patricia.Prefix, item patricia.Item) error {
return nil
}

// Create a new tree.
// Create a new default trie (using the default parameter values).
trie := NewTrie()

// Create a new custom trie.
trie := NewTrie(MaxPrefixPerNode(16), MaxChildrenPerSparseNode(10))

// Insert some items.
trie.Insert(Prefix("Pepa Novak"), 1)
trie.Insert(Prefix("Pepa Sindelar"), 2)
Expand Down
10 changes: 2 additions & 8 deletions patricia/children.go
Expand Up @@ -5,12 +5,6 @@

package patricia

// Max prefix length that is kept in a single trie node.
var MaxPrefixPerNode = 10

// Max children to keep in a node in the sparse mode.
const MaxChildrenPerSparseNode = 8

type childList interface {
length() int
head() *Trie
Expand All @@ -25,9 +19,9 @@ type sparseChildList struct {
children []*Trie
}

func newSparseChildList() childList {
func newSparseChildList(maxChildrenPerSparseNode int) childList {
return &sparseChildList{
children: make([]*Trie, 0, MaxChildrenPerSparseNode),
children: make([]*Trie, 0, maxChildrenPerSparseNode),
}
}

Expand Down
56 changes: 45 additions & 11 deletions patricia/patricia.go
Expand Up @@ -13,6 +13,11 @@ import (
// Trie
//------------------------------------------------------------------------------

const (
DefaultMaxPrefixPerNode = 10
DefaultMaxChildrenPerSparseNode = 8
)

type (
Prefix []byte
Item interface{}
Expand All @@ -27,15 +32,44 @@ type Trie struct {
prefix Prefix
item Item

maxPrefixPerNode int
maxChildrenPerSparseNode int

children childList
}

// Public API ------------------------------------------------------------------

type Option func(*Trie)

// 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
}
}

Expand Down Expand Up @@ -233,7 +267,7 @@ func (trie *Trie) DeleteSubtree(prefix Prefix) (deleted bool) {
// If we are in the root of the trie, reset the trie.
if parent == nil {
root.prefix = nil
root.children = newSparseChildList()
root.children = newSparseChildList(trie.maxPrefixPerNode)
return true
}

Expand All @@ -257,12 +291,12 @@ func (trie *Trie) put(key Prefix, item Item, replace bool) (inserted bool) {
)

if node.prefix == nil {
if len(key) <= MaxPrefixPerNode {
if len(key) <= trie.maxPrefixPerNode {
node.prefix = key
goto InsertItem
}
node.prefix = key[:MaxPrefixPerNode]
key = key[MaxPrefixPerNode:]
node.prefix = key[:trie.maxPrefixPerNode]
key = key[trie.maxPrefixPerNode:]
goto AppendChild
}

Expand Down Expand Up @@ -306,14 +340,14 @@ AppendChild:
// This loop starts with empty node.prefix that needs to be filled.
for len(key) != 0 {
child := NewTrie()
if len(key) <= MaxPrefixPerNode {
if len(key) <= trie.maxPrefixPerNode {
child.prefix = key
node.children = node.children.add(child)
node = child
goto InsertItem
} else {
child.prefix = key[:MaxPrefixPerNode]
key = key[MaxPrefixPerNode:]
child.prefix = key[:trie.maxPrefixPerNode]
key = key[trie.maxPrefixPerNode:]
node.children = node.children.add(child)
node = child
}
Expand Down Expand Up @@ -344,7 +378,7 @@ func (trie *Trie) compact() *Trie {
}

// 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
}

Expand Down
2 changes: 1 addition & 1 deletion patricia/patricia_dense_test.go
Expand Up @@ -55,7 +55,7 @@ func TestTrie_InsertDensePreceeding(t *testing.T) {
trie := NewTrie()
start := byte(70)
// create a dense node
for i := byte(0); i <= MaxChildrenPerSparseNode; i++ {
for i := byte(0); i <= DefaultMaxChildrenPerSparseNode; i++ {
if !trie.Insert(Prefix([]byte{start + i}), true) {
t.Errorf("insert failed, prefix=%v", start+i)
}
Expand Down
14 changes: 14 additions & 0 deletions patricia/patricia_test.go
Expand Up @@ -13,6 +13,20 @@ import (

// Tests -----------------------------------------------------------------------

func TestTrie_ConstructorOptions(t *testing.T) {
trie := NewTrie(MaxPrefixPerNode(16), MaxChildrenPerSparseNode(10))

if trie.maxPrefixPerNode != 16 {
t.Errorf("Unexpected trie.maxPrefixPerNode value, expected=%v, got=%v",
16, trie.maxPrefixPerNode)
}

if trie.maxChildrenPerSparseNode != 10 {
t.Errorf("Unexpected trie.maxChildrenPerSparseNode value, expected=%v, got=%v",
10, trie.maxChildrenPerSparseNode)
}
}

func TestTrie_GetNonexistentPrefix(t *testing.T) {
trie := NewTrie()

Expand Down

0 comments on commit 341a370

Please sign in to comment.