Skip to content

Commit 8e8766a

Browse files
authored
perf(tspath): optimize hasRelativePathSegment (#1569)
1 parent ae0b6e5 commit 8e8766a

File tree

1 file changed

+56
-11
lines changed

1 file changed

+56
-11
lines changed

internal/tspath/path.go

Lines changed: 56 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -470,28 +470,73 @@ func simpleNormalizePath(path string) (string, bool) {
470470
return "", false
471471
}
472472

473+
// hasRelativePathSegment reports whether p contains ".", "..", "./", "../", "/.", "/..", "//", "/./", or "/../".
473474
func hasRelativePathSegment(p string) bool {
474-
if p == "." || p == ".." {
475-
return true
475+
n := len(p)
476+
if n == 0 {
477+
return false
476478
}
477479

478-
if strings.HasPrefix(p, "./") || strings.HasPrefix(p, "../") {
480+
if p == "." || p == ".." {
479481
return true
480482
}
481483

482-
if strings.HasSuffix(p, "/.") || strings.HasSuffix(p, "/..") {
483-
return true
484+
// Leading "./" OR "../"
485+
if p[0] == '.' {
486+
if n >= 2 && p[1] == '/' {
487+
return true
488+
}
489+
// Leading "../"
490+
if n >= 3 && p[1] == '.' && p[2] == '/' {
491+
return true
492+
}
484493
}
485-
486-
if strings.Contains(p, "//") {
487-
return true
494+
// Trailing "/." OR "/.."
495+
if p[n-1] == '.' {
496+
if n >= 2 && p[n-2] == '/' {
497+
return true
498+
}
499+
if n >= 3 && p[n-2] == '.' && p[n-3] == '/' {
500+
return true
501+
}
488502
}
489503

490-
if strings.Contains(p, "/./") || strings.Contains(p, "/../") {
491-
return true
504+
// Now look for any `//` or `/./` or `/../`
505+
506+
prevSlash := false
507+
segLen := 0 // length of current segment since last slash
508+
dotCount := 0 // consecutive dots at start of the current segment; -1 => not only dots
509+
510+
for i := range n {
511+
c := p[i]
512+
if c == '/' {
513+
// "//"
514+
if prevSlash {
515+
return true
516+
}
517+
// "/./" or "/../"
518+
if (segLen == 1 && dotCount == 1) || (segLen == 2 && dotCount == 2) {
519+
return true
520+
}
521+
prevSlash = true
522+
segLen = 0
523+
dotCount = 0
524+
continue
525+
}
526+
527+
if c == '.' {
528+
if dotCount >= 0 {
529+
dotCount++
530+
}
531+
} else {
532+
dotCount = -1
533+
}
534+
segLen++
535+
prevSlash = false
492536
}
493537

494-
return false
538+
// Trailing "/." or "/.."
539+
return (segLen == 1 && dotCount == 1) || (segLen == 2 && dotCount == 2)
495540
}
496541

497542
func NormalizePath(path string) string {

0 commit comments

Comments
 (0)