乐趣区

关于uint256:共读holimanuint256的代码

对于 holiman/uint256 的代码辅助浏览

本文首先介绍了 Big Endian 和 Little Endian 的定义和区别,接着介绍 uint256 的数据结构,以及应用的字节序,而后介绍了如何应用 uint256,最初做一下总结。

对于字节序:Big Endian 和 Little Endian

字节序,也就是字节的程序,指的是多字节的数据在内存中的寄存程序。

Big Endian 和 Little Endian

Big Endian 是指低地址端 寄存 高位字节。
Little Endian 是指低地址端 寄存 低位字节。

各自的劣势

Big Endian: 符号位的断定固定为第一个字节,容易判断正负。
Little Endian: 长度为 1,2,4 字节的数,排列形式都是一样的,数据类型转换十分不便。

数据结构

uint256 的组成由 4 个 uint64 组成的数组,并且是用的 little-endian 序列,也就是说 Int[3]是最高无效,Int[0]是最低无效,
type Int [4]uint64

序列化

在 Int 中定了 4 中办法用于 Int 和 byte 数组之间的转换,函数如下

func (z *Int) SetBytes(buf []byte) *Int
func (z *Int) Bytes32() [32]byte
func (z *Int) Bytes20() [20]byte
func (z *Int) Bytes() []byte

其中转换成 bytes 的办法有三种,两种是固定长度,一种是不定长。依据代码咱们能够看到,不定长的函数 Bytes 是定长函数 Bytes32 返回值切片;

// Bytes32 returns the value of z as a 32-byte big-endian array.
func (z *Int) Bytes32() [32]byte {// The PutUint64()s are inlined and we get 4x (load, bswap, store) instructions.
 var b [32]byte
 binary.BigEndian.PutUint64(b[0:8], z[3])
 binary.BigEndian.PutUint64(b[8:16], z[2])
 binary.BigEndian.PutUint64(b[16:24], z[1])
 binary.BigEndian.PutUint64(b[24:32], z[0])
 return b
}

// Bytes returns the value of z as a big-endian byte slice.
func (z *Int) Bytes() []byte {b := z.Bytes32()
 return b[32-z.ByteLen():]
}

初始化的几种办法

Int 的初始化有三种办法,NewInt 返回值为 0 的 Int,FromBig 是将 *big.Int 转换成 Int,FromHex 将 16 进制的字符串转换成 Int。

// NewInt returns a new zero-initialized Int.
func NewInt() *Int {return &Int{}
}

func FromBig(b *big.Int) (*Int, bool) 
func FromHex(hex string) (*Int, error)

正负的示意,最大值和最小值

因为 Int 的 String 办法返回的是 Hex 字符串,所以不是很直观的判断到底是负数还是正数,上面的函数能够用来断定,这个 Int 是负数还是正数;

func (z *Int) Sign() int {if z.IsZero() {return 0}
 if z[3] < 0x8000000000000000 {return 1}
 return -1
}

下面的函数能够看出只有 z[3] < 0x8000000000000000,即是正数,所以负数最大是:0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,- 1 示意为
0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 最大的正数就是 0x8000000000000000000000000000000000000000000000000000000000000000

罕用的加减乘除

代码太多,咱们简略剖析下加减运算,加法运算是针对 [4]uint64 数组中的数据进行运算,因为最低无效位在[0]uint64 中,所以是从下表 0 开始进行计算的;
Add 和 AddOverflow 的区别是如果最高无效位有进位运算,那么就是 Overflow;
同理咱们能够自行剖析减乘除运算;

// Add sets z to the sum x+y
func (z *Int) Add(x, y *Int) *Int {
 var carry uint64
 z[0], carry = bits.Add64(x[0], y[0], 0)
 z[1], carry = bits.Add64(x[1], y[1], carry)
 z[2], carry = bits.Add64(x[2], y[2], carry)
 z[3], _ = bits.Add64(x[3], y[3], carry)
 return z
}

// Add64 returns the sum with carry of x, y and carry: sum = x + y + carry.
// The carry input must be 0 or 1; otherwise the behavior is undefined.
// The carryOut output is guaranteed to be 0 or 1.
//
// This function's execution time does not depend on the inputs.
func Add64(x, y, carry uint64) (sum, carryOut uint64) {
 sum = x + y + carry
 // The sum will overflow if both top bits are set (x & y) or if one of them
 // is (x | y), and a carry from the lower place happened. If such a carry
 // happens, the top bit will be 1 + 0 + 1 = 0 (&^ sum).
 carryOut = ((x & y) | ((x | y) &^ sum)) >> 63
 return
}


// AddOverflow sets z to the sum x+y, and returns whether overflow occurred
func (z *Int) AddOverflow(x, y *Int) bool {
 var carry uint64
 z[0], carry = bits.Add64(x[0], y[0], 0)
 z[1], carry = bits.Add64(x[1], y[1], carry)
 z[2], carry = bits.Add64(x[2], y[2], carry)
 z[3], carry = bits.Add64(x[3], y[3], carry)
 return carry != 0
}

// Sub sets z to the difference x-y
func (z *Int) Sub(x, y *Int) *Int {
 var carry uint64
 z[0], carry = bits.Sub64(x[0], y[0], 0)
 z[1], carry = bits.Sub64(x[1], y[1], carry)
 z[2], carry = bits.Sub64(x[2], y[2], carry)
 z[3], _ = bits.Sub64(x[3], y[3], carry)
 return z
}

// Sub32 returns the difference of x, y and borrow, diff = x - y - borrow.
// The borrow input must be 0 or 1; otherwise the behavior is undefined.
// The borrowOut output is guaranteed to be 0 or 1.
//
// This function's execution time does not depend on the inputs.
func Sub32(x, y, borrow uint32) (diff, borrowOut uint32) {
 diff = x - y - borrow
 // The difference will underflow if the top bit of x is not set and the top
 // bit of y is set (^x & y) or if they are the same (^(x ^ y)) and a borrow
 // from the lower place happens. If that borrow happens, the result will be
 // 1 - 1 - 1 = 0 - 0 - 1 = 1 (& diff).
 borrowOut = ((^x & y) | (^(x ^ y) & diff)) >> 31
 return
}

逻辑运算或与非

逻辑运算就的原理很简略,就是按位进行逻辑运算,具体的代码如下

// Not sets z = ^x and returns z.
func (z *Int) Not(x *Int) *Int {z[3], z[2], z[1], z[0] = ^x[3], ^x[2], ^x[1], ^x[0]
 return z
}

// Or sets z = x | y and returns z.
func (z *Int) Or(x, y *Int) *Int {z[0] = x[0] | y[0]
 z[1] = x[1] | y[1]
 z[2] = x[2] | y[2]
 z[3] = x[3] | y[3]
 return z
}

// And sets z = x & y and returns z.
func (z *Int) And(x, y *Int) *Int {z[0] = x[0] & y[0]
 z[1] = x[1] & y[1]
 z[2] = x[2] & y[2]
 z[3] = x[3] & y[3]
 return z
}

// Xor sets z = x ^ y and returns z.
func (z *Int) Xor(x, y *Int) *Int {z[0] = x[0] ^ y[0]
 z[1] = x[1] ^ y[1]
 z[2] = x[2] ^ y[2]
 z[3] = x[3] ^ y[3]
 return z
}

总结

本文率领大家简略的理解 uin256 库的实现,目前最新版代码的以太坊虚拟机应用的就是这个库。还有很多的函数以及用法须要大家去缓缓摸索。
将来可期,一路前行!

退出移动版