Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
245 changes: 245 additions & 0 deletions flate/example_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package flate_test

import (
"bytes"
"fmt"
"io"
"log"
"os"
"strings"
"sync"

"github.com/klauspost/compress/flate"
)

// In performance critical applications, Reset can be used to discard the
// current compressor or decompressor state and reinitialize them quickly
// by taking advantage of previously allocated memory.
func Example_reset() {
proverbs := []string{
"Don't communicate by sharing memory, share memory by communicating.\n",
"Concurrency is not parallelism.\n",
"The bigger the interface, the weaker the abstraction.\n",
"Documentation is for users.\n",
}

var r strings.Reader
var b bytes.Buffer
buf := make([]byte, 32<<10)

zw, err := flate.NewWriter(nil, flate.DefaultCompression)
if err != nil {
log.Fatal(err)
}
zr := flate.NewReader(nil)

for _, s := range proverbs {
r.Reset(s)
b.Reset()

// Reset the compressor and encode from some input stream.
zw.Reset(&b)
if _, err := io.CopyBuffer(zw, &r, buf); err != nil {
log.Fatal(err)
}
if err := zw.Close(); err != nil {
log.Fatal(err)
}

// Reset the decompressor and decode to some output stream.
if err := zr.(flate.Resetter).Reset(&b, nil); err != nil {
log.Fatal(err)
}
if _, err := io.CopyBuffer(os.Stdout, zr, buf); err != nil {
log.Fatal(err)
}
if err := zr.Close(); err != nil {
log.Fatal(err)
}
}

// Output:
// Don't communicate by sharing memory, share memory by communicating.
// Concurrency is not parallelism.
// The bigger the interface, the weaker the abstraction.
// Documentation is for users.
}

// A preset dictionary can be used to improve the compression ratio.
// The downside to using a dictionary is that the compressor and decompressor
// must agree in advance what dictionary to use.
func Example_dictionary() {
// The dictionary is a string of bytes. When compressing some input data,
// the compressor will attempt to substitute substrings with matches found
// in the dictionary. As such, the dictionary should only contain substrings
// that are expected to be found in the actual data stream.
const dict = `<?xml version="1.0"?>` + `<book>` + `<data>` + `<meta name="` + `" content="`

// The data to compress should (but is not required to) contain frequent
// substrings that match those in the dictionary.
const data = `<?xml version="1.0"?>
<book>
<meta name="title" content="The Go Programming Language"/>
<meta name="authors" content="Alan Donovan and Brian Kernighan"/>
<meta name="published" content="2015-10-26"/>
<meta name="isbn" content="978-0134190440"/>
<data>...</data>
</book>
`

var b bytes.Buffer

// Compress the data using the specially crafted dictionary.
zw, err := flate.NewWriterDict(&b, flate.BestCompression, []byte(dict))
if err != nil {
log.Fatal(err)
}
if _, err := io.Copy(zw, strings.NewReader(data)); err != nil {
log.Fatal(err)
}
if err := zw.Close(); err != nil {
log.Fatal(err)
}

// The decompressor must use the same dictionary as the compressor.
// Otherwise, the input may appear as corrupted.
fmt.Println("Decompressed output using the dictionary:")
zr := flate.NewReaderDict(bytes.NewReader(b.Bytes()), []byte(dict))
if _, err := io.Copy(os.Stdout, zr); err != nil {
log.Fatal(err)
}
if err := zr.Close(); err != nil {
log.Fatal(err)
}

fmt.Println()

// Substitute all of the bytes in the dictionary with a '#' to visually
// demonstrate the approximate effectiveness of using a preset dictionary.
fmt.Println("Substrings matched by the dictionary are marked with #:")
hashDict := []byte(dict)
for i := range hashDict {
hashDict[i] = '#'
}
zr = flate.NewReaderDict(&b, hashDict)
if _, err := io.Copy(os.Stdout, zr); err != nil {
log.Fatal(err)
}
if err := zr.Close(); err != nil {
log.Fatal(err)
}

// Output:
// Decompressed output using the dictionary:
// <?xml version="1.0"?>
// <book>
// <meta name="title" content="The Go Programming Language"/>
// <meta name="authors" content="Alan Donovan and Brian Kernighan"/>
// <meta name="published" content="2015-10-26"/>
// <meta name="isbn" content="978-0134190440"/>
// <data>...</data>
// </book>
//
// Substrings matched by the dictionary are marked with #:
// #####################
// ######
// ############title###########The Go Programming Language"/#
// ############authors###########Alan Donovan and Brian Kernighan"/#
// ############published###########2015-10-26"/#
// ############isbn###########978-0134190440"/#
// ######...</#####
// </#####
}

// DEFLATE is suitable for transmitting compressed data across the network.
func Example_synchronization() {
var wg sync.WaitGroup
defer wg.Wait()

// Use io.Pipe to simulate a network connection.
// A real network application should take care to properly close the
// underlying connection.
rp, wp := io.Pipe()

// Start a goroutine to act as the transmitter.
wg.Add(1)
go func() {
defer wg.Done()
defer wp.Close()

zw, err := flate.NewWriter(wp, flate.BestSpeed)
if err != nil {
log.Fatal(err)
}

b := make([]byte, 256)
for _, m := range strings.Fields("A long time ago in a galaxy far, far away...") {
// We use a simple framing format where the first byte is the
// message length, followed the message itself.
b[0] = uint8(copy(b[1:], m))

if _, err := zw.Write(b[:1+len(m)]); err != nil {
log.Fatal(err)
}

// Flush ensures that the receiver can read all data sent so far.
if err := zw.Flush(); err != nil {
log.Fatal(err)
}
}

if err := zw.Close(); err != nil {
log.Fatal(err)
}
}()

// Start a goroutine to act as the receiver.
wg.Add(1)
go func() {
defer wg.Done()

zr := flate.NewReader(rp)

b := make([]byte, 256)
for {
// Read the message length.
// This is guaranteed to return for every corresponding
// Flush and Close on the transmitter side.
if _, err := io.ReadFull(zr, b[:1]); err != nil {
if err == io.EOF {
break // The transmitter closed the stream
}
log.Fatal(err)
}

// Read the message content.
n := int(b[0])
if _, err := io.ReadFull(zr, b[:n]); err != nil {
log.Fatal(err)
}

fmt.Printf("Received %d bytes: %s\n", n, b[:n])
}
fmt.Println()

if err := zr.Close(); err != nil {
log.Fatal(err)
}
}()

// Output:
// Received 1 bytes: A
// Received 4 bytes: long
// Received 4 bytes: time
// Received 3 bytes: ago
// Received 2 bytes: in
// Received 1 bytes: a
// Received 6 bytes: galaxy
// Received 4 bytes: far,
// Received 3 bytes: far
// Received 7 bytes: away...
}
4 changes: 3 additions & 1 deletion flate/huffman_bit_writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,9 @@ func (w *huffmanBitWriter) flush() {
n++
}
w.bits = 0
w.write(w.bytes[:n])
if n > 0 {
w.write(w.bytes[:n])
}
w.nbytes = 0
}

Expand Down
Loading