1// Copyright 2016 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package flate_test
6
7import (
8	"bytes"
9	"compress/flate"
10	"fmt"
11	"io"
12	"log"
13	"os"
14	"strings"
15	"sync"
16)
17
18// In performance critical applications, Reset can be used to discard the
19// current compressor or decompressor state and reinitialize them quickly
20// by taking advantage of previously allocated memory.
21func Example_reset() {
22	proverbs := []string{
23		"Don't communicate by sharing memory, share memory by communicating.\n",
24		"Concurrency is not parallelism.\n",
25		"The bigger the interface, the weaker the abstraction.\n",
26		"Documentation is for users.\n",
27	}
28
29	var r strings.Reader
30	var b bytes.Buffer
31	buf := make([]byte, 32<<10)
32
33	zw, err := flate.NewWriter(nil, flate.DefaultCompression)
34	if err != nil {
35		log.Fatal(err)
36	}
37	zr := flate.NewReader(nil)
38
39	for _, s := range proverbs {
40		r.Reset(s)
41		b.Reset()
42
43		// Reset the compressor and encode from some input stream.
44		zw.Reset(&b)
45		if _, err := io.CopyBuffer(zw, &r, buf); err != nil {
46			log.Fatal(err)
47		}
48		if err := zw.Close(); err != nil {
49			log.Fatal(err)
50		}
51
52		// Reset the decompressor and decode to some output stream.
53		if err := zr.(flate.Resetter).Reset(&b, nil); err != nil {
54			log.Fatal(err)
55		}
56		if _, err := io.CopyBuffer(os.Stdout, zr, buf); err != nil {
57			log.Fatal(err)
58		}
59		if err := zr.Close(); err != nil {
60			log.Fatal(err)
61		}
62	}
63
64	// Output:
65	// Don't communicate by sharing memory, share memory by communicating.
66	// Concurrency is not parallelism.
67	// The bigger the interface, the weaker the abstraction.
68	// Documentation is for users.
69}
70
71// A preset dictionary can be used to improve the compression ratio.
72// The downside to using a dictionary is that the compressor and decompressor
73// must agree in advance what dictionary to use.
74func Example_dictionary() {
75	// The dictionary is a string of bytes. When compressing some input data,
76	// the compressor will attempt to substitute substrings with matches found
77	// in the dictionary. As such, the dictionary should only contain substrings
78	// that are expected to be found in the actual data stream.
79	const dict = `<?xml version="1.0"?>` + `<book>` + `<data>` + `<meta name="` + `" content="`
80
81	// The data to compress should (but is not required to) contain frequent
82	// substrings that match those in the dictionary.
83	const data = `<?xml version="1.0"?>
84<book>
85	<meta name="title" content="The Go Programming Language"/>
86	<meta name="authors" content="Alan Donovan and Brian Kernighan"/>
87	<meta name="published" content="2015-10-26"/>
88	<meta name="isbn" content="978-0134190440"/>
89	<data>...</data>
90</book>
91`
92
93	var b bytes.Buffer
94
95	// Compress the data using the specially crafted dictionary.
96	zw, err := flate.NewWriterDict(&b, flate.DefaultCompression, []byte(dict))
97	if err != nil {
98		log.Fatal(err)
99	}
100	if _, err := io.Copy(zw, strings.NewReader(data)); err != nil {
101		log.Fatal(err)
102	}
103	if err := zw.Close(); err != nil {
104		log.Fatal(err)
105	}
106
107	// The decompressor must use the same dictionary as the compressor.
108	// Otherwise, the input may appear as corrupted.
109	fmt.Println("Decompressed output using the dictionary:")
110	zr := flate.NewReaderDict(bytes.NewReader(b.Bytes()), []byte(dict))
111	if _, err := io.Copy(os.Stdout, zr); err != nil {
112		log.Fatal(err)
113	}
114	if err := zr.Close(); err != nil {
115		log.Fatal(err)
116	}
117
118	fmt.Println()
119
120	// Substitute all of the bytes in the dictionary with a '#' to visually
121	// demonstrate the approximate effectiveness of using a preset dictionary.
122	fmt.Println("Substrings matched by the dictionary are marked with #:")
123	hashDict := []byte(dict)
124	for i := range hashDict {
125		hashDict[i] = '#'
126	}
127	zr = flate.NewReaderDict(&b, hashDict)
128	if _, err := io.Copy(os.Stdout, zr); err != nil {
129		log.Fatal(err)
130	}
131	if err := zr.Close(); err != nil {
132		log.Fatal(err)
133	}
134
135	// Output:
136	// Decompressed output using the dictionary:
137	// <?xml version="1.0"?>
138	// <book>
139	// 	<meta name="title" content="The Go Programming Language"/>
140	// 	<meta name="authors" content="Alan Donovan and Brian Kernighan"/>
141	// 	<meta name="published" content="2015-10-26"/>
142	// 	<meta name="isbn" content="978-0134190440"/>
143	// 	<data>...</data>
144	// </book>
145	//
146	// Substrings matched by the dictionary are marked with #:
147	// #####################
148	// ######
149	// 	############title###########The Go Programming Language"/#
150	// 	############authors###########Alan Donovan and Brian Kernighan"/#
151	// 	############published###########2015-10-26"/#
152	// 	############isbn###########978-0134190440"/#
153	// 	######...</#####
154	// </#####
155}
156
157// DEFLATE is suitable for transmitting compressed data across the network.
158func Example_synchronization() {
159	var wg sync.WaitGroup
160	defer wg.Wait()
161
162	// Use io.Pipe to simulate a network connection.
163	// A real network application should take care to properly close the
164	// underlying connection.
165	rp, wp := io.Pipe()
166
167	// Start a goroutine to act as the transmitter.
168	wg.Add(1)
169	go func() {
170		defer wg.Done()
171
172		zw, err := flate.NewWriter(wp, flate.BestSpeed)
173		if err != nil {
174			log.Fatal(err)
175		}
176
177		b := make([]byte, 256)
178		for _, m := range strings.Fields("A long time ago in a galaxy far, far away...") {
179			// We use a simple framing format where the first byte is the
180			// message length, followed the message itself.
181			b[0] = uint8(copy(b[1:], m))
182
183			if _, err := zw.Write(b[:1+len(m)]); err != nil {
184				log.Fatal(err)
185			}
186
187			// Flush ensures that the receiver can read all data sent so far.
188			if err := zw.Flush(); err != nil {
189				log.Fatal(err)
190			}
191		}
192
193		if err := zw.Close(); err != nil {
194			log.Fatal(err)
195		}
196	}()
197
198	// Start a goroutine to act as the receiver.
199	wg.Add(1)
200	go func() {
201		defer wg.Done()
202
203		zr := flate.NewReader(rp)
204
205		b := make([]byte, 256)
206		for {
207			// Read the message length.
208			// This is guaranteed to return for every corresponding
209			// Flush and Close on the transmitter side.
210			if _, err := io.ReadFull(zr, b[:1]); err != nil {
211				if err == io.EOF {
212					break // The transmitter closed the stream
213				}
214				log.Fatal(err)
215			}
216
217			// Read the message content.
218			n := int(b[0])
219			if _, err := io.ReadFull(zr, b[:n]); err != nil {
220				log.Fatal(err)
221			}
222
223			fmt.Printf("Received %d bytes: %s\n", n, b[:n])
224		}
225		fmt.Println()
226
227		if err := zr.Close(); err != nil {
228			log.Fatal(err)
229		}
230	}()
231
232	// Output:
233	// Received 1 bytes: A
234	// Received 4 bytes: long
235	// Received 4 bytes: time
236	// Received 3 bytes: ago
237	// Received 2 bytes: in
238	// Received 1 bytes: a
239	// Received 6 bytes: galaxy
240	// Received 4 bytes: far,
241	// Received 3 bytes: far
242	// Received 7 bytes: away...
243}
244