glob/match/tree.go

174 lines
2.9 KiB
Go

package match
import (
"fmt"
"unicode/utf8"
"github.com/gobwas/glob/internal/debug"
"github.com/gobwas/glob/util/runes"
)
type Tree struct {
value MatchIndexer
left Matcher
right Matcher
minLen int
runes int
vrunes int
lrunes int
rrunes int
}
type SizedTree struct {
Tree
}
type IndexedTree struct {
value MatchIndexer
left MatchIndexer
right MatchIndexer
}
func (st SizedTree) RunesCount() int {
return st.Tree.runes
}
func NewTree(v MatchIndexer, l, r Matcher) Matcher {
tree := Tree{
value: v,
left: l,
right: r,
}
tree.minLen = v.MinLen()
if l != nil {
tree.minLen += l.MinLen()
}
if r != nil {
tree.minLen += r.MinLen()
}
var (
ls, lsz = l.(Sizer)
rs, rsz = r.(Sizer)
vs, vsz = v.(Sizer)
)
if lsz {
tree.lrunes = ls.RunesCount()
}
if rsz {
tree.rrunes = rs.RunesCount()
}
if vsz {
tree.vrunes = vs.RunesCount()
}
//li, lix := l.(MatchIndexer)
//ri, rix := r.(MatchIndexer)
if vsz && lsz && rsz {
tree.runes = tree.vrunes + tree.lrunes + tree.rrunes
return SizedTree{tree}
}
return tree
}
func (t Tree) MinLen() int {
return t.minLen
}
func (t Tree) Content(cb func(Matcher)) {
if t.left != nil {
cb(t.left)
}
cb(t.value)
if t.right != nil {
cb(t.right)
}
}
func (t Tree) Match(s string) (ok bool) {
if debug.Enabled {
done := debug.Matching("tree", s)
defer func() { done(ok) }()
}
n := len(s)
offset, limit := t.offsetLimit(s)
for len(s)-offset-limit >= t.vrunes {
if debug.Enabled {
debug.Logf(
"value %s indexing: %q (offset=%d; limit=%d)",
t.value, s[offset:n-limit], offset, limit,
)
}
index, segments := t.value.Index(s[offset : n-limit])
if debug.Enabled {
debug.Logf(
"value %s index: %d; %v",
t.value, index, segments,
)
}
if index == -1 {
releaseSegments(segments)
return false
}
if debug.Enabled {
debug.Logf("matching left: %q", s[:offset+index])
}
left := t.left.Match(s[:offset+index])
if debug.Enabled {
debug.Logf("matching left: -> %t", left)
}
if left {
for _, seg := range segments {
if debug.Enabled {
debug.Logf("matching right: %q", s[offset+index+seg:])
}
right := t.right.Match(s[offset+index+seg:])
if debug.Enabled {
debug.Logf("matching right: -> %t", right)
}
if right {
releaseSegments(segments)
return true
}
}
}
releaseSegments(segments)
_, x := utf8.DecodeRuneInString(s[offset+index:])
if x == 0 {
// No progress.
break
}
offset = offset + index + x
}
return false
}
// Retuns substring and offset/limit pair in bytes.
func (t Tree) offsetLimit(s string) (offset, limit int) {
n := utf8.RuneCountInString(s)
if t.runes > n {
return 0, 0
}
if n := t.lrunes; n > 0 {
offset = len(runes.Head(s, n))
}
if n := t.rrunes; n > 0 {
limit = len(runes.Tail(s, n))
}
return
}
func (t Tree) String() string {
return fmt.Sprintf(
"<btree:[%v<-%s->%v]>",
t.left, t.value, t.right,
)
}