summaryrefslogtreecommitdiff
path: root/libgo/go/path/path.go
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/path/path.go')
-rw-r--r--libgo/go/path/path.go80
1 files changed, 56 insertions, 24 deletions
diff --git a/libgo/go/path/path.go b/libgo/go/path/path.go
index b07534b36f4..bdb85c6b92a 100644
--- a/libgo/go/path/path.go
+++ b/libgo/go/path/path.go
@@ -10,6 +10,43 @@ import (
"strings"
)
+// A lazybuf is a lazily constructed path buffer.
+// It supports append, reading previously appended bytes,
+// and retrieving the final string. It does not allocate a buffer
+// to hold the output until that output diverges from s.
+type lazybuf struct {
+ s string
+ buf []byte
+ w int
+}
+
+func (b *lazybuf) index(i int) byte {
+ if b.buf != nil {
+ return b.buf[i]
+ }
+ return b.s[i]
+}
+
+func (b *lazybuf) append(c byte) {
+ if b.buf == nil {
+ if b.w < len(b.s) && b.s[b.w] == c {
+ b.w++
+ return
+ }
+ b.buf = make([]byte, len(b.s))
+ copy(b.buf, b.s[:b.w])
+ }
+ b.buf[b.w] = c
+ b.w++
+}
+
+func (b *lazybuf) string() string {
+ if b.buf == nil {
+ return b.s[:b.w]
+ }
+ return string(b.buf[:b.w])
+}
+
// Clean returns the shortest path name equivalent to path
// by purely lexical processing. It applies the following rules
// iteratively until no further processing can be done:
@@ -42,10 +79,11 @@ func Clean(path string) string {
// writing to buf; w is index of next byte to write.
// dotdot is index in buf where .. must stop, either because
// it is the leading slash or it is a leading ../../.. prefix.
- buf := []byte(path)
- r, w, dotdot := 0, 0, 0
+ out := lazybuf{s: path}
+ r, dotdot := 0, 0
if rooted {
- r, w, dotdot = 1, 1, 1
+ out.append('/')
+ r, dotdot = 1, 1
}
for r < n {
@@ -60,46 +98,40 @@ func Clean(path string) string {
// .. element: remove to last /
r += 2
switch {
- case w > dotdot:
+ case out.w > dotdot:
// can backtrack
- w--
- for w > dotdot && buf[w] != '/' {
- w--
+ out.w--
+ for out.w > dotdot && out.index(out.w) != '/' {
+ out.w--
}
case !rooted:
// cannot backtrack, but not rooted, so append .. element.
- if w > 0 {
- buf[w] = '/'
- w++
+ if out.w > 0 {
+ out.append('/')
}
- buf[w] = '.'
- w++
- buf[w] = '.'
- w++
- dotdot = w
+ out.append('.')
+ out.append('.')
+ dotdot = out.w
}
default:
// real path element.
// add slash if needed
- if rooted && w != 1 || !rooted && w != 0 {
- buf[w] = '/'
- w++
+ if rooted && out.w != 1 || !rooted && out.w != 0 {
+ out.append('/')
}
// copy element
for ; r < n && path[r] != '/'; r++ {
- buf[w] = path[r]
- w++
+ out.append(path[r])
}
}
}
// Turn empty string into "."
- if w == 0 {
- buf[w] = '.'
- w++
+ if out.w == 0 {
+ return "."
}
- return string(buf[0:w])
+ return out.string()
}
// Split splits path immediately following the final slash.