diff --git a/format.go b/format.go index 04d7aaa..1d427f2 100644 --- a/format.go +++ b/format.go @@ -196,9 +196,12 @@ func AppendInt(dst []byte, n int64) []byte { if n >= 0 { return AppendUint(dst, uint64(n)) } + if n > -100 { + return append(dst, smallNegInts[-n]...) + } var buf [20]byte i := formatUintBuf(&buf, uint64(-n)) i-- - buf[i] = '-' + buf[i] = '-' //nolint:gosec // i is always >= 0: formatUintBuf returns at least 1 for any input, so i >= 0 after decrement return append(dst, buf[i:]...) } diff --git a/format_test.go b/format_test.go index 13b51d2..b878637 100644 --- a/format_test.go +++ b/format_test.go @@ -109,7 +109,7 @@ func Test_AppendUint(t *testing.T) { func Test_AppendInt(t *testing.T) { t.Parallel() - tests := []int64{0, 1, -1, 99, -99, 12345, -12345, math.MaxInt64, math.MinInt64} + tests := []int64{0, 1, -1, 99, -99, 100, -100, 12345, -12345, math.MaxInt64, math.MinInt64} for _, tt := range tests { expected := strconv.AppendInt([]byte("prefix"), tt, 10) result := AppendInt([]byte("prefix"), tt) @@ -117,6 +117,26 @@ func Test_AppendInt(t *testing.T) { } } +func Test_AppendInt_SmallNegativeCache(t *testing.T) { + t.Parallel() + // Test all small negative integers that should use the cache (-1 to -99) + for i := int64(-1); i >= -99; i-- { + t.Run(strconv.FormatInt(i, 10), func(t *testing.T) { + t.Parallel() + expected := strconv.AppendInt([]byte("prefix"), i, 10) + result := AppendInt([]byte("prefix"), i) + require.Equal(t, expected, result) + }) + } + // Verify boundary: -100 should NOT use the cache + t.Run("boundary/-100", func(t *testing.T) { + t.Parallel() + expected := strconv.AppendInt([]byte("prefix"), -100, 10) + result := AppendInt([]byte("prefix"), -100) + require.Equal(t, expected, result) + }) +} + // Benchmarks func Benchmark_FormatUint(b *testing.B) { @@ -295,19 +315,27 @@ func Benchmark_AppendUint(b *testing.B) { } func Benchmark_AppendInt(b *testing.B) { - input := int64(-123456789) + inputs := []struct { + name string + value int64 + }{ + {"small_neg", -42}, + {"medium_neg", -123456789}, + } dst := make([]byte, 0, 32) - b.Run("fiber", func(b *testing.B) { - b.ReportAllocs() - for i := 0; i < b.N; i++ { - _ = AppendInt(dst, input) - } - }) - b.Run("strconv", func(b *testing.B) { - b.ReportAllocs() - for i := 0; i < b.N; i++ { - _ = strconv.AppendInt(dst, input, 10) - } - }) + for _, input := range inputs { + b.Run(input.name+"/fiber", func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = AppendInt(dst, input.value) + } + }) + b.Run(input.name+"/strconv", func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + _ = strconv.AppendInt(dst, input.value, 10) + } + }) + } }