使用binary.PutVarint(...)时索引超出范围

时间:2015-09-22 14:55:48

标签: go

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字节,对吧?

1 个答案:

答案 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位)。

相关问题