// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Package strbuilder defines a string builder type that allocates // less than the standard library's strings.Builder by using a // sync.Pool, so it doesn't matter if the compiler can't prove that // the builder doesn't escape into the fmt package, etc. package strbuilder import ( "bytes" "strconv" "sync" ) var pool = sync.Pool{ New: func() interface{} { return new(Builder) }, } type Builder struct { bb bytes.Buffer scratch [20]byte // long enough for MinInt64, MaxUint64 locked bool // in pool, not for use } // Get returns a new or reused string Builder. func Get() *Builder { b := pool.Get().(*Builder) b.bb.Reset() b.locked = false return b } // String both returns the Builder's string, and returns the builder // to the pool. func (b *Builder) String() string { if b.locked { panic("String called twiced on Builder") } s := b.bb.String() b.locked = true pool.Put(b) return s } func (b *Builder) WriteByte(v byte) error { return b.bb.WriteByte(v) } func (b *Builder) WriteString(s string) (int, error) { return b.bb.WriteString(s) } func (b *Builder) Write(p []byte) (int, error) { return b.bb.Write(p) } func (b *Builder) WriteInt(v int64) { b.Write(strconv.AppendInt(b.scratch[:0], v, 10)) } func (b *Builder) WriteUint(v uint64) { b.Write(strconv.AppendUint(b.scratch[:0], v, 10)) } // Grow grows the buffer's capacity, if necessary, to guarantee space // for another n bytes. After Grow(n), at least n bytes can be written // to the buffer without another allocation. If n is negative, Grow // will panic. If the buffer can't grow it will panic with // ErrTooLarge. func (b *Builder) Grow(n int) { b.bb.Grow(n) }