github.com/selefra/selefra-utils@v0.0.4/pkg/string_builder/string_builder.go (about)

     1  package string_builder
     2  
     3  import (
     4  	"strconv"
     5  	"unicode/utf8"
     6  	"unsafe"
     7  )
     8  
     9  // StringBuilder A Builder is used to efficiently build a string using Write methods.
    10  // It minimizes memory copying. The zero value is ready to use.
    11  // Do not copy a non-zero Builder.
    12  type StringBuilder struct {
    13  	addr *StringBuilder // of receiver, to detect copies by value
    14  	buf  []byte
    15  }
    16  
    17  func New(initCap ...int) *StringBuilder {
    18  	b := &StringBuilder{}
    19  	if len(initCap) > 0 {
    20  		b = b.Grow(initCap[0])
    21  	}
    22  	return b
    23  }
    24  
    25  // noescape hides a pointer from escape analysis.  noescape is
    26  // the identity function but escape analysis doesn't think the
    27  // output depends on the input. noescape is inlined and currently
    28  // compiles down to zero instructions.
    29  // USE CAREFULLY!
    30  // This was copied from the runtime; see issues 23382 and 7921.
    31  //go:nosplit
    32  //go:nocheckptr
    33  func noescape(p unsafe.Pointer) unsafe.Pointer {
    34  	x := uintptr(p)
    35  	return unsafe.Pointer(x ^ 0)
    36  }
    37  
    38  func (b *StringBuilder) copyCheck() {
    39  	if b.addr == nil {
    40  		// This hack works around a failing of Go's escape analysis
    41  		// that was causing b to escape and be heap allocated.
    42  		// See issue 23382.
    43  		// TODO: once issue 7921 is fixed, this should be reverted to
    44  		// just "b.addr = b".
    45  		b.addr = (*StringBuilder)(noescape(unsafe.Pointer(b)))
    46  	} else if b.addr != b {
    47  		panic("strings: illegal use of non-zero Builder copied by value")
    48  	}
    49  }
    50  
    51  // String returns the accumulated string.
    52  func (b *StringBuilder) String() string {
    53  	return *(*string)(unsafe.Pointer(&b.buf))
    54  }
    55  
    56  // Len returns the number of accumulated bytes; b.Len() == len(b.String()).
    57  func (b *StringBuilder) Len() int { return len(b.buf) }
    58  
    59  // Cap returns the capacity of the builder's underlying byte slice. It is the
    60  // total space allocated for the string being built and includes any bytes
    61  // already written.
    62  func (b *StringBuilder) Cap() int { return cap(b.buf) }
    63  
    64  // Reset resets the Builder to be empty.
    65  func (b *StringBuilder) Reset() *StringBuilder {
    66  	b.addr = nil
    67  	b.buf = nil
    68  	return b
    69  }
    70  
    71  // grow copies the buffer to a new, larger buffer so that there are at least n
    72  // bytes of capacity beyond len(b.buf).
    73  func (b *StringBuilder) grow(n int) {
    74  	buf := make([]byte, len(b.buf), 2*cap(b.buf)+n)
    75  	copy(buf, b.buf)
    76  	b.buf = buf
    77  }
    78  
    79  // Grow grows b's capacity, if necessary, to guarantee space for
    80  // another n bytes. After Grow(n), at least n bytes can be written to b
    81  // without another allocation. If n is negative, Grow panics.
    82  func (b *StringBuilder) Grow(n int) *StringBuilder {
    83  	b.copyCheck()
    84  	if n < 0 {
    85  		panic("strings.Builder.Grow: negative count")
    86  	}
    87  	if cap(b.buf)-len(b.buf) < n {
    88  		b.grow(n)
    89  	}
    90  	return b
    91  }
    92  
    93  // Write appends the contents of p to b's buffer.
    94  // Write always returns len(p), nil.
    95  func (b *StringBuilder) Write(p []byte) (int, error) {
    96  	b.copyCheck()
    97  	b.buf = append(b.buf, p...)
    98  	return len(p), nil
    99  }
   100  
   101  func (b *StringBuilder) Append(p []byte) *StringBuilder {
   102  	_, _ = b.Write(p)
   103  	return b
   104  }
   105  
   106  // WriteByte appends the byte c to b's buffer.
   107  // The returned error is always nil.
   108  func (b *StringBuilder) WriteByte(c byte) error {
   109  	b.copyCheck()
   110  	b.buf = append(b.buf, c)
   111  	return nil
   112  }
   113  
   114  func (b *StringBuilder) AppendByte(c byte) *StringBuilder {
   115  	_ = b.WriteByte(c)
   116  	return b
   117  }
   118  
   119  // WriteRune appends the UTF-8 encoding of Unicode code point r to b's buffer.
   120  // It returns the length of r and a nil error.
   121  func (b *StringBuilder) WriteRune(r rune) (int, error) {
   122  	b.copyCheck()
   123  	// Compare as uint32 to correctly handle negative runes.
   124  	if uint32(r) < utf8.RuneSelf {
   125  		b.buf = append(b.buf, byte(r))
   126  		return 1, nil
   127  	}
   128  	l := len(b.buf)
   129  	if cap(b.buf)-l < utf8.UTFMax {
   130  		b.grow(utf8.UTFMax)
   131  	}
   132  	n := utf8.EncodeRune(b.buf[l:l+utf8.UTFMax], r)
   133  	b.buf = b.buf[:l+n]
   134  	return n, nil
   135  }
   136  
   137  func (b *StringBuilder) AppendRune(r rune) *StringBuilder {
   138  	_, _ = b.WriteRune(r)
   139  	return b
   140  }
   141  
   142  // WriteString appends the contents of s to b's buffer.
   143  // It returns the length of s and a nil error.
   144  func (b *StringBuilder) WriteString(s string) (int, error) {
   145  	b.copyCheck()
   146  	b.buf = append(b.buf, s...)
   147  	return len(s), nil
   148  }
   149  
   150  func (b *StringBuilder) AppendString(s string) *StringBuilder {
   151  	_, _ = b.WriteString(s)
   152  	return b
   153  }
   154  
   155  func (b *StringBuilder) AppendInt(n int) *StringBuilder {
   156  	b.AppendString(strconv.Itoa(n))
   157  	return b
   158  }