http://play.golang.org/p/RqScJVvpS7
package main
import (
"fmt"
"math/rand"
"encoding/binary"
)
func main() {
buffer := []byte{0, 0, 0, 0, 0, 0, 0, 0}
num := rand.Int63()
count := binary.PutVarint(buffer, num)
fmt.Println(count)
}
我在前一段时间工作num
只是一个递增的uint64
而我正在使用binary.PutUvarint
,但现在它是随机的int64和binary.PutVarint
我得到一个错误:
panic: runtime error: index out of range
goroutine 1 [running]:
encoding/binary.PutUvarint(0x1042bf58, 0x8, 0x8, 0x6ccb, 0xff9faa4, 0x9acb0442, 0x7fcfd52, 0x4d658221)
/usr/local/go/src/encoding/binary/varint.go:44 +0xc0
encoding/binary.PutVarint(0x1042bf58, 0x8, 0x8, 0x6ccb, 0x7fcfd52, 0x4d658221, 0x14f9e0, 0x104000e0)
/usr/local/go/src/encoding/binary/varint.go:83 +0x60
main.main()
/tmp/sandbox010341234/main.go:12 +0x100
我错过了什么?我本以为这是一个微不足道的变化......
编辑:我刚试过扩展我的缓冲区数组。由于一些奇怪的原因它可以工作,我得到count
10
。怎么可能? int64
是64位= 8字节,对吧?
答案 0 :(得分:7)
引用encoding/binary
的文档:
varint函数使用可变长度编码对单个整数值进行编码和解码;较小的值需要较少的字节。有关规范,请参阅https://developers.google.com/protocol-buffers/docs/encoding。
因此binary.PutVarint()
不是固定的,而是可变长度的编码。传递int64
时,大数字需要8个字节以上,小数字需要8个字节以上。由于您编码的数字是随机数,因此即使在最高字节中也会有随机位。
见这个简单的例子:
buffer := make([]byte, 100)
for num := int64(1); num < 1<<60; num <<= 4 {
count := binary.PutVarint(buffer, num)
fmt.Printf("Num=%d, bytes=%d\n", num, count)
}
输出:
Num=1, bytes=1
Num=16, bytes=1
Num=256, bytes=2
Num=4096, bytes=2
Num=65536, bytes=3
Num=1048576, bytes=4
Num=16777216, bytes=4
Num=268435456, bytes=5
Num=4294967296, bytes=5
Num=68719476736, bytes=6
Num=1099511627776, bytes=6
Num=17592186044416, bytes=7
Num=281474976710656, bytes=8
Num=4503599627370496, bytes=8
Num=72057594037927936, bytes=9
可变长度编码的本质是小数字使用较少的字节,但只有当大数字可能使用超过8个字节(大小为int64
)时才能实现。
特定编码的详细信息位于linked page。
一个非常简单的例子是:一个字节是8位。使用7位输出字节作为“有用”位来编码数据/数字。如果最高位为1,则表示需要更多字节。如果最高位为0,我们就完成了。您可以看到可以使用1个输出字节(例如n=10
)编码小数字,而我们对每个7位有用数据使用1个额外位,因此如果输入数字使用所有64位,我们最终将超过8个字节:需要10个组来覆盖64位,因此我们需要10个字节(9个组只有9 * 7 = 63位)。