Update to gobook@0a3e8d997823052c6a8575feca6e515ac6f8299d
This commit is contained in:
parent
84af1e6bd3
commit
34cd1719c0
28
ch13/bzip-print/bzip2.c
Normal file
28
ch13/bzip-print/bzip2.c
Normal file
@ -0,0 +1,28 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 362.
|
||||
// This is the version that appears in print,
|
||||
// but it does not comply with the proposed
|
||||
// rules for passing pointers between Go and C.
|
||||
// (https://github.com/golang/proposal/blob/master/design/12416-cgo-pointers.md)
|
||||
// See gopl.io/ch13/bzip for an updated version.
|
||||
|
||||
//!+
|
||||
/* This file is gopl.io/ch13/bzip/bzip2.c, */
|
||||
/* a simple wrapper for libbzip2 suitable for cgo. */
|
||||
#include <bzlib.h>
|
||||
|
||||
int bz2compress(bz_stream *s, int action,
|
||||
char *in, unsigned *inlen, char *out, unsigned *outlen) {
|
||||
s->next_in = in;
|
||||
s->avail_in = *inlen;
|
||||
s->next_out = out;
|
||||
s->avail_out = *outlen;
|
||||
int r = BZ2_bzCompress(s, action);
|
||||
*inlen -= s->avail_in;
|
||||
*outlen -= s->avail_out;
|
||||
return r;
|
||||
}
|
||||
|
||||
//!-
|
96
ch13/bzip-print/bzip2.go
Normal file
96
ch13/bzip-print/bzip2.go
Normal file
@ -0,0 +1,96 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 362.
|
||||
// This is the version that appears in print,
|
||||
// but it does not comply with the proposed
|
||||
// rules for passing pointers between Go and C.
|
||||
// (https://github.com/golang/proposal/blob/master/design/12416-cgo-pointers.md)
|
||||
// See gopl.io/ch13/bzip for an updated version.
|
||||
//!+
|
||||
|
||||
// Package bzip provides a writer that uses bzip2 compression (bzip.org).
|
||||
package bzip
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -I/usr/include
|
||||
#cgo LDFLAGS: -L/usr/lib -lbz2
|
||||
#include <bzlib.h>
|
||||
int bz2compress(bz_stream *s, int action,
|
||||
char *in, unsigned *inlen, char *out, unsigned *outlen);
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"io"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type writer struct {
|
||||
w io.Writer // underlying output stream
|
||||
stream *C.bz_stream
|
||||
outbuf [64 * 1024]byte
|
||||
}
|
||||
|
||||
// NewWriter returns a writer for bzip2-compressed streams.
|
||||
func NewWriter(out io.Writer) io.WriteCloser {
|
||||
const (
|
||||
blockSize = 9
|
||||
verbosity = 0
|
||||
workFactor = 30
|
||||
)
|
||||
w := &writer{w: out, stream: new(C.bz_stream)}
|
||||
C.BZ2_bzCompressInit(w.stream, blockSize, verbosity, workFactor)
|
||||
return w
|
||||
}
|
||||
|
||||
//!-
|
||||
|
||||
//!+write
|
||||
func (w *writer) Write(data []byte) (int, error) {
|
||||
if w.stream == nil {
|
||||
panic("closed")
|
||||
}
|
||||
var total int // uncompressed bytes written
|
||||
|
||||
for len(data) > 0 {
|
||||
inlen, outlen := C.uint(len(data)), C.uint(cap(w.outbuf))
|
||||
C.bz2compress(w.stream, C.BZ_RUN,
|
||||
(*C.char)(unsafe.Pointer(&data[0])), &inlen,
|
||||
(*C.char)(unsafe.Pointer(&w.outbuf)), &outlen)
|
||||
total += int(inlen)
|
||||
data = data[inlen:]
|
||||
if _, err := w.w.Write(w.outbuf[:outlen]); err != nil {
|
||||
return total, err
|
||||
}
|
||||
}
|
||||
return total, nil
|
||||
}
|
||||
|
||||
//!-write
|
||||
|
||||
//!+close
|
||||
// Close flushes the compressed data and closes the stream.
|
||||
// It does not close the underlying io.Writer.
|
||||
func (w *writer) Close() error {
|
||||
if w.stream == nil {
|
||||
panic("closed")
|
||||
}
|
||||
defer func() {
|
||||
C.BZ2_bzCompressEnd(w.stream)
|
||||
w.stream = nil
|
||||
}()
|
||||
for {
|
||||
inlen, outlen := C.uint(0), C.uint(cap(w.outbuf))
|
||||
r := C.bz2compress(w.stream, C.BZ_FINISH, nil, &inlen,
|
||||
(*C.char)(unsafe.Pointer(&w.outbuf)), &outlen)
|
||||
if _, err := w.w.Write(w.outbuf[:outlen]); err != nil {
|
||||
return err
|
||||
}
|
||||
if r == C.BZ_STREAM_END {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//!-close
|
40
ch13/bzip-print/bzip2_test.go
Normal file
40
ch13/bzip-print/bzip2_test.go
Normal file
@ -0,0 +1,40 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
package bzip_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/bzip2" // reader
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"gopl.io/ch13/bzip" // writer
|
||||
)
|
||||
|
||||
func TestBzip2(t *testing.T) {
|
||||
var compressed, uncompressed bytes.Buffer
|
||||
w := bzip.NewWriter(&compressed)
|
||||
|
||||
// Write a repetitive message in a million pieces,
|
||||
// compressing one copy but not the other.
|
||||
tee := io.MultiWriter(w, &uncompressed)
|
||||
for i := 0; i < 1000000; i++ {
|
||||
io.WriteString(tee, "hello")
|
||||
}
|
||||
if err := w.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Check the size of the compressed stream.
|
||||
if got, want := compressed.Len(), 255; got != want {
|
||||
t.Errorf("1 million hellos compressed to %d bytes, want %d", got, want)
|
||||
}
|
||||
|
||||
// Decompress and compare with original.
|
||||
var decompressed bytes.Buffer
|
||||
io.Copy(&decompressed, bzip2.NewReader(&compressed))
|
||||
if !bytes.Equal(uncompressed.Bytes(), decompressed.Bytes()) {
|
||||
t.Error("decompression yielded a different message")
|
||||
}
|
||||
}
|
@ -17,6 +17,12 @@ int bz2compress(bz_stream *s, int action,
|
||||
int r = BZ2_bzCompress(s, action);
|
||||
*inlen -= s->avail_in;
|
||||
*outlen -= s->avail_out;
|
||||
|
||||
/* "C code may store a Go pointer in C memory subject to rule 2:
|
||||
* it must stop storing the pointer before it returns to Go." */
|
||||
s->next_in = NULL;
|
||||
s->next_out = NULL;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,20 @@
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 362.
|
||||
// The version of this file that appears in the book does not comply
|
||||
// with the proposed rules for passing pointers between Go and C.
|
||||
// (https://github.com/golang/proposal/blob/master/design/12416-cgo-pointers.md)
|
||||
// The rules forbid a C function like bz2compress from storing 'in' and
|
||||
// 'out' (pointers to variables allocated by Go) into the Go variable 's',
|
||||
// even temporarily.
|
||||
//
|
||||
// To comply with the rules, the bz_stream variable must be allocated
|
||||
// by C code. We have introduced two C functions, bz2alloc and
|
||||
// bz2free, to allocate and free instances of the bz_stream type.
|
||||
// Also, we have changed bz2compress so that before it returns, it
|
||||
// clears the fields of the bz_stream that contain point to Go
|
||||
// variables.
|
||||
|
||||
//!+
|
||||
|
||||
// Package bzip provides a writer that uses bzip2 compression (bzip.org).
|
||||
@ -11,8 +25,12 @@ package bzip
|
||||
#cgo CFLAGS: -I/usr/include
|
||||
#cgo LDFLAGS: -L/usr/lib -lbz2
|
||||
#include <bzlib.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
bz_stream* bz2alloc() { return calloc(1, sizeof(bz_stream)); }
|
||||
int bz2compress(bz_stream *s, int action,
|
||||
char *in, unsigned *inlen, char *out, unsigned *outlen);
|
||||
void bz2free(bz_stream* s) { return free(s); }
|
||||
*/
|
||||
import "C"
|
||||
|
||||
@ -34,9 +52,7 @@ func NewWriter(out io.Writer) io.WriteCloser {
|
||||
verbosity = 0
|
||||
workFactor = 30
|
||||
)
|
||||
// NOTE: This code may not work in future Go releases.
|
||||
// See http://www.gopl.io/errata.html for explanation.
|
||||
w := &writer{w: out, stream: new(C.bz_stream)}
|
||||
w := &writer{w: out, stream: C.bz2alloc()}
|
||||
C.BZ2_bzCompressInit(w.stream, blockSize, verbosity, workFactor)
|
||||
return w
|
||||
}
|
||||
@ -75,6 +91,7 @@ func (w *writer) Close() error {
|
||||
}
|
||||
defer func() {
|
||||
C.BZ2_bzCompressEnd(w.stream)
|
||||
C.bz2free(w.stream)
|
||||
w.stream = nil
|
||||
}()
|
||||
for {
|
||||
|
64
ch7/tempconv/tempconv.go.~master~
Normal file
64
ch7/tempconv/tempconv.go.~master~
Normal file
@ -0,0 +1,64 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
|
||||
// Package tempconv performs Celsius and Fahrenheit temperature computations.
|
||||
package tempconv
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type Celsius float64
|
||||
type Fahrenheit float64
|
||||
|
||||
func CToF(c Celsius) Fahrenheit { return Fahrenheit(c*9.0/5.0 + 32.0) }
|
||||
func FToC(f Fahrenheit) Celsius { return Celsius((f - 32.0) * 5.0 / 9.0) }
|
||||
|
||||
func (c Celsius) String() string { return fmt.Sprintf("%g°C", c) }
|
||||
|
||||
/*
|
||||
//!+flagvalue
|
||||
package flag
|
||||
|
||||
// Value is the interface to the value stored in a flag.
|
||||
type Value interface {
|
||||
String() string
|
||||
Set(string) error
|
||||
}
|
||||
//!-flagvalue
|
||||
*/
|
||||
|
||||
//!+celsiusflag
|
||||
// *celsiusFlag satisfies the flag.Value interface.
|
||||
type celsiusFlag struct{ Celsius }
|
||||
|
||||
func (f *celsiusFlag) Set(s string) error {
|
||||
var unit string
|
||||
var value float64
|
||||
fmt.Sscanf(s, "%f%s", &value, &unit) // no error check needed
|
||||
switch unit {
|
||||
case "C", "°C":
|
||||
f.Celsius = Celsius(value)
|
||||
return nil
|
||||
case "F", "°F":
|
||||
f.Celsius = FToC(Fahrenheit(value))
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("invalid temperature %q", s)
|
||||
}
|
||||
|
||||
//!-celsiusflag
|
||||
|
||||
//!+Celsiusflag
|
||||
// CelsiusFlag defines a Celsius flag with the specified name,
|
||||
// default value, and usage, and returns the address of the flag variable.
|
||||
// The flag argument must have a quantity and a unit, e.g., "100C".
|
||||
func CelsiusFlag(name string, value Celsius, usage string) *Celsius {
|
||||
f := celsiusFlag{value}
|
||||
flag.CommandLine.Var(&f, name, usage)
|
||||
return &f.Celsius
|
||||
}
|
||||
|
||||
//!-Celsiusflag
|
89
ch8/chat/chat.go.~master~
Normal file
89
ch8/chat/chat.go.~master~
Normal file
@ -0,0 +1,89 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
//!+
|
||||
|
||||
// Chat is a server that lets clients chat with each other.
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
)
|
||||
|
||||
//!+broadcaster
|
||||
type client chan<- string // an outgoing message channel
|
||||
|
||||
var (
|
||||
entering = make(chan client)
|
||||
leaving = make(chan client)
|
||||
messages = make(chan string) // all incoming client messages
|
||||
)
|
||||
|
||||
func broadcaster() {
|
||||
clients := make(map[client]bool) // all connected clients
|
||||
for {
|
||||
select {
|
||||
case msg := <-messages:
|
||||
// Broadcast incoming message to all
|
||||
// clients' outgoing message channels.
|
||||
for cli := range clients {
|
||||
cli <- msg
|
||||
}
|
||||
case cli := <-entering:
|
||||
clients[cli] = true
|
||||
case cli := <-leaving:
|
||||
delete(clients, cli)
|
||||
close(cli)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//!-broadcaster
|
||||
|
||||
//!+handleConn
|
||||
func handleConn(conn net.Conn) {
|
||||
ch := make(chan string) // outgoing client messages
|
||||
go clientWriter(conn, ch)
|
||||
|
||||
who := conn.RemoteAddr().String()
|
||||
entering <- ch
|
||||
messages <- who + " has arrived"
|
||||
input := bufio.NewScanner(conn)
|
||||
for input.Scan() {
|
||||
messages <- who + ": " + input.Text()
|
||||
}
|
||||
messages <- who + " has left"
|
||||
leaving <- ch
|
||||
conn.Close()
|
||||
}
|
||||
|
||||
func clientWriter(conn net.Conn, ch <-chan string) {
|
||||
for msg := range ch {
|
||||
fmt.Fprintln(conn, msg)
|
||||
}
|
||||
}
|
||||
|
||||
//!-handleConn
|
||||
|
||||
//!+main
|
||||
func main() {
|
||||
listener, err := net.Listen("tcp", "localhost:8000")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
go broadcaster()
|
||||
for {
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
continue
|
||||
}
|
||||
go handleConn(conn)
|
||||
}
|
||||
}
|
||||
|
||||
//!-main
|
Loading…
Reference in New Issue
Block a user