v0.1.0 initial commit
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
package filter
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Pattern is a smart-case matcher: an all-lowercase pattern matches
|
||||
// case-insensitively, any upper-case character makes it case-sensitive.
|
||||
// A pattern free of regexp metacharacters takes an allocation-free
|
||||
// substring path; everything else compiles to stdlib RE2.
|
||||
type Pattern struct {
|
||||
Src string
|
||||
re *regexp.Regexp
|
||||
lit []byte
|
||||
fold bool
|
||||
}
|
||||
|
||||
// NewPattern compiles s; an empty s yields an inert pattern.
|
||||
func NewPattern(s string) (Pattern, error) {
|
||||
p := Pattern{Src: s, fold: s == strings.ToLower(s)}
|
||||
if s == "" {
|
||||
return p, nil
|
||||
}
|
||||
if regexp.QuoteMeta(s) == s {
|
||||
if p.fold {
|
||||
p.lit = []byte(strings.ToLower(s))
|
||||
} else {
|
||||
p.lit = []byte(s)
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
expr := s
|
||||
if p.fold {
|
||||
expr = "(?i)" + s
|
||||
}
|
||||
re, err := regexp.Compile(expr)
|
||||
if err != nil {
|
||||
return Pattern{}, err
|
||||
}
|
||||
p.re = re
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// Empty reports whether the pattern constrains nothing.
|
||||
func (p Pattern) Empty() bool { return p.Src == "" }
|
||||
|
||||
// MatchBytes reports whether b contains a match.
|
||||
func (p Pattern) MatchBytes(b []byte) bool {
|
||||
switch {
|
||||
case p.Src == "":
|
||||
return true
|
||||
case p.lit != nil && p.fold:
|
||||
return foldContains(b, p.lit)
|
||||
case p.lit != nil:
|
||||
return bytes.Contains(b, p.lit)
|
||||
default:
|
||||
return p.re.Match(b)
|
||||
}
|
||||
}
|
||||
|
||||
// MatchString reports whether s contains a match.
|
||||
func (p Pattern) MatchString(s string) bool {
|
||||
switch {
|
||||
case p.Src == "":
|
||||
return true
|
||||
case p.lit != nil && p.fold:
|
||||
return foldContains([]byte(s), p.lit)
|
||||
case p.lit != nil:
|
||||
return strings.Contains(s, string(p.lit))
|
||||
default:
|
||||
return p.re.MatchString(s)
|
||||
}
|
||||
}
|
||||
|
||||
func lowerASCII(c byte) byte {
|
||||
if c >= 'A' && c <= 'Z' {
|
||||
return c + 'a' - 'A'
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// foldContains reports whether hay contains needle, ASCII case-insensitively.
|
||||
// needle must already be lowercased.
|
||||
func foldContains(hay, needle []byte) bool {
|
||||
if len(needle) == 0 {
|
||||
return true
|
||||
}
|
||||
if len(hay) < len(needle) {
|
||||
return false
|
||||
}
|
||||
first := needle[0]
|
||||
for i := 0; i+len(needle) <= len(hay); i++ {
|
||||
if lowerASCII(hay[i]) != first {
|
||||
continue
|
||||
}
|
||||
match := true
|
||||
for j := 1; j < len(needle); j++ {
|
||||
if lowerASCII(hay[i+j]) != needle[j] {
|
||||
match = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if match {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
Reference in New Issue
Block a user