summaryrefslogtreecommitdiff
path: root/vendor/codeberg.org/gruf/go-fastpath/v2/path.go
diff options
context:
space:
mode:
authorLibravatar dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>2022-11-28 09:01:53 +0000
committerLibravatar GitHub <noreply@github.com>2022-11-28 09:01:53 +0000
commitfe39d50e09c1fcf5d13386e15951c5608e7df0e4 (patch)
tree8101dfc2573c43705c09189a3589693fdaf14124 /vendor/codeberg.org/gruf/go-fastpath/v2/path.go
parentfix missing lookup cache key for invalid domain block (#1158) (diff)
downloadgotosocial-fe39d50e09c1fcf5d13386e15951c5608e7df0e4.tar.xz
[chore]: Bump codeberg.org/gruf/go-store/v2 from 2.0.9 to 2.0.10 (#1160)
Bumps codeberg.org/gruf/go-store/v2 from 2.0.9 to 2.0.10. --- updated-dependencies: - dependency-name: codeberg.org/gruf/go-store/v2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Diffstat (limited to 'vendor/codeberg.org/gruf/go-fastpath/v2/path.go')
-rw-r--r--vendor/codeberg.org/gruf/go-fastpath/v2/path.go326
1 files changed, 326 insertions, 0 deletions
diff --git a/vendor/codeberg.org/gruf/go-fastpath/v2/path.go b/vendor/codeberg.org/gruf/go-fastpath/v2/path.go
new file mode 100644
index 000000000..42cbfd4f7
--- /dev/null
+++ b/vendor/codeberg.org/gruf/go-fastpath/v2/path.go
@@ -0,0 +1,326 @@
+package fastpath
+
+import (
+ "unsafe"
+)
+
+// Clean: see Builder.Clean(). Analogous to path.Clean().
+func Clean(path string) string {
+ return (&Builder{}).Clean(path)
+}
+
+// Join: see Builder.Join(). Analogous to path.Join().
+func Join(elems ...string) string {
+ return (&Builder{}).Join(elems...)
+}
+
+// Builder provides a means of cleaning and joining system paths,
+// while retaining a singular underlying byte buffer for performance.
+type Builder struct {
+ // B is the underlying byte buffer
+ B []byte
+
+ dd int // pos of last '..' appended to builder
+ abs bool // abs stores whether path passed to first .Append() is absolute
+ set bool // set stores whether b.abs has been set i.e. not first call to .Append()
+}
+
+// Reset resets the Builder object
+func (b *Builder) Reset() {
+ b.B = b.B[:0]
+ b.dd = 0
+ b.abs = false
+ b.set = false
+}
+
+// Len returns the number of accumulated bytes in the Builder
+func (b Builder) Len() int {
+ return len(b.B)
+}
+
+// Cap returns the capacity of the underlying Builder buffer
+func (b Builder) Cap() int {
+ return cap(b.B)
+}
+
+// Bytes returns the accumulated path bytes.
+func (b Builder) Bytes() []byte {
+ return b.B
+}
+
+// String returns the accumulated path string.
+func (b Builder) String() string {
+ return *(*string)(unsafe.Pointer(&b.B))
+}
+
+// Absolute returns whether current path is absolute (not relative).
+func (b Builder) Absolute() bool {
+ return b.abs
+}
+
+// SetAbsolute converts the current path to-or-from absolute.
+func (b *Builder) SetAbsolute(enabled bool) {
+ if !b.set {
+ // Ensure 1B avail
+ b.Guarantee(1)
+
+ if enabled {
+ // Set empty 'abs'
+ b.appendByte('/')
+ b.abs = true
+ } else {
+ // Set empty 'rel'
+ b.appendByte('.')
+ b.abs = false
+ }
+
+ b.set = true
+ return
+ }
+
+ if !enabled && b.abs {
+ // set && absolute
+ // -> update
+ b.abs = false
+
+ // If empty, set to '.' (empty rel path)
+ if len(b.B) == 0 || (len(b.B) == 1 && b.B[0] == '/') {
+ b.Guarantee(1)
+ b.B = b.B[:1]
+ b.B[0] = '.'
+ return
+ }
+
+ if b.B[0] != '/' {
+ // No need to change
+ return
+ }
+
+ if len(b.B) > 1 {
+ // Shift bytes 1 left
+ copy(b.B, b.B[1:])
+ }
+
+ // and drop the '/' prefix'
+ b.B = b.B[:len(b.B)-1]
+ } else if enabled && !b.abs {
+ // set && !absolute
+ // -> update
+ b.abs = true
+
+ // Ensure 1B avail
+ b.Guarantee(1)
+
+ // If empty, set to '/' (empty abs path)
+ if len(b.B) == 0 || (len(b.B) == 1 && b.B[0] == '.') {
+ b.Guarantee(1)
+ b.B = b.B[:1]
+ b.B[0] = '/'
+ return
+ }
+
+ // Increase length
+ l := len(b.B)
+ b.B = b.B[:l+1]
+
+ // Shift bytes 1 right
+ copy(b.B[1:], b.B[:l])
+
+ // Set first byte '/'
+ b.B[0] = '/'
+ }
+}
+
+// AppendBytes adds and cleans the supplied path bytes to the
+// builder's internal buffer, growing the buffer if necessary
+// to accomodate the extra path length.
+func (b *Builder) AppendBytes(path []byte) {
+ if len(path) == 0 {
+ return
+ }
+ b.Guarantee(len(path) + 1)
+ b.append(*(*string)(unsafe.Pointer(&b)))
+}
+
+// Append adds and cleans the supplied path string to the
+// builder's internal buffer, growing the buffer if necessary
+// to accomodate the extra path length.
+func (b *Builder) Append(path string) {
+ if len(path) == 0 {
+ return
+ }
+ b.Guarantee(len(path) + 1)
+ b.append(path)
+}
+
+// Clean creates the shortest possible functional equivalent
+// to the supplied path, resetting the builder before performing
+// this operation. The builder object is NOT reset after return.
+func (b *Builder) Clean(path string) string {
+ if path == "" {
+ return "."
+ }
+ b.Reset()
+ b.Guarantee(len(path) + 1)
+ b.append(path)
+ return string(b.B)
+}
+
+// Join connects and cleans multiple paths, resetting the builder before
+// performing this operation and returning the shortest possible combination
+// of all the supplied paths. The builder object is NOT reset after return.
+func (b *Builder) Join(elems ...string) string {
+ var size int
+ for _, elem := range elems {
+ size += len(elem)
+ }
+ if size == 0 {
+ return ""
+ }
+ b.Reset()
+ b.Guarantee(size + 1)
+ for _, elem := range elems {
+ if elem == "" {
+ continue
+ }
+ b.append(elem)
+ }
+ return string(b.B)
+}
+
+// append performs the main logic of 'Append()' but without an empty path check or preallocation.
+func (b *Builder) append(path string) {
+ if !b.set {
+ // Set if absolute or not
+ b.abs = path[0] == '/'
+ b.set = true
+ } else if !b.abs && len(b.B) == 1 && b.B[0] == '.' {
+ // Empty non-abs path segment, drop
+ // the period so not prefixed './'
+ b.B = b.B[:0]
+ }
+
+ for i := 0; i < len(path); {
+ switch {
+ // Empty path segment
+ case path[i] == '/':
+ i++
+
+ // Singular '.' path segment, treat as empty
+ case path[i] == '.' && (i+1 == len(path) || path[i+1] == '/'):
+ i++
+
+ // Backtrack segment
+ case path[i] == '.' && path[i+1] == '.' && (i+2 == len(path) || path[i+2] == '/'):
+ i += 2
+
+ switch {
+ // Check if it's possible to backtrack with
+ // our current state of the buffer. i.e. is
+ // our buffer length longer than the last
+ // '..' we placed?
+ case len(b.B) > b.dd:
+ b.backtrack()
+
+ // If we reached here, need to check if
+ // we can append '..' to the path buffer,
+ // which is ONLY when path is NOT absolute
+ case !b.abs:
+ if len(b.B) > 0 {
+ b.appendByte('/')
+ }
+ b.appendByte('.')
+ b.appendByte('.')
+ b.dd = len(b.B)
+ }
+
+ default:
+ if (b.abs && len(b.B) != 1) || (!b.abs && len(b.B) > 0) {
+ // Append path separator
+ b.appendByte('/')
+ }
+
+ // Append slice up to next '/'
+ i += b.appendSlice(path[i:])
+ }
+ }
+
+ if len(b.B) > 0 {
+ return
+ }
+
+ if b.abs {
+ // Empty absolute path => /
+ b.appendByte('/')
+ } else {
+ // Empty relative path => .
+ b.appendByte('.')
+ }
+}
+
+// Guarantee ensures there is at least the requested size
+// free bytes available in the buffer, reallocating if necessary
+func (b *Builder) Guarantee(size int) {
+ if size > cap(b.B)-len(b.B) {
+ nb := make([]byte, 2*cap(b.B)+size)
+ copy(nb, b.B)
+ b.B = nb[:len(b.B)]
+ }
+}
+
+// Truncate reduces the length of the buffer by the requested
+// number of bytes. If the byte slice is *effectively* empty,
+// i.e. absolute and "/" or relative and ".", it won't be truncated.
+func (b *Builder) Truncate(size int) {
+ if len(b.B) == 0 {
+ return
+ }
+
+ if len(b.B) == 1 && ((b.abs && b.B[0] == '/') ||
+ (!b.abs && b.B[0] == '.')) {
+ // *effectively* empty
+ return
+ }
+
+ // Truncate requested bytes
+ b.B = b.B[:len(b.B)-size]
+}
+
+// appendByte appends the supplied byte to the end of
+// the buffer. appending is achieved by continually reslicing the
+// buffer and setting the next byte-at-index, this is safe as guarantee()
+// will have been called beforehand
+func (b *Builder) appendByte(c byte) {
+ b.B = b.B[:len(b.B)+1]
+ b.B[len(b.B)-1] = c
+}
+
+// appendSlice appends the supplied string slice to
+// the end of the buffer and returns the number of indices
+// we were able to iterate before hitting a path separator '/'.
+// appending is achieved by continually reslicing the buffer
+// and setting the next byte-at-index, this is safe as guarantee()
+// will have been called beforehand
+func (b *Builder) appendSlice(slice string) int {
+ i := 0
+ for i < len(slice) && slice[i] != '/' {
+ b.B = b.B[:len(b.B)+1]
+ b.B[len(b.B)-1] = slice[i]
+ i++
+ }
+ return i
+}
+
+// backtrack reduces the end of the buffer back to the last
+// separating '/', or end of buffer
+func (b *Builder) backtrack() {
+ b.B = b.B[:len(b.B)-1]
+
+ for len(b.B)-1 > b.dd && b.B[len(b.B)-1] != '/' {
+ b.B = b.B[:len(b.B)-1]
+ }
+
+ if len(b.B) > 0 {
+ b.B = b.B[:len(b.B)-1]
+ }
+}