1 package internal 2 3 import "strconv" 4 5 // LocalBuffer is a simplified equivalent to bytes.Buffer that allows usage of a preallocated local 6 // byte slice. 7 // 8 // The bytes.Buffer type is very efficient except in one regard: it must always allocate the byte 9 // slice on the heap, since its internal "buf" field starts out as nil and cannot be directly modified. 10 // LocalBuffer exports this field so it can be initialized to a slice which, if allowed by Go's escape 11 // analysis, can remain on the stack as long as its capacity is not exceeded. For example: 12 // 13 // buf := LocalBuffer{Data: make([]byte, 0, 100)} 14 // buf.AppendString("some data") // etc. 15 // result := buf.Data 16 // 17 // In the example, as long as no more than 100 bytes are written to the buffer, there are no heap 18 // allocations. As soon as the capacity is exceeded, the byte slice is moved to the heap and its 19 // capacity expands exponentially, similar to bytes.Buffer. 20 // 21 // To simplify the API, the LocalBuffer methods return nothing; that's why they're named Append 22 // rather than Write, because Write has a connotation of imitating the io.Writer API. 23 type LocalBuffer struct { 24 // Data is the current content of the buffer. 25 Data []byte 26 } 27 28 // Append appends a byte slice to the buffer. 29 func (b *LocalBuffer) Append(data []byte) { 30 oldLen := b.grow(len(data)) 31 copy(b.Data[oldLen:], data) 32 } 33 34 // AppendByte appends a single byte to the buffer. 35 func (b *LocalBuffer) AppendByte(ch byte) { //nolint:stdmethods 36 oldLen := b.grow(1) 37 b.Data[oldLen] = ch 38 } 39 40 // AppendInt appends the base-10 string representation of an integer to the buffer. 41 func (b *LocalBuffer) AppendInt(n int) { 42 temp := make([]byte, 0, 20) 43 temp = strconv.AppendInt(temp, int64(n), 10) 44 b.Append(temp) 45 } 46 47 // AppendString appends the bytes of a string to the buffer. 48 func (b *LocalBuffer) AppendString(s string) { 49 oldLen := b.grow(len(s)) 50 copy(b.Data[oldLen:], s) 51 } 52 53 func (b *LocalBuffer) grow(addBytes int) int { 54 oldLen := len(b.Data) 55 newLen := oldLen + addBytes 56 if cap(b.Data) >= newLen { 57 b.Data = b.Data[0:newLen] 58 } else { 59 newCap := cap(b.Data) * 2 60 if newCap < newLen { 61 newCap = newLen * 2 62 } 63 newData := make([]byte, newLen, newCap) 64 copy(newData[0:oldLen], b.Data) 65 b.Data = newData 66 } 67 return oldLen 68 } 69