diff options
author | 2022-11-28 09:01:53 +0000 | |
---|---|---|
committer | 2022-11-28 09:01:53 +0000 | |
commit | fe39d50e09c1fcf5d13386e15951c5608e7df0e4 (patch) | |
tree | 8101dfc2573c43705c09189a3589693fdaf14124 /vendor/codeberg.org/gruf/go-fastpath/v2/path.go | |
parent | fix missing lookup cache key for invalid domain block (#1158) (diff) | |
download | gotosocial-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.go | 326 |
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] + } +} |