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  }