# Varint 编码

Protobuf 中的 Varint 根据整型大小进行不定长二进制编码，小于 128 （）的整型编码后占 1个字节，小于 16384 （）的整型编码后占 2 个，依此类推，最多可以使用 10 个字节表示大于等于 的整型；其实现原理见下图：

`00000000 00000000 00000001 00101011`

Varint 编码实现如下：

``````const maxVarintBytes = 10 // maximum length of a varint

// EncodeVarint returns the varint encoding of x.
// This is the format for the
// int32, int64, uint32, uint64, bool, and enum
// protocol buffer types.
// Not used by the package itself, but helpful to clients
// wishing to use the same encoding.
func EncodeVarint(x uint64) []byte {
var buf [maxVarintBytes]byte
var n int
for n = 0; x > 127; n++ {
// 首位记 1, 写入原始数字从低位始的 7 个 bit
buf[n] = 0x80 | uint8(x&0x7F)
// 移走记录过的 7 位
x >>= 7
}
// 剩余不足 7 位的部分直接以 8 位形式存下来，故首位为 0
buf[n] = uint8(x)
n++
return buf[0:n]
}

``````0x80 => 0000000010000000
0x7f => 0000000001111111

``````// DecodeVarint reads a varint-encoded integer from the slice.
// It returns the integer and the number of bytes consumed, or
// zero if there is not enough.
// This is the format for the
// int32, int64, uint32, uint64, bool, and enum
// protocol buffer types.
func DecodeVarint(buf []byte) (x uint64, n int) {
for shift := uint(0); shift < 64; shift += 7 {
if n >= len(buf) {
return 0, 0
}
b := uint64(buf[n])
n++
// 弃首位取 7 位并加回 x
x |= (b & 0x7F) << shift
// 首位为 0
if (b & 0x80) == 0 {
return x, n
}
}

// The number is too large to represent in a 64-bit value.
return 0, 0
}

``````fmt.Println(EncodeVarint(uint64(-299)))
// output:
// [213 253 255 255 255 255 255 255 255 1]

``````// SizeVarint returns the varint encoding size of an integer.
func SizeVarint(x uint64) int {
switch {
case x < 1<<7:
return 1
case x < 1<<14:
return 2
case x < 1<<21:
return 3
case x < 1<<28:
return 4
case x < 1<<35:
return 5
case x < 1<<42:
return 6
case x < 1<<49:
return 7
case x < 1<<56:
return 8
case x < 1<<63:
return 9
}
return 10
}

# Zigzag

Zigzag 编码将有符号整型映射到无符号整型，如其名，编码后的值在正数与负数整型间摇摆，如下表：

Signed Original Encoded As
0 0
-1 1
1 2
-2 3
2147483647 4294967294
-2147483648 4294967295

``````func Zigzag64(x uint64) uint64 {
// 左移一位 XOR (-1 / 0 的 64 位补码)
return (x << 1) ^ uint64(int64(x) >> 63)
}

# 小结

update: 2020.01.22 修正错误描述