vm/encoding: move bigint from vm to encoding package
This commit is contained in:
parent
783f5ecb01
commit
7ca2807875
16 changed files with 55 additions and 47 deletions
149
pkg/encoding/bigint/bigint.go
Normal file
149
pkg/encoding/bigint/bigint.go
Normal file
|
@ -0,0 +1,149 @@
|
|||
package bigint
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"math/big"
|
||||
"math/bits"
|
||||
)
|
||||
|
||||
// wordSizeBytes is a size of a big.Word (uint) in bytes.`
|
||||
const wordSizeBytes = bits.UintSize / 8
|
||||
|
||||
// FromBytes converts data in little-endian format to
|
||||
// an integer.
|
||||
func FromBytes(data []byte) *big.Int {
|
||||
n := new(big.Int)
|
||||
size := len(data)
|
||||
if size == 0 {
|
||||
return big.NewInt(0)
|
||||
}
|
||||
|
||||
isNeg := data[size-1]&0x80 != 0
|
||||
|
||||
size = getEffectiveSize(data, isNeg)
|
||||
if size == 0 {
|
||||
if isNeg {
|
||||
return big.NewInt(-1)
|
||||
}
|
||||
|
||||
return big.NewInt(0)
|
||||
}
|
||||
|
||||
lw := size / wordSizeBytes
|
||||
ws := make([]big.Word, lw+1)
|
||||
for i := 0; i < lw; i++ {
|
||||
base := i * wordSizeBytes
|
||||
for j := base + 7; j >= base; j-- {
|
||||
ws[i] <<= 8
|
||||
ws[i] ^= big.Word(data[j])
|
||||
}
|
||||
}
|
||||
|
||||
for i := size - 1; i >= lw*wordSizeBytes; i-- {
|
||||
ws[lw] <<= 8
|
||||
ws[lw] ^= big.Word(data[i])
|
||||
}
|
||||
|
||||
if isNeg {
|
||||
for i := 0; i <= lw; i++ {
|
||||
ws[i] = ^ws[i]
|
||||
}
|
||||
|
||||
shift := byte(wordSizeBytes-size%wordSizeBytes) * 8
|
||||
ws[lw] = ws[lw] & (^big.Word(0) >> shift)
|
||||
|
||||
n.SetBits(ws)
|
||||
n.Neg(n)
|
||||
|
||||
return n.Sub(n, big.NewInt(1))
|
||||
}
|
||||
|
||||
return n.SetBits(ws)
|
||||
}
|
||||
|
||||
// getEffectiveSize returns minimal number of bytes required
|
||||
// to represent a number (two's complement for negatives).
|
||||
func getEffectiveSize(buf []byte, isNeg bool) int {
|
||||
var b byte
|
||||
if isNeg {
|
||||
b = 0xFF
|
||||
}
|
||||
|
||||
size := len(buf)
|
||||
for ; size > 0; size-- {
|
||||
if buf[size-1] != b {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return size
|
||||
}
|
||||
|
||||
// ToBytes converts integer to a slice in little-endian format.
|
||||
// Note: NEO3 serialization differs from default C# BigInteger.ToByteArray()
|
||||
// when n == 0. For zero is equal to empty slice in NEO3.
|
||||
// https://github.com/neo-project/neo-vm/blob/master/src/neo-vm/Types/Integer.cs#L16
|
||||
func ToBytes(n *big.Int) []byte {
|
||||
return ToPreallocatedBytes(n, []byte{})
|
||||
}
|
||||
|
||||
// ToPreallocatedBytes converts integer to a slice in little-endian format using given
|
||||
// byte array for conversion result.
|
||||
func ToPreallocatedBytes(n *big.Int, data []byte) []byte {
|
||||
sign := n.Sign()
|
||||
if sign == 0 {
|
||||
return data
|
||||
}
|
||||
|
||||
var ws []big.Word
|
||||
if sign == 1 {
|
||||
ws = n.Bits()
|
||||
} else {
|
||||
n1 := new(big.Int).Add(n, big.NewInt(1))
|
||||
if n1.Sign() == 0 { // n == -1
|
||||
return append(data, 0xFF)
|
||||
}
|
||||
|
||||
ws = n1.Bits()
|
||||
}
|
||||
|
||||
lb := len(ws) * wordSizeBytes
|
||||
if c := cap(data); c < lb {
|
||||
data = make([]byte, lb, lb+1)
|
||||
} else {
|
||||
data = data[:lb]
|
||||
}
|
||||
data = wordsToBytes(ws, data)
|
||||
|
||||
size := len(data)
|
||||
for ; data[size-1] == 0; size-- {
|
||||
}
|
||||
|
||||
data = data[:size]
|
||||
|
||||
if data[size-1]&0x80 != 0 {
|
||||
data = append(data, 0)
|
||||
}
|
||||
|
||||
if sign == -1 {
|
||||
for i := range data {
|
||||
data[i] = ^data[i]
|
||||
}
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
func wordsToBytes(ws []big.Word, bs []byte) []byte {
|
||||
if wordSizeBytes == 8 {
|
||||
for i := range ws {
|
||||
binary.LittleEndian.PutUint64(bs[i*wordSizeBytes:], uint64(ws[i]))
|
||||
}
|
||||
} else {
|
||||
for i := range ws {
|
||||
binary.LittleEndian.PutUint32(bs[i*wordSizeBytes:], uint32(ws[i]))
|
||||
}
|
||||
}
|
||||
|
||||
return bs
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue