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