Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions frac/active_token_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,21 @@ func (tp *activeTokenProvider) FindContains(needle []byte) ([]uint32, error) {
return tids, nil
}

// FindSuffix finds tids of tokens which end with a provided suffix.
func (tp *activeTokenProvider) FindSuffix(suffix []byte) ([]uint32, error) {
if len(suffix) == 0 {
return nil, nil
}
var tids []uint32
for tid := tp.FirstTID(); tid <= tp.LastTID(); tid++ {
token := tp.GetToken(tid)
if len(token) >= len(suffix) && bytes.Equal(token[len(token)-len(suffix):], suffix) {
tids = append(tids, tid)
}
}
return tids, nil
}

// FindToken finds tids of tokens which suffice a provided searcher (predicate).
func (tp *activeTokenProvider) FindToken(searcher pattern.Searcher) ([]uint32, error) {
firstTID := searcher.FirstTID()
Expand Down
43 changes: 43 additions & 0 deletions frac/sealed/token/block_loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,49 @@ func (b *Block) containsV5(from, to int, needle []byte) ([]int, error) {
return indexes, nil
}

func (b *Block) suffix(from, to int, suffix []byte) ([]int, error) {
if b.FracVer >= config.BinaryDataV6 {
return b.suffixV6(from, to, suffix), nil
}

return b.suffixV5(from, to, suffix), nil
}

// TODO(cheb0) delete this when Block.FracVer is deleted
func (b *Block) suffixV5(from, to int, suffix []byte) []int {
indexes := make([]int, 0)
suffixLen := len(suffix)

for i := from; i <= to; i++ {
token := b.GetToken(i)
if len(token) >= suffixLen && bytes.Equal(token[len(token)-suffixLen:], suffix) {
indexes = append(indexes, i)
}
}

return indexes
}

func (b *Block) suffixV6(from, to int, suffix []byte) []int {
indexes := make([]int, 0)
suffixLen := uint32(len(suffix))

offsets := b.Offsets[from : to+2]

for i := 1; i < len(offsets); i++ {
endPos := offsets[i]
tokLen := endPos - offsets[i-1]
if tokLen >= suffixLen {
tokSuffix := b.Payload[endPos-suffixLen : endPos]
if bytes.Equal(tokSuffix, suffix) {
indexes = append(indexes, from+i-1)
}
}
}

return indexes
}

func (b *Block) find(from, to int, searcher pattern.Searcher) ([]int, error) {
indexes := make([]int, 0)
for i := from; i <= to; i++ {
Expand Down
14 changes: 14 additions & 0 deletions frac/sealed/token/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,20 @@ func (tp *Provider) FindContains(needle []byte) ([]uint32, error) {
})
}

func (tp *Provider) FindSuffix(suffix []byte) ([]uint32, error) {
requiredLetters := util.NewLettersBitset(suffix)

return tp.findInBlocks(
tp.FirstTID(),
tp.LastTID(),
func(e *TableEntry) bool {
return e.Letters.IsNil() || e.Letters.ContainsAll(requiredLetters)
},
func(b *Block, firstIndex, lastIndex int) ([]int, error) {
return b.suffix(firstIndex, lastIndex, suffix)
})
}

func (tp *Provider) FindToken(searcher pattern.Searcher) ([]uint32, error) {
return tp.findInBlocks(
searcher.FirstTID(),
Expand Down
16 changes: 16 additions & 0 deletions pattern/pattern.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
type tokenProvider interface {
GetToken(uint32) []byte
FindContains(needle []byte) ([]uint32, error)
FindSuffix(suffix []byte) ([]uint32, error)
FindToken(searcher Searcher) ([]uint32, error)
FirstTID() uint32
LastTID() uint32
Expand Down Expand Up @@ -424,13 +425,28 @@ func isSimpleWildcardContains(token parser.Token) (needle []byte, ok bool) {
return []byte(lit.Terms[1].Data), true
}

// isSimpleWildcardSuffix checks if this AST token is simple wildcard like '*abc'
func isSimpleWildcardSuffix(token parser.Token) (suffix []byte, ok bool) {
lit, ok := token.(*parser.Literal)
if !ok || len(lit.Terms) != 2 {
return nil, false
}
if !lit.Terms[0].IsWildcard() || lit.Terms[1].Kind != parser.TermText {
return nil, false
}
return []byte(lit.Terms[1].Data), true
}

func Search(ctx context.Context, t parser.Token, tp tokenProvider) ([]uint32, error) {
if util.IsCancelled(ctx) {
return nil, ctx.Err()
}
if needle, ok := isSimpleWildcardContains(t); ok {
return tp.FindContains(needle)
}
if suffix, ok := isSimpleWildcardSuffix(t); ok {
return tp.FindSuffix(suffix)
}
s := newSearcher(t, tp)
return tp.FindToken(s)
}
36 changes: 36 additions & 0 deletions pattern/pattern_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,20 @@ func (tp *simpleTokenProvider) FindContains(needle []byte) ([]uint32, error) {
return tids, nil
}

func (tp *simpleTokenProvider) FindSuffix(suffix []byte) ([]uint32, error) {
if len(suffix) == 0 {
return nil, nil
}
var tids []uint32
for t := tp.FirstTID(); t <= tp.LastTID(); t++ {
token := tp.GetToken(t)
if len(token) >= len(suffix) && bytes.Equal(token[len(token)-len(suffix):], suffix) {
tids = append(tids, t)
}
}
return tids, nil
}

func (tp *simpleTokenProvider) FindToken(searcher Searcher) ([]uint32, error) {
firstTID := searcher.FirstTID()
lastTID := searcher.LastTID()
Expand Down Expand Up @@ -335,6 +349,28 @@ func TestPatternSuffix2(t *testing.T) {
testAll(t, tp, tests)
}

func TestPatternSuffixOnly(t *testing.T) {
tp := newTestTokenProvider([]string{
"abc",
"xabc",
"xyabc",
"xyzabc",
"abcx",
"xabcx",
"notabc",
"nothing",
})

tests := []testCase{
{"*abc", []string{"abc", "notabc", "xabc", "xyabc", "xyzabc"}},
{"*x", []string{"abcx", "xabcx"}},
{"*ng", []string{"nothing"}},
{"*g", []string{"nothing"}},
}

testAll(t, tp, tests)
}

func TestPatternMiddle(t *testing.T) {
tp := newTestTokenProvider([]string{
"a:b:a",
Expand Down
Loading