You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

119 lines
3.3 KiB

  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. package builder
  5. import (
  6. "unicode/utf8"
  7. "unsafe"
  8. )
  9. // A StringBuilder 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. // noescape hides a pointer from escape analysis. noescape is
  17. // the identity function but escape analysis doesn't think the
  18. // output depends on the input. noescape is inlined and currently
  19. // compiles down to zero instructions.
  20. // USE CAREFULLY!
  21. // This was copied from the runtime; see issues 23382 and 7921.
  22. //go:nosplit
  23. func noescape(p unsafe.Pointer) unsafe.Pointer {
  24. x := uintptr(p)
  25. return unsafe.Pointer(x ^ 0)
  26. }
  27. func (b *StringBuilder) copyCheck() {
  28. if b.addr == nil {
  29. // This hack works around a failing of Go's escape analysis
  30. // that was causing b to escape and be heap allocated.
  31. // See issue 23382.
  32. // TODO: once issue 7921 is fixed, this should be reverted to
  33. // just "b.addr = b".
  34. b.addr = (*StringBuilder)(noescape(unsafe.Pointer(b)))
  35. } else if b.addr != b {
  36. panic("strings: illegal use of non-zero Builder copied by value")
  37. }
  38. }
  39. // String returns the accumulated string.
  40. func (b *StringBuilder) String() string {
  41. return *(*string)(unsafe.Pointer(&b.buf))
  42. }
  43. // Len returns the number of accumulated bytes; b.Len() == len(b.String()).
  44. func (b *StringBuilder) Len() int { return len(b.buf) }
  45. // Reset resets the Builder to be empty.
  46. func (b *StringBuilder) Reset() {
  47. b.addr = nil
  48. b.buf = nil
  49. }
  50. // grow copies the buffer to a new, larger buffer so that there are at least n
  51. // bytes of capacity beyond len(b.buf).
  52. func (b *StringBuilder) grow(n int) {
  53. buf := make([]byte, len(b.buf), 2*cap(b.buf)+n)
  54. copy(buf, b.buf)
  55. b.buf = buf
  56. }
  57. // Grow grows b's capacity, if necessary, to guarantee space for
  58. // another n bytes. After Grow(n), at least n bytes can be written to b
  59. // without another allocation. If n is negative, Grow panics.
  60. func (b *StringBuilder) Grow(n int) {
  61. b.copyCheck()
  62. if n < 0 {
  63. panic("strings.Builder.Grow: negative count")
  64. }
  65. if cap(b.buf)-len(b.buf) < n {
  66. b.grow(n)
  67. }
  68. }
  69. // Write appends the contents of p to b's buffer.
  70. // Write always returns len(p), nil.
  71. func (b *StringBuilder) Write(p []byte) (int, error) {
  72. b.copyCheck()
  73. b.buf = append(b.buf, p...)
  74. return len(p), nil
  75. }
  76. // WriteByte appends the byte c to b's buffer.
  77. // The returned error is always nil.
  78. func (b *StringBuilder) WriteByte(c byte) error {
  79. b.copyCheck()
  80. b.buf = append(b.buf, c)
  81. return nil
  82. }
  83. // WriteRune appends the UTF-8 encoding of Unicode code point r to b's buffer.
  84. // It returns the length of r and a nil error.
  85. func (b *StringBuilder) WriteRune(r rune) (int, error) {
  86. b.copyCheck()
  87. if r < utf8.RuneSelf {
  88. b.buf = append(b.buf, byte(r))
  89. return 1, nil
  90. }
  91. l := len(b.buf)
  92. if cap(b.buf)-l < utf8.UTFMax {
  93. b.grow(utf8.UTFMax)
  94. }
  95. n := utf8.EncodeRune(b.buf[l:l+utf8.UTFMax], r)
  96. b.buf = b.buf[:l+n]
  97. return n, nil
  98. }
  99. // WriteString appends the contents of s to b's buffer.
  100. // It returns the length of s and a nil error.
  101. func (b *StringBuilder) WriteString(s string) (int, error) {
  102. b.copyCheck()
  103. b.buf = append(b.buf, s...)
  104. return len(s), nil
  105. }