| // Copyright 2019 The Wuffs Authors. | |
| // | |
| // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
| // https://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
| // <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your | |
| // option. This file may not be copied, modified, or distributed | |
| // except according to those terms. | |
| // | |
| // SPDX-License-Identifier: Apache-2.0 OR MIT | |
| package raczstd_test | |
| import ( | |
| "bytes" | |
| "fmt" | |
| "io" | |
| "log" | |
| "github.com/google/wuffs/lib/rac" | |
| "github.com/google/wuffs/lib/raczstd" | |
| ) | |
| // Example_roundTrip demonstrates compressing (using a rac.Writer and a | |
| // raczstd.CodecWriter) and decompressing (using a rac.Reader and a | |
| // raczstd.CodecReader). This includes decompressing an excerpt of the original | |
| // data, exercising the "random access" part of RAC. | |
| func Example_roundTrip() { | |
| // Create some test data. | |
| oBuf := &bytes.Buffer{} | |
| for i := 99; i > 0; i-- { | |
| fmt.Fprintf(oBuf, "%d bottles of beer on the wall, %d bottles of beer.\n"+ | |
| "Take one down, pass it around, %d bottles of beer on the wall.\n", | |
| i, i, i-1) | |
| } | |
| original := oBuf.Bytes() | |
| // Create the RAC file. | |
| cBuf := &bytes.Buffer{} | |
| w := &rac.Writer{ | |
| Writer: cBuf, | |
| CodecWriter: &raczstd.CodecWriter{}, | |
| // It's not necessary to explicitly declare the DChunkSize. The zero | |
| // value implies a reasonable default. Nonetheless, using a 1 KiB | |
| // DChunkSize (which is relatively small) makes for a more interesting | |
| // test, as the resultant RAC file then contains more than one chunk. | |
| DChunkSize: 1024, | |
| // We also use the default IndexLocation value, which makes for a | |
| // simpler example, but if you're copy/pasting this code, note that | |
| // using an explicit IndexLocationAtStart can result in slightly more | |
| // efficient RAC files, at the cost of using more memory to encode. | |
| } | |
| if _, err := w.Write(original); err != nil { | |
| log.Fatalf("Write: %v", err) | |
| } | |
| if err := w.Close(); err != nil { | |
| log.Fatalf("Close: %v", err) | |
| } | |
| compressed := cBuf.Bytes() | |
| // The exact compression ratio depends on the zstd encoder's algorithm, | |
| // which can change across Go standard library releases, but it should be | |
| // at least a 4x ratio. It'd be larger if we didn't specify an explicit | |
| // (but relatively small) DChunkSize. | |
| if ratio := len(original) / len(compressed); ratio < 4 { | |
| log.Fatalf("compression ratio (%dx) was too small", ratio) | |
| } | |
| // Prepare to decompress. | |
| r := &rac.Reader{ | |
| ReadSeeker: bytes.NewReader(compressed), | |
| CompressedSize: int64(len(compressed)), | |
| CodecReaders: []rac.CodecReader{&raczstd.CodecReader{}}, | |
| } | |
| defer r.Close() | |
| // Read the whole file. | |
| wBuf := &bytes.Buffer{} | |
| if _, err := io.Copy(wBuf, r); err != nil { | |
| log.Fatal(err) | |
| } | |
| wholeFile := wBuf.Bytes() | |
| if !bytes.Equal(wholeFile, original) { | |
| log.Fatal("round trip did not preserve whole file") | |
| } else { | |
| fmt.Printf("Whole file preserved (%d bytes).\n", len(wholeFile)) | |
| } | |
| // Read an excerpt. | |
| const offset, length = 3000, 1200 | |
| want := original[offset : offset+length] | |
| got := make([]byte, length) | |
| if _, err := r.Seek(offset, io.SeekStart); err != nil { | |
| log.Fatalf("Seek: %v", err) | |
| } | |
| if _, err := io.ReadFull(r, got); err != nil { | |
| log.Fatalf("ReadFull: %v", err) | |
| } | |
| if !bytes.Equal(got, want) { | |
| log.Fatal("round trip did not preserve excerpt") | |
| } else { | |
| fmt.Printf("Excerpt preserved (%d bytes).\n", len(got)) | |
| } | |
| // Output: | |
| // Whole file preserved (11357 bytes). | |
| // Excerpt preserved (1200 bytes). | |
| } |