| // 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" | |
| "compress/flate" | |
| "fmt" | |
| "io" | |
| "log" | |
| "os" | |
| "strings" | |
| "sync" | |
| ) | |
| // 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.DefaultCompression, []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() | |
| zw, err := flate.NewWriter(wp, flate.BestSpeed) | |
| if err != nil { | |
| log.Fatal(err) | |
| } | |
| b := make([]byte, 256) | |
| for m := range strings.FieldsSeq("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... | |
| } | |