package substring import ( "bytes" "regexp" "github.com/toqueteos/trie" ) type BytesMatcher interface { Match(b []byte) bool MatchIndex(b []byte) int } // regexp type regexpBytes struct{ re *regexp.Regexp } func BytesRegexp(pat string) *regexpBytes { return ®expBytes{regexp.MustCompile(pat)} } func (m *regexpBytes) Match(b []byte) bool { return m.re.Match(b) } func (m *regexpBytes) MatchIndex(b []byte) int { found := m.re.FindIndex(b) if found != nil { return found[1] } return -1 } // exact type exactBytes struct{ pat []byte } func BytesExact(pat string) *exactBytes { return &exactBytes{[]byte(pat)} } func (m *exactBytes) Match(b []byte) bool { l, r := len(m.pat), len(b) if l != r { return false } for i := 0; i < l; i++ { if b[i] != m.pat[i] { return false } } return true } func (m *exactBytes) MatchIndex(b []byte) int { if m.Match(b) { return len(b) } return -1 } // any, search `s` in `.Match(pat)` type anyBytes struct { pat []byte } func BytesAny(pat string) *anyBytes { return &anyBytes{[]byte(pat)} } func (m *anyBytes) Match(b []byte) bool { return bytes.Index(m.pat, b) >= 0 } func (m *anyBytes) MatchIndex(b []byte) int { if idx := bytes.Index(m.pat, b); idx >= 0 { return idx + len(b) } return -1 } // has, search `pat` in `.Match(s)` type hasBytes struct { pat []byte } func BytesHas(pat string) *hasBytes { return &hasBytes{[]byte(pat)} } func (m *hasBytes) Match(b []byte) bool { return bytes.Index(b, m.pat) >= 0 } func (m *hasBytes) MatchIndex(b []byte) int { if idx := bytes.Index(b, m.pat); idx >= 0 { return idx + len(m.pat) } return -1 } // prefix type prefixBytes struct{ pat []byte } func BytesPrefix(pat string) *prefixBytes { return &prefixBytes{[]byte(pat)} } func (m *prefixBytes) Match(b []byte) bool { return bytes.HasPrefix(b, m.pat) } func (m *prefixBytes) MatchIndex(b []byte) int { if bytes.HasPrefix(b, m.pat) { return len(m.pat) } return -1 } // prefixes type prefixesBytes struct { t *trie.Trie } func BytesPrefixes(pats ...string) *prefixesBytes { t := trie.New() for _, pat := range pats { t.Insert([]byte(pat)) } return &prefixesBytes{t} } func (m *prefixesBytes) Match(b []byte) bool { return m.t.PrefixIndex(b) >= 0 } func (m *prefixesBytes) MatchIndex(b []byte) int { if idx := m.t.PrefixIndex(b); idx >= 0 { return idx } return -1 } // suffix type suffixBytes struct{ pat []byte } func BytesSuffix(pat string) *suffixBytes { return &suffixBytes{[]byte(pat)} } func (m *suffixBytes) Match(b []byte) bool { return bytes.HasSuffix(b, m.pat) } func (m *suffixBytes) MatchIndex(b []byte) int { if bytes.HasSuffix(b, m.pat) { return len(m.pat) } return -1 } // suffixes type suffixesBytes struct { t *trie.Trie } func BytesSuffixes(pats ...string) *suffixesBytes { t := trie.New() for _, pat := range pats { t.Insert(reverse([]byte(pat))) } return &suffixesBytes{t} } func (m *suffixesBytes) Match(b []byte) bool { return m.t.PrefixIndex(reverse(b)) >= 0 } func (m *suffixesBytes) MatchIndex(b []byte) int { if idx := m.t.PrefixIndex(reverse(b)); idx >= 0 { return idx } return -1 } // after type afterBytes struct { first []byte matcher BytesMatcher } func BytesAfter(first string, m BytesMatcher) *afterBytes { return &afterBytes{[]byte(first), m} } func (a *afterBytes) Match(b []byte) bool { if idx := bytes.Index(b, a.first); idx >= 0 { return a.matcher.Match(b[idx+len(a.first):]) } return false } func (a *afterBytes) MatchIndex(b []byte) int { if idx := bytes.Index(b, a.first); idx >= 0 { return idx + a.matcher.MatchIndex(b[idx:]) } return -1 } // and, returns true iff all matchers return true type andBytes struct{ matchers []BytesMatcher } func BytesAnd(m ...BytesMatcher) *andBytes { return &andBytes{m} } func (a *andBytes) Match(b []byte) bool { for _, m := range a.matchers { if !m.Match(b) { return false } } return true } func (a *andBytes) MatchIndex(b []byte) int { longest := 0 for _, m := range a.matchers { if idx := m.MatchIndex(b); idx < 0 { return -1 } else if idx > longest { longest = idx } } return longest } // or, returns true iff any matcher returns true type orBytes struct{ matchers []BytesMatcher } func BytesOr(m ...BytesMatcher) *orBytes { return &orBytes{m} } func (o *orBytes) Match(b []byte) bool { for _, m := range o.matchers { if m.Match(b) { return true } } return false } func (o *orBytes) MatchIndex(b []byte) int { for _, m := range o.matchers { if idx := m.MatchIndex(b); idx >= 0 { return idx } } return -1 } type suffixGroupBytes struct { suffix BytesMatcher matchers []BytesMatcher } func BytesSuffixGroup(s string, m ...BytesMatcher) *suffixGroupBytes { return &suffixGroupBytes{BytesSuffix(s), m} } func (sg *suffixGroupBytes) Match(b []byte) bool { if sg.suffix.Match(b) { return BytesOr(sg.matchers...).Match(b) } return false } func (sg *suffixGroupBytes) MatchIndex(b []byte) int { if sg.suffix.MatchIndex(b) >= 0 { return BytesOr(sg.matchers...).MatchIndex(b) } return -1 }