From e943cdd3277ad21e34fc8813c413e0c35ac40e85 Mon Sep 17 00:00:00 2001 From: Alberto Carretero Date: Wed, 11 Dec 2024 10:41:41 +0100 Subject: [PATCH 1/3] feat: optimize common case of GlobPath --- internal/strdist/strdist.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/internal/strdist/strdist.go b/internal/strdist/strdist.go index f205bfcf..ac2b446a 100644 --- a/internal/strdist/strdist.go +++ b/internal/strdist/strdist.go @@ -105,6 +105,11 @@ func Distance(a, b string, f CostFunc, cut int64) int64 { // * - Any zero or more characters, except for / // ** - Any zero or more characters, including / func GlobPath(a, b string) bool { + if !wildcardPrefixMatch(a, b) { + // Fast path. + return false + } + a = strings.ReplaceAll(a, "**", "⁑") b = strings.ReplaceAll(b, "**", "⁑") return Distance(a, b, globCost, 1) == 0 @@ -125,3 +130,16 @@ func globCost(ar, br rune) Cost { } return Cost{SwapAB: 1, DeleteA: 1, InsertB: 1} } + +func wildcardPrefixMatch(a, b string) bool { + ai := strings.IndexAny(a, "*?") + bi := strings.IndexAny(b, "*?") + if ai == -1 { + ai = len(a) + } + if bi == -1 { + bi = len(b) + } + mini := min(ai, bi) + return a[:mini] == b[:mini] +} From 8b5c59ed7475967b7bad16e56c94d2a6a0d57703 Mon Sep 17 00:00:00 2001 From: Alberto Carretero Date: Wed, 11 Dec 2024 20:27:26 +0100 Subject: [PATCH 2/3] also optimize general suffix --- internal/strdist/strdist.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/internal/strdist/strdist.go b/internal/strdist/strdist.go index ac2b446a..7fd2d460 100644 --- a/internal/strdist/strdist.go +++ b/internal/strdist/strdist.go @@ -109,6 +109,10 @@ func GlobPath(a, b string) bool { // Fast path. return false } + if !wildcardSuffixMatch(a, b) { + // Fast path. + return false + } a = strings.ReplaceAll(a, "**", "⁑") b = strings.ReplaceAll(b, "**", "⁑") @@ -143,3 +147,18 @@ func wildcardPrefixMatch(a, b string) bool { mini := min(ai, bi) return a[:mini] == b[:mini] } + +func wildcardSuffixMatch(a, b string) bool { + ai := strings.LastIndexAny(a, "*?") + la := 0 + if ai != -1 { + la = len(a) - ai - 1 + } + lb := 0 + bi := strings.LastIndexAny(b, "*?") + if bi != -1 { + lb = len(b) - bi - 1 + } + minl := min(la, lb) + return a[len(a)-minl:] == b[len(b)-minl:] +} From 19829794e6454f78334c9a67b74d8abb9bc66b25 Mon Sep 17 00:00:00 2001 From: Alberto Carretero Date: Wed, 11 Dec 2024 20:33:44 +0100 Subject: [PATCH 3/3] docs --- internal/strdist/strdist.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/internal/strdist/strdist.go b/internal/strdist/strdist.go index 7fd2d460..d5b640ef 100644 --- a/internal/strdist/strdist.go +++ b/internal/strdist/strdist.go @@ -135,6 +135,9 @@ func globCost(ar, br rune) Cost { return Cost{SwapAB: 1, DeleteA: 1, InsertB: 1} } +// wildcardPrefixMatch compares whether the prefixes of a and b are equal up +// to the shortest one. The prefix is defined as the longest substring that +// starts at index 0 and does not contain a wildcard. func wildcardPrefixMatch(a, b string) bool { ai := strings.IndexAny(a, "*?") bi := strings.IndexAny(b, "*?") @@ -148,6 +151,9 @@ func wildcardPrefixMatch(a, b string) bool { return a[:mini] == b[:mini] } +// wildcardSuffixMatch compares whether the suffixes of a and b are equal up +// to the shortest one. The suffix is defined as the longest substring that ends +// at the string length and does not contain a wildcard. func wildcardSuffixMatch(a, b string) bool { ai := strings.LastIndexAny(a, "*?") la := 0