github.com/sagernet/gvisor@v0.0.0-20240428053021-e691de28565f/pkg/fspath/builder.go (about) 1 // Copyright 2019 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package fspath 16 17 import ( 18 "fmt" 19 20 "github.com/sagernet/gvisor/pkg/gohacks" 21 ) 22 23 // Builder is similar to strings.Builder, but is used to produce pathnames 24 // given path components in reverse order (from leaf to root). This is useful 25 // in the common case where a filesystem is represented by a tree of named 26 // nodes, and the path to a given node must be produced by walking upward from 27 // that node to a given root. 28 type Builder struct { 29 buf []byte 30 start int 31 needSep bool 32 } 33 34 // Reset resets the Builder to be empty. 35 func (b *Builder) Reset() { 36 b.start = len(b.buf) 37 b.needSep = false 38 } 39 40 // Len returns the number of accumulated bytes. 41 func (b *Builder) Len() int { 42 return len(b.buf) - b.start 43 } 44 45 func (b *Builder) needToGrow(n int) bool { 46 return b.start < n 47 } 48 49 func (b *Builder) grow(n int) { 50 newLen := b.Len() + n 51 var newCap int 52 if len(b.buf) == 0 { 53 newCap = 64 // arbitrary 54 } else { 55 newCap = 2 * len(b.buf) 56 } 57 for newCap < newLen { 58 newCap *= 2 59 if newCap == 0 { 60 panic(fmt.Sprintf("required length (%d) causes buffer size to overflow", newLen)) 61 } 62 } 63 newBuf := make([]byte, newCap) 64 copy(newBuf[newCap-b.Len():], b.buf[b.start:]) 65 b.start += newCap - len(b.buf) 66 b.buf = newBuf 67 } 68 69 // PrependComponent prepends the given path component to b's buffer. A path 70 // separator is automatically inserted if appropriate. 71 func (b *Builder) PrependComponent(pc string) { 72 if b.needSep { 73 b.PrependByte('/') 74 } 75 b.PrependString(pc) 76 b.needSep = true 77 } 78 79 // PrependString prepends the given string to b's buffer. 80 func (b *Builder) PrependString(str string) { 81 if b.needToGrow(len(str)) { 82 b.grow(len(str)) 83 } 84 b.start -= len(str) 85 copy(b.buf[b.start:], str) 86 } 87 88 // PrependByte prepends the given byte to b's buffer. 89 func (b *Builder) PrependByte(c byte) { 90 if b.needToGrow(1) { 91 b.grow(1) 92 } 93 b.start-- 94 b.buf[b.start] = c 95 } 96 97 // AppendString appends the given string to b's buffer. 98 func (b *Builder) AppendString(str string) { 99 if b.needToGrow(len(str)) { 100 b.grow(len(str)) 101 } 102 oldStart := b.start 103 b.start -= len(str) 104 copy(b.buf[b.start:], b.buf[oldStart:]) 105 copy(b.buf[len(b.buf)-len(str):], str) 106 } 107 108 // String returns the accumulated string. No other methods should be called 109 // after String. 110 func (b *Builder) String() string { 111 return gohacks.StringFromImmutableBytes(b.buf[b.start:]) 112 }