1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
|
package cat
import (
"strings"
)
// Builder is a fluent concatenation helper. It is safe for concurrent use by
// multiple goroutines only if each goroutine uses a distinct *Builder.
// If pooling is enabled via Pool(true), call Release() when done.
// The Builder uses an internal strings.Builder for efficient string concatenation
// and manages a separator that is inserted between added values.
// It supports chaining methods for a fluent API style.
type Builder struct {
buf strings.Builder
sep string
needsSep bool
}
// New begins a new Builder with a separator. If pooling is enabled,
// the Builder is reused and MUST be released with b.Release() when done.
// If sep is empty, uses DefaultSep().
// Optional initial arguments x are added immediately after creation.
// Pooling is controlled globally via Pool(true/false); when enabled, Builders
// are recycled to reduce allocations in high-throughput scenarios.
func New(sep string, x ...any) *Builder {
var b *Builder
if poolEnabled.Load() {
b = builderPool.Get().(*Builder)
b.buf.Reset()
b.sep = sep
b.needsSep = false
} else {
b = &Builder{sep: sep}
}
// Process initial arguments *after* the builder is prepared.
if len(x) > 0 {
b.Add(x...)
}
return b
}
// Start begins a new Builder with no separator (using an empty string as sep).
// It is a convenience function that wraps New(empty, x...), where empty is a constant empty string.
// This allows starting a concatenation without any separator between initial or subsequent additions.
// If pooling is enabled via Pool(true), the returned Builder MUST be released with b.Release() when done.
// Optional variadic arguments x are passed directly to New and added immediately after creation.
// Useful for fluent chains where no default separator is desired from the start.
func Start(x ...any) *Builder {
return New(empty, x...)
}
// Grow pre-sizes the internal buffer.
// This can be used to preallocate capacity based on an estimated total size,
// reducing reallocations during subsequent Add calls.
// It chains, returning the Builder for fluent use.
func (b *Builder) Grow(n int) *Builder { b.buf.Grow(n); return b }
// Add appends values to the builder.
// It inserts the current separator before each new value if needed (i.e., after the first addition).
// Values are converted to strings using the optimized write function, which handles
// common types efficiently without allocations where possible.
// Supports any number of arguments of any type.
// Chains, returning the Builder for fluent use.
func (b *Builder) Add(args ...any) *Builder {
for _, arg := range args {
if b.needsSep && b.sep != empty {
b.buf.WriteString(b.sep)
}
write(&b.buf, arg)
b.needsSep = true
}
return b
}
// If appends values to the builder only if the condition is true.
// Behaves like Add when condition is true; does nothing otherwise.
// Useful for conditional concatenation in chains.
// Chains, returning the Builder for fluent use.
func (b *Builder) If(condition bool, args ...any) *Builder {
if condition {
b.Add(args...)
}
return b
}
// Sep changes the separator for subsequent additions.
// Future Add calls will use this new separator.
// Does not affect already added content.
// If sep is empty, no separator will be added between future values.
// Chains, returning the Builder for fluent use.
func (b *Builder) Sep(sep string) *Builder { b.sep = sep; return b }
// String returns the concatenated result.
// This does not release the Builder; if pooling is enabled, call Release separately
// if you are done with the Builder.
// Can be called multiple times; the internal buffer remains unchanged.
func (b *Builder) String() string { return b.buf.String() }
// Output returns the concatenated result and releases the Builder if pooling is enabled.
// This is a convenience method to get the string and clean up in one call.
// After Output, the Builder should not be used further if pooled, as it may be recycled.
// If pooling is disabled, it behaves like String without release.
func (b *Builder) Output() string {
out := b.buf.String()
b.Release() // Release takes care of the poolEnabled check
return out
}
// Release returns the Builder to the pool if pooling is enabled.
// You should call this exactly once per New() when Pool(true) is active.
// Resets the internal state (buffer, separator, needsSep) before pooling to avoid
// retaining data or large allocations.
// If pooling is disabled, this is a no-op.
// Safe to call multiple times, but typically called once at the end of use.
func (b *Builder) Release() {
if poolEnabled.Load() {
// Avoid retaining large buffers.
b.buf.Reset()
b.sep = empty
b.needsSep = false
builderPool.Put(b)
}
}
|