1// Copyright 2019 The Wuffs Authors. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// https://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package raclz4_test 16 17import ( 18 "bytes" 19 "fmt" 20 "io" 21 "log" 22 23 "github.com/google/wuffs/lib/rac" 24 "github.com/google/wuffs/lib/raclz4" 25) 26 27// Example_roundTrip demonstrates compressing (using a rac.Writer and a 28// raclz4.CodecWriter) and decompressing (using a rac.Reader and a 29// raclz4.CodecReader). This includes decompressing an excerpt of the original 30// data, exercising the "random access" part of RAC. 31func Example_roundTrip() { 32 // Create some test data. 33 oBuf := &bytes.Buffer{} 34 for i := 99; i > 0; i-- { 35 fmt.Fprintf(oBuf, "%d bottles of beer on the wall, %d bottles of beer.\n"+ 36 "Take one down, pass it around, %d bottles of beer on the wall.\n", 37 i, i, i-1) 38 } 39 original := oBuf.Bytes() 40 41 // Create the RAC file. 42 cBuf := &bytes.Buffer{} 43 w := &rac.Writer{ 44 Writer: cBuf, 45 CodecWriter: &raclz4.CodecWriter{}, 46 // It's not necessary to explicitly declare the DChunkSize. The zero 47 // value implies a reasonable default. Nonetheless, using a 1 KiB 48 // DChunkSize (which is relatively small) makes for a more interesting 49 // test, as the resultant RAC file then contains more than one chunk. 50 DChunkSize: 1024, 51 // We also use the default IndexLocation value, which makes for a 52 // simpler example, but if you're copy/pasting this code, note that 53 // using an explicit IndexLocationAtStart can result in slightly more 54 // efficient RAC files, at the cost of using more memory to encode. 55 } 56 if _, err := w.Write(original); err != nil { 57 log.Fatalf("Write: %v", err) 58 } 59 if err := w.Close(); err != nil { 60 log.Fatalf("Close: %v", err) 61 } 62 compressed := cBuf.Bytes() 63 64 // The exact compression ratio depends on the lz4 encoder's algorithm, 65 // which can change across Go standard library releases, but it should be 66 // at least a 3x ratio. It'd be larger if we didn't specify an explicit 67 // (but relatively small) DChunkSize. 68 if ratio := len(original) / len(compressed); ratio < 3 { 69 log.Fatalf("compression ratio (%dx) was too small", ratio) 70 } 71 72 // Prepare to decompress. 73 r := &rac.Reader{ 74 ReadSeeker: bytes.NewReader(compressed), 75 CompressedSize: int64(len(compressed)), 76 CodecReaders: []rac.CodecReader{&raclz4.CodecReader{}}, 77 } 78 defer r.Close() 79 80 // Read the whole file. 81 wBuf := &bytes.Buffer{} 82 if _, err := io.Copy(wBuf, r); err != nil { 83 log.Fatal(err) 84 } 85 wholeFile := wBuf.Bytes() 86 if !bytes.Equal(wholeFile, original) { 87 log.Fatal("round trip did not preserve whole file") 88 } else { 89 fmt.Printf("Whole file preserved (%d bytes).\n", len(wholeFile)) 90 } 91 92 // Read an excerpt. 93 const offset, length = 3000, 1200 94 want := original[offset : offset+length] 95 got := make([]byte, length) 96 if _, err := r.Seek(offset, io.SeekStart); err != nil { 97 log.Fatalf("Seek: %v", err) 98 } 99 if _, err := io.ReadFull(r, got); err != nil { 100 log.Fatalf("ReadFull: %v", err) 101 } 102 if !bytes.Equal(got, want) { 103 log.Fatal("round trip did not preserve excerpt") 104 } else { 105 fmt.Printf("Excerpt preserved (%d bytes).\n", len(got)) 106 } 107 108 // Output: 109 // Whole file preserved (11357 bytes). 110 // Excerpt preserved (1200 bytes). 111} 112