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 }