forked from TrueCloudLab/neoneo-go
bigint: don't reallocate big.Int in ToBytes(), fix #2864
In some cases n.Add() can reuse the []Word buffer and n.Sub() reallocate it
away. If that happens, we're out of luck with 0.99.0+ versions (since
3945e81857
). I'm not sure why it does that, bit
width doesn't change in most of the cases and even if it does, we still have
enough of it in cap() to hold the old Abs() value (when we have a negative
value we in fact decreate its Abs() first and increase it back
afterwards). Still, that's what we have.
So when we have processTokenTransfer() doing Neg/Neg in-place its value is not
affected, but the original []Word bits that are reused by amount value are
(they're shared initially, Amount: *amount).
name old time/op new time/op delta
ToPreallocatedBytes-8 65.8ns ± 2% 45.6ns ± 2% -30.73% (p=0.008 n=5+5)
name old alloc/op new alloc/op delta
ToPreallocatedBytes-8 0.00B 0.00B ~ (all equal)
name old allocs/op new allocs/op delta
ToPreallocatedBytes-8 0.00 0.00 ~ (all equal)
This commit is contained in:
parent
5ad1fcd321
commit
dfd4f6978f
3 changed files with 32 additions and 4 deletions
|
@ -7,9 +7,11 @@ import (
|
||||||
|
|
||||||
func BenchmarkToPreallocatedBytes(b *testing.B) {
|
func BenchmarkToPreallocatedBytes(b *testing.B) {
|
||||||
v := big.NewInt(100500)
|
v := big.NewInt(100500)
|
||||||
|
vn := big.NewInt(-100500)
|
||||||
buf := make([]byte, 4)
|
buf := make([]byte, 4)
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
_ = ToPreallocatedBytes(v, buf[:0])
|
_ = ToPreallocatedBytes(v, buf[:0])
|
||||||
|
_ = ToPreallocatedBytes(vn, buf[:0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package bigint
|
package bigint
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
"math/bits"
|
"math/bits"
|
||||||
|
|
||||||
|
@ -113,9 +114,28 @@ func ToPreallocatedBytes(n *big.Int, data []byte) []byte {
|
||||||
}
|
}
|
||||||
|
|
||||||
if sign < 0 {
|
if sign < 0 {
|
||||||
n.Add(n, bigOne)
|
bits := n.Bits()
|
||||||
defer func() { n.Sub(n, bigOne) }()
|
carry := true
|
||||||
if n.Sign() == 0 { // n == -1
|
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)
|
return append(data[:0], 0xFF)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -106,8 +106,14 @@ var testCases = []struct {
|
||||||
|
|
||||||
func TestIntToBytes(t *testing.T) {
|
func TestIntToBytes(t *testing.T) {
|
||||||
for _, tc := range testCases {
|
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)
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue