-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathtrie.go
132 lines (120 loc) · 2.9 KB
/
trie.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// Copyright (c) 2018 Couchbase, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package stempel
import (
"fmt"
"github.com/blevesearch/stempel/javadata"
)
// trie represents the internal trie structure
type trie struct {
rows []*row
cmds []string
root int32
forward bool
}
func newTrie(r *javadata.Reader) (rv *trie, err error) {
rv = &trie{}
rv.forward, err = r.ReadBool()
if err != nil {
return nil, fmt.Errorf("error reading trie forward: %v", err)
}
rv.root, err = r.ReadInt32()
if err != nil {
return nil, fmt.Errorf("error reading trie root: %v", err)
}
// commands
nCommands, err := r.ReadInt32()
if err != nil {
return nil, fmt.Errorf("error reading trie num commands: %v", err)
}
for nCommands > 0 {
utfCommand, nerr := r.ReadUTF()
if nerr != nil {
return nil, fmt.Errorf("error reading trie command utf: %v", nerr)
}
rv.cmds = append(rv.cmds, utfCommand)
nCommands--
}
// rows
nRows, err := r.ReadInt32()
if err != nil {
return nil, fmt.Errorf("error reading trie num rows: %v", err)
}
for nRows > 0 {
row, err := newRow(r)
if err != nil {
return nil, fmt.Errorf("error reading trie row: %v", err)
}
rv.rows = append(rv.rows, row)
nRows--
}
return rv, nil
}
func (t *trie) getRow(i int) *row {
if i < 0 || i >= len(t.rows) {
return nil
}
return t.rows[i]
}
func (t *trie) GetLastOnPath(key []rune) []rune {
now := t.getRow(int(t.root))
var last []rune
var w int32
e := newStrEnum(key, t.forward)
// walk over each rune
// if rune has row in the table, note the cmd (as last)
// if rune has row in table, see if it transitions to another row
// if it does, move to that row and next char on next loop itr
// if it does not, return the last cmd
// if you get to end of string and there is command in row use it
// or return last
for i := 0; i < len(key)-1; i++ {
r, err := e.next()
if err != nil {
return last
}
w = now.getCmd(r)
if w >= 0 {
last = []rune(t.cmds[w])
}
w = now.getRef(r)
if w >= 0 {
now = t.getRow(int(w))
} else {
return last
}
}
r, err := e.next()
if err != nil {
return last
}
w = now.getCmd(r)
if err != nil {
return last
}
if w >= 0 {
return []rune(t.cmds[w])
}
return last
}
func (t *trie) String() string {
rv := ""
for _, cmd := range t.cmds {
rv += fmt.Sprintf("cmd: %s\n", string(cmd))
}
for _, row := range t.rows {
rv += fmt.Sprintf("row: %v\n", row)
}
return rv
}