From 54ff68b0a9b40a0becf32737500d9d8cdc9c836a Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sat, 16 May 2026 11:10:53 +0000 Subject: [PATCH] refactor(glob): use strings.Builder for regexp compilation (#37730) Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: wxiaoguang <2114189+wxiaoguang@users.noreply.github.com> Co-authored-by: wxiaoguang --- modules/glob/glob.go | 80 ++++++++++++++++++++++++-------------------- 1 file changed, 43 insertions(+), 37 deletions(-) diff --git a/modules/glob/glob.go b/modules/glob/glob.go index 5e5593af1c..387a305af3 100644 --- a/modules/glob/glob.go +++ b/modules/glob/glob.go @@ -8,6 +8,7 @@ import ( "fmt" "regexp" "slices" + "strings" "code.gitea.io/gitea/modules/util" ) @@ -29,16 +30,17 @@ type globCompiler struct { globPattern []rune regexpPattern string regexp *regexp.Regexp + builder *strings.Builder pos int negativeFlip bool } // compileChars compiles character class patterns like [abc] or [!abc] -func (g *globCompiler) compileChars() (string, error) { - result := "" +func (g *globCompiler) compileChars() error { + g.builder.WriteByte('[') if g.pos < len(g.globPattern) && g.globPattern[g.pos] == '!' { g.pos++ - result += "^" + g.builder.WriteByte('^') } for g.pos < len(g.globPattern) { @@ -46,33 +48,34 @@ func (g *globCompiler) compileChars() (string, error) { g.pos++ if c == ']' { - return "[" + result + "]", nil + g.builder.WriteByte(']') + return nil } if c == '\\' { if g.pos >= len(g.globPattern) { - return "", errors.New("unterminated character class escape") + return errors.New("unterminated character class escape") } - result += "\\" + string(g.globPattern[g.pos]) + g.builder.WriteByte('\\') + g.builder.WriteRune(g.globPattern[g.pos]) g.pos++ } else { - result += string(c) + g.builder.WriteRune(c) } } - return "", errors.New("unterminated character class") + return errors.New("unterminated character class") } // compile compiles the glob pattern into a regular expression -func (g *globCompiler) compile(subPattern bool) (string, error) { - result := "" - +func (g *globCompiler) compile(subPattern bool) error { for g.pos < len(g.globPattern) { c := g.globPattern[g.pos] g.pos++ if subPattern && c == '}' { - return "(" + result + ")", nil + g.builder.WriteByte(')') + return nil } switch c { @@ -95,59 +98,61 @@ func (g *globCompiler) compile(subPattern bool) (string, error) { } else { g.pos++ } - result += ".*" // match any sequence of characters + g.builder.WriteString(".*") // match any sequence of characters } else { - result += g.nonSeparatorChars + "*" // match any sequence of non-separator characters + g.builder.WriteString(g.nonSeparatorChars) + g.builder.WriteByte('*') // match any sequence of non-separator characters } case '?': if g.regexpQuestion { - result += "?" + g.builder.WriteByte('?') } else { - result += g.nonSeparatorChars // match any single non-separator character + g.builder.WriteString(g.nonSeparatorChars) // match any single non-separator character } case '+': if g.regexpPlus { - result += "+" + g.builder.WriteByte('+') } else { - result += "\\" + string(c) + g.builder.WriteByte('\\') + g.builder.WriteRune(c) } case '[': - chars, err := g.compileChars() - if err != nil { - return "", err + if err := g.compileChars(); err != nil { + return err } - result += chars case '{': - subResult, err := g.compile(true) - if err != nil { - return "", err + g.builder.WriteByte('(') + if err := g.compile(true); err != nil { + return err } - result += subResult case ',': if subPattern { - result += "|" + g.builder.WriteByte('|') } else { - result += "," + g.builder.WriteByte(',') } case '\\': if g.pos >= len(g.globPattern) { - return "", errors.New("no character to escape") + return errors.New("no character to escape") } - result += "\\" + string(g.globPattern[g.pos]) + g.builder.WriteByte('\\') + g.builder.WriteRune(g.globPattern[g.pos]) g.pos++ case '.', '^', '$', '(', ')', '|': - result += "\\" + string(c) // escape regexp special characters + g.builder.WriteByte('\\') + g.builder.WriteRune(c) // escape regexp special characters default: - result += string(c) + g.builder.WriteRune(c) } } - return result, nil + return nil } func initGlobCompiler(g *globCompiler, pattern string, separators []rune) (Glob, error) { g.globPattern = []rune(pattern) g.separators = separators + g.builder = new(strings.Builder) // Escape separators for use in character class escapedSeparators := regexp.QuoteMeta(string(separators)) @@ -162,12 +167,13 @@ func initGlobCompiler(g *globCompiler, pattern string, separators []rune) (Glob, g.pos++ } - compiled, err := g.compile(false) - if err != nil { + g.builder.WriteByte('^') + if err := g.compile(false); err != nil { return nil, err } - - g.regexpPattern = "^" + compiled + "$" + g.builder.WriteByte('$') + g.regexpPattern = g.builder.String() + g.builder = nil regex, err := regexp.Compile(g.regexpPattern) if err != nil {