From 87c9145ab7bb99ad86b14a9ba0bf5f1feab82126 Mon Sep 17 00:00:00 2001 From: it4rb Date: Tue, 27 Feb 2024 14:57:30 +0700 Subject: [PATCH] add function to fill u256 to bigint (#8) --- number/conversion.go | 44 ++++++++++++++++++++++++++++++++++ number/conversion_test.go | 50 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 number/conversion.go create mode 100644 number/conversion_test.go diff --git a/number/conversion.go b/number/conversion.go new file mode 100644 index 0000000..8290683 --- /dev/null +++ b/number/conversion.go @@ -0,0 +1,44 @@ +package number + +import ( + "encoding/hex" + "math/big" + "math/bits" + + "github.com/holiman/uint256" +) + +const MaxWords = 256 / bits.UintSize + +var MaxU256Hex, _ = hex.DecodeString("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") + +// similar to `z.ToBig()` but try to re-use space inside `b` instead of allocating +func FillBig(z *uint256.Int, b *big.Int) { + switch MaxWords { // Compile-time check. + case 4: // 64-bit architectures. + if cap(b.Bits()) < 4 { + // this will resize b, we can be sure that b will only hold at most MaxU256 + b.SetBytes(MaxU256Hex) + } + words := b.Bits()[:4] + words[0] = big.Word(z[0]) + words[1] = big.Word(z[1]) + words[2] = big.Word(z[2]) + words[3] = big.Word(z[3]) + b.SetBits(words) + case 8: // 32-bit architectures. + if cap(b.Bits()) < 8 { + b.SetBytes(MaxU256Hex) + } + words := b.Bits()[:8] + words[0] = big.Word(z[0]) + words[1] = big.Word(z[0] >> 32) + words[2] = big.Word(z[1]) + words[3] = big.Word(z[1] >> 32) + words[4] = big.Word(z[2]) + words[5] = big.Word(z[2] >> 32) + words[6] = big.Word(z[3]) + words[7] = big.Word(z[3] >> 32) + b.SetBits(words) + } +} diff --git a/number/conversion_test.go b/number/conversion_test.go new file mode 100644 index 0000000..c402183 --- /dev/null +++ b/number/conversion_test.go @@ -0,0 +1,50 @@ +package number_test + +import ( + "fmt" + "math/big" + "testing" + + "github.com/KyberNetwork/blockchain-toolkit/integer" + "github.com/KyberNetwork/blockchain-toolkit/number" + "github.com/holiman/uint256" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestFillBig(t *testing.T) { + var err error + var xU256 uint256.Int + + for i := 0; i < 200; i++ { + xStr := number.RandNumberHexString(64) + + t.Run(fmt.Sprintf("test %s", xStr), func(t *testing.T) { + err = xU256.SetFromHex("0x" + xStr) + require.Nil(t, err) + + // known correct value to compare against + xBI, ok := new(big.Int).SetString(xStr, 16) + require.True(t, ok) + + // should resize and fill small int + smallBI := big.NewInt(12) + number.FillBig(&xU256, smallBI) + assert.Equal(t, xBI, smallBI) + + // should resize and fill big int + largeBI := integer.TenPow(200) + number.FillBig(&xU256, largeBI) + assert.Equal(t, xBI, largeBI) + + }) + } +} + +func BenchmarkFillBig(b *testing.B) { + xU256 := number.Number_1e18 + xBI := big.NewInt(0) + for i := 0; i < b.N; i++ { + number.FillBig(xU256, xBI) + } +}