diff --git a/pkg/encoding/bigint/bench_test.go b/pkg/encoding/bigint/bench_test.go index 411a10b85..893be0118 100644 --- a/pkg/encoding/bigint/bench_test.go +++ b/pkg/encoding/bigint/bench_test.go @@ -7,9 +7,11 @@ import ( func BenchmarkToPreallocatedBytes(b *testing.B) { v := big.NewInt(100500) + vn := big.NewInt(-100500) buf := make([]byte, 4) for i := 0; i < b.N; i++ { _ = ToPreallocatedBytes(v, buf[:0]) + _ = ToPreallocatedBytes(vn, buf[:0]) } } diff --git a/pkg/encoding/bigint/bigint.go b/pkg/encoding/bigint/bigint.go index 4b9779e4a..8274bb684 100644 --- a/pkg/encoding/bigint/bigint.go +++ b/pkg/encoding/bigint/bigint.go @@ -1,6 +1,7 @@ package bigint import ( + "math" "math/big" "math/bits" @@ -113,9 +114,28 @@ func ToPreallocatedBytes(n *big.Int, data []byte) []byte { } if sign < 0 { - n.Add(n, bigOne) - defer func() { n.Sub(n, bigOne) }() - if n.Sign() == 0 { // n == -1 + bits := n.Bits() + carry := true + nonZero := false + for i := range bits { + if carry { + bits[i]-- + carry = (bits[i] == math.MaxUint) + } + nonZero = nonZero || (bits[i] != 0) + } + defer func() { + var carry = true + for i := range bits { + if carry { + bits[i]++ + carry = (bits[i] == 0) + } else { + break + } + } + }() + if !nonZero { // n == -1 return append(data[:0], 0xFF) } } diff --git a/pkg/encoding/bigint/bigint_test.go b/pkg/encoding/bigint/bigint_test.go index 79c277546..39c40d666 100644 --- a/pkg/encoding/bigint/bigint_test.go +++ b/pkg/encoding/bigint/bigint_test.go @@ -106,8 +106,14 @@ var testCases = []struct { func TestIntToBytes(t *testing.T) { for _, tc := range testCases { - buf := ToBytes(big.NewInt(tc.number)) + num := big.NewInt(tc.number) + var numC = *num // See #2864. + buf := ToBytes(num) assert.Equal(t, tc.buf, buf, "error while converting %d", tc.number) + _ = numC.Neg(&numC) + _ = ToBytes(&numC) + _ = numC.Neg(&numC) + assert.Equal(t, num, &numC, "number mismatch after converting %d", tc.number) } }