在Mac OS X上的Go 1.5中无法gzip超过32768字节的切片

时间:2016-03-22 19:32:05

标签: macos go gzip

我正在尝试使用compress/gzip压缩Go中的字节切片。每当我在笔记本电脑上压缩长度超过2 ^ 15的切片时,索引为2 ^ 15或更大的每个字节在解压缩后都会设置为0。当我在我的研究集群上运行相同的代码时,它也会中断。

在我的笔记本电脑上调用go version打印:

$ go version
go version go1.5 darwin/amd64

在群集上调用go version打印:

$ go version
go version go1.3.3 linux/amd64

下面是我写的一个示范测试文件。它生成不同长度的随机切片,压缩它们,然后解压缩它们。它检查没有调用返回错误,并检查压缩和解压缩的切片是否相同:

package compress

import (
    "bytes"
    "compress/gzip"
    "math/rand"
    "testing"
)

func byteSliceEq(xs, ys []byte) bool {
    if len(xs) != len(ys) { return false }
    for i := range xs {
        if xs[i] != ys[i] { return false }
    }
    return true
}

func TestGzip(t *testing.T) {
    tests := []struct {
        n int
    }{
        { 1<<10 },
        { 1<<15 },
        { 1<<15 + 1 },
        { 1<<20 },

    }

    rand.Seed(0)

    for i := range tests {
        n := tests[i].n

        in, out := make([]byte, n), make([]byte, n)
        buf := &bytes.Buffer{}
        for i := range in { in[i] = byte(rand.Intn(256)) }

        writer := gzip.NewWriter(buf)
        _, err := writer.Write(in)
        if err != nil {
            t.Errorf("%d) n = %d: writer.Write() error: %s",
                i + 1, n, err.Error())
        }
        err = writer.Close()
        if err != nil {
            t.Errorf("%d) n = %d: writer.Close() error: %s",
                i + 1, n, err.Error())
        }

        reader, err := gzip.NewReader(buf)
        if err != nil {
            t.Errorf("%d) n = %d: gzip.NewReader error: %s",
                i + 1, n, err.Error())
        }
        reader.Read(out)
        err = reader.Close()
        if err != nil {
            t.Errorf("%d) n = %d: reader.Close() error: %s",
                i + 1, n, err.Error())
        }

        if !byteSliceEq(in, out) {
            idx := -1
            for i := range in {
                if in[i] != out[i] {
                    idx = i
                    break
                }
            }
            t.Errorf("%d) n = %d: in[%d] = %d, but out[%d] = %d",
                i + 1, n, idx, in[idx], idx, out[idx])
        }
    }
}

当我运行此测试时,我得到以下输出:

$ go test --run "TestGzip"
--- FAIL: TestGzip (0.12s)
    gzip_test.go:77: 3) n = 32769: in[32768] = 78, but out[32768] = 0
    gzip_test.go:77: 4) n = 1048576: in[32768] = 229, but out[32768] = 0
FAIL
exit status 1

有谁知道这里发生了什么?我是否以某种方式滥用包裹?如果我没有提供足够的信息,请告诉我。

1 个答案:

答案 0 :(得分:5)

问题出在这一行:

reader.Read(out)

无法保证Reader.Read()会一步读取整个out切片

gzip.Reader.Read()将实施io.Reader.Read() 引用其文档(“一般合同”):

Read(p []byte) (n int, err error)
     

读取将最多 len(p)字节读入p。

无法保证在Reader.Read()填充之前out将会读取,如果实现需要,它可能会以较少的字节停止(即使未达到EOF)。如果传递“大”片,如果实现的内部缓存耗尽,则可能很容易发生这种情况。 Read()返回读取字节数(以及error),您可以使用它来检查是否已读取整个切片。

甚至更好,相反,您可以使用io.ReadFull()确保完整阅读out

if _, err = io.ReadFull(reader, out); err != nil {
    t.Errorf("Error reading full out slice:", err)
}

通过应用此更改,您的测试通过。