github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/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/nicocha30/gvisor-ligolo/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  }