共计 4145 个字符,预计需要花费 11 分钟才能阅读完成。
对于 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 库的实现,目前最新版代码的以太坊虚拟机应用的就是这个库。还有很多的函数以及用法须要大家去缓缓摸索。
将来可期,一路前行!