2016-03-22 3 views
3

compress/gzipを使用してGoでバイトスライスを圧縮しようとしています。私のラップトップで2^15以上の長さのスライスを圧縮すると、インデックスが2^15以上のすべてのバイトが圧縮解除後に0に設定されます。私が研究クラスタ上で同じコードを実行すると、それも壊れます。Mac OS XのGo 1.5で32768バイトを超えるスライスをgzipできない

私のラップトップのプリント上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 

誰が何を知っていますそれは何の呼び出しがエラーを返しませんし、また、圧縮や解凍のスライスが同じであることを確認することを確認しますここにいるの?パッケージを何らかの方法で悪用していますか?私が十分な情報を与えていないかどうか教えてください。

+0

あなたは1.6を試すことができますか? OneOfOne

+0

まだありません。リリース履歴にはバグ修正の参照はありませんが、何が起こるか試してみることができます。また、元のバージョンの質問では、クラスタで動作していたと言いましたが、再テストでテストを実行したときに間違ったディレクトリにあったことがわかりました。 – mansfield

+0

私はちょうどテストしましたが、go1では動作しません。4 - > tip、それはバグトラッカーで問題を提出する価値のあるバグかもしれません。 – OneOfOne

答えて

5

問題は、この行である:

reader.Read(out) 

Reader.Read()一歩で全体outスライスを読むという保証はありません。

gzip.Reader.Read()は、io.Reader.Read()を実装します。そのドキュメント("一般規約")から引用

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

読むは、pにバイトを LEN(P)までを読み込みます。

outが満たされるまでReader.Read()が読まれることを保証するものではありません実装が(EOFに達していない場合でも)そう望む場合、それは少数のバイトで停止することがあります。 「大きな」スライスを渡すと、インプリメンテーションの内部キャッシュが使い果たされると、これが簡単に発生します。 Read()は、読み取りバイト数(およびerror)を返します。フルスライスが読み取られたかどうかを確認するために使用できます。

またはより良い、代わりにあなたは必ずoutが完全に読まされていることを確認するためにio.ReadFull()を使用することがあります。

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

この変更を適用することにより、あなたのテストは合格します。

+0

はい、これで完全に修正されました。私の基本を忘れて、愚かな私。ありがとうございました! – mansfield

関連する問題