1package helpers 2 3import ( 4 "bytes" 5 "strings" 6) 7 8// This provides an efficient way to join lots of big string and byte slices 9// together. It avoids the cost of repeatedly reallocating as the buffer grows 10// by measuring exactly how big the buffer should be and then allocating once. 11// This is a measurable speedup. 12type Joiner struct { 13 lastByte byte 14 strings []joinerString 15 bytes []joinerBytes 16 length uint32 17} 18 19type joinerString struct { 20 data string 21 offset uint32 22} 23 24type joinerBytes struct { 25 data []byte 26 offset uint32 27} 28 29func (j *Joiner) AddString(data string) { 30 if len(data) > 0 { 31 j.lastByte = data[len(data)-1] 32 } 33 j.strings = append(j.strings, joinerString{data, j.length}) 34 j.length += uint32(len(data)) 35} 36 37func (j *Joiner) AddBytes(data []byte) { 38 if len(data) > 0 { 39 j.lastByte = data[len(data)-1] 40 } 41 j.bytes = append(j.bytes, joinerBytes{data, j.length}) 42 j.length += uint32(len(data)) 43} 44 45func (j *Joiner) LastByte() byte { 46 return j.lastByte 47} 48 49func (j *Joiner) Length() uint32 { 50 return j.length 51} 52 53func (j *Joiner) EnsureNewlineAtEnd() { 54 if j.length > 0 && j.lastByte != '\n' { 55 j.AddString("\n") 56 } 57} 58 59func (j *Joiner) Done() []byte { 60 if len(j.strings) == 0 && len(j.bytes) == 1 && j.bytes[0].offset == 0 { 61 // No need to allocate if there was only a single byte array written 62 return j.bytes[0].data 63 } 64 buffer := make([]byte, j.length) 65 for _, item := range j.strings { 66 copy(buffer[item.offset:], item.data) 67 } 68 for _, item := range j.bytes { 69 copy(buffer[item.offset:], item.data) 70 } 71 return buffer 72} 73 74func (j *Joiner) Contains(s string, b []byte) bool { 75 for _, item := range j.strings { 76 if strings.Contains(item.data, s) { 77 return true 78 } 79 } 80 for _, item := range j.bytes { 81 if bytes.Contains(item.data, b) { 82 return true 83 } 84 } 85 return false 86} 87