mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2025-12-18 11:44:20 +08:00
deploy: 06a1bdf735
This commit is contained in:
89
vendor/gopl.io/ch8/cake/cake.go
generated
vendored
Normal file
89
vendor/gopl.io/ch8/cake/cake.go
generated
vendored
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/
|
||||
|
||||
// See page 234.
|
||||
|
||||
// Package cake provides a simulation of
|
||||
// a concurrent cake shop with numerous parameters.
|
||||
//
|
||||
// Use this command to run the benchmarks:
|
||||
// $ go test -bench=. gopl.io/ch8/cake
|
||||
package cake
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Shop struct {
|
||||
Verbose bool
|
||||
Cakes int // number of cakes to bake
|
||||
BakeTime time.Duration // time to bake one cake
|
||||
BakeStdDev time.Duration // standard deviation of baking time
|
||||
BakeBuf int // buffer slots between baking and icing
|
||||
NumIcers int // number of cooks doing icing
|
||||
IceTime time.Duration // time to ice one cake
|
||||
IceStdDev time.Duration // standard deviation of icing time
|
||||
IceBuf int // buffer slots between icing and inscribing
|
||||
InscribeTime time.Duration // time to inscribe one cake
|
||||
InscribeStdDev time.Duration // standard deviation of inscribing time
|
||||
}
|
||||
|
||||
type cake int
|
||||
|
||||
func (s *Shop) baker(baked chan<- cake) {
|
||||
for i := 0; i < s.Cakes; i++ {
|
||||
c := cake(i)
|
||||
if s.Verbose {
|
||||
fmt.Println("baking", c)
|
||||
}
|
||||
work(s.BakeTime, s.BakeStdDev)
|
||||
baked <- c
|
||||
}
|
||||
close(baked)
|
||||
}
|
||||
|
||||
func (s *Shop) icer(iced chan<- cake, baked <-chan cake) {
|
||||
for c := range baked {
|
||||
if s.Verbose {
|
||||
fmt.Println("icing", c)
|
||||
}
|
||||
work(s.IceTime, s.IceStdDev)
|
||||
iced <- c
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Shop) inscriber(iced <-chan cake) {
|
||||
for i := 0; i < s.Cakes; i++ {
|
||||
c := <-iced
|
||||
if s.Verbose {
|
||||
fmt.Println("inscribing", c)
|
||||
}
|
||||
work(s.InscribeTime, s.InscribeStdDev)
|
||||
if s.Verbose {
|
||||
fmt.Println("finished", c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Work runs the simulation 'runs' times.
|
||||
func (s *Shop) Work(runs int) {
|
||||
for run := 0; run < runs; run++ {
|
||||
baked := make(chan cake, s.BakeBuf)
|
||||
iced := make(chan cake, s.IceBuf)
|
||||
go s.baker(baked)
|
||||
for i := 0; i < s.NumIcers; i++ {
|
||||
go s.icer(iced, baked)
|
||||
}
|
||||
s.inscriber(iced)
|
||||
}
|
||||
}
|
||||
|
||||
// work blocks the calling goroutine for a period of time
|
||||
// that is normally distributed around d
|
||||
// with a standard deviation of stddev.
|
||||
func work(d, stddev time.Duration) {
|
||||
delay := d + time.Duration(rand.NormFloat64()*float64(stddev))
|
||||
time.Sleep(delay)
|
||||
}
|
||||
74
vendor/gopl.io/ch8/cake/cake_test.go
generated
vendored
Normal file
74
vendor/gopl.io/ch8/cake/cake_test.go
generated
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
package cake_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"gopl.io/ch8/cake"
|
||||
)
|
||||
|
||||
var defaults = cake.Shop{
|
||||
Verbose: testing.Verbose(),
|
||||
Cakes: 20,
|
||||
BakeTime: 10 * time.Millisecond,
|
||||
NumIcers: 1,
|
||||
IceTime: 10 * time.Millisecond,
|
||||
InscribeTime: 10 * time.Millisecond,
|
||||
}
|
||||
|
||||
func Benchmark(b *testing.B) {
|
||||
// Baseline: one baker, one icer, one inscriber.
|
||||
// Each step takes exactly 10ms. No buffers.
|
||||
cakeshop := defaults
|
||||
cakeshop.Work(b.N) // 224 ms
|
||||
}
|
||||
|
||||
func BenchmarkBuffers(b *testing.B) {
|
||||
// Adding buffers has no effect.
|
||||
cakeshop := defaults
|
||||
cakeshop.BakeBuf = 10
|
||||
cakeshop.IceBuf = 10
|
||||
cakeshop.Work(b.N) // 224 ms
|
||||
}
|
||||
|
||||
func BenchmarkVariable(b *testing.B) {
|
||||
// Adding variability to rate of each step
|
||||
// increases total time due to channel delays.
|
||||
cakeshop := defaults
|
||||
cakeshop.BakeStdDev = cakeshop.BakeTime / 4
|
||||
cakeshop.IceStdDev = cakeshop.IceTime / 4
|
||||
cakeshop.InscribeStdDev = cakeshop.InscribeTime / 4
|
||||
cakeshop.Work(b.N) // 259 ms
|
||||
}
|
||||
|
||||
func BenchmarkVariableBuffers(b *testing.B) {
|
||||
// Adding channel buffers reduces
|
||||
// delays resulting from variability.
|
||||
cakeshop := defaults
|
||||
cakeshop.BakeStdDev = cakeshop.BakeTime / 4
|
||||
cakeshop.IceStdDev = cakeshop.IceTime / 4
|
||||
cakeshop.InscribeStdDev = cakeshop.InscribeTime / 4
|
||||
cakeshop.BakeBuf = 10
|
||||
cakeshop.IceBuf = 10
|
||||
cakeshop.Work(b.N) // 244 ms
|
||||
}
|
||||
|
||||
func BenchmarkSlowIcing(b *testing.B) {
|
||||
// Making the middle stage slower
|
||||
// adds directly to the critical path.
|
||||
cakeshop := defaults
|
||||
cakeshop.IceTime = 50 * time.Millisecond
|
||||
cakeshop.Work(b.N) // 1.032 s
|
||||
}
|
||||
|
||||
func BenchmarkSlowIcingManyIcers(b *testing.B) {
|
||||
// Adding more icing cooks reduces the cost of icing
|
||||
// to its sequential component, following Amdahl's Law.
|
||||
cakeshop := defaults
|
||||
cakeshop.IceTime = 50 * time.Millisecond
|
||||
cakeshop.NumIcers = 5
|
||||
cakeshop.Work(b.N) // 288ms
|
||||
}
|
||||
96
vendor/gopl.io/ch8/chat/chat.go
generated
vendored
Normal file
96
vendor/gopl.io/ch8/chat/chat.go
generated
vendored
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 254.
|
||||
//!+
|
||||
|
||||
// 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()
|
||||
ch <- "You are " + who
|
||||
messages <- who + " has arrived"
|
||||
entering <- ch
|
||||
|
||||
input := bufio.NewScanner(conn)
|
||||
for input.Scan() {
|
||||
messages <- who + ": " + input.Text()
|
||||
}
|
||||
// NOTE: ignoring potential errors from input.Err()
|
||||
|
||||
leaving <- ch
|
||||
messages <- who + " has left"
|
||||
conn.Close()
|
||||
}
|
||||
|
||||
func clientWriter(conn net.Conn, ch <-chan string) {
|
||||
for msg := range ch {
|
||||
fmt.Fprintln(conn, msg) // NOTE: ignoring network errors
|
||||
}
|
||||
}
|
||||
|
||||
//!-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
|
||||
89
vendor/gopl.io/ch8/chat/chat.go.~master~
generated
vendored
Normal file
89
vendor/gopl.io/ch8/chat/chat.go.~master~
generated
vendored
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
|
||||
43
vendor/gopl.io/ch8/clock1/clock.go
generated
vendored
Normal file
43
vendor/gopl.io/ch8/clock1/clock.go
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 219.
|
||||
//!+
|
||||
|
||||
// Clock1 is a TCP server that periodically writes the time.
|
||||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
listener, err := net.Listen("tcp", "localhost:8000")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
for {
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
log.Print(err) // e.g., connection aborted
|
||||
continue
|
||||
}
|
||||
handleConn(conn) // handle one connection at a time
|
||||
}
|
||||
}
|
||||
|
||||
func handleConn(c net.Conn) {
|
||||
defer c.Close()
|
||||
for {
|
||||
_, err := io.WriteString(c, time.Now().Format("15:04:05\n"))
|
||||
if err != nil {
|
||||
return // e.g., client disconnected
|
||||
}
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
//!-
|
||||
42
vendor/gopl.io/ch8/clock2/clock.go
generated
vendored
Normal file
42
vendor/gopl.io/ch8/clock2/clock.go
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 222.
|
||||
|
||||
// Clock is a TCP server that periodically writes the time.
|
||||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
func handleConn(c net.Conn) {
|
||||
defer c.Close()
|
||||
for {
|
||||
_, err := io.WriteString(c, time.Now().Format("15:04:05\n"))
|
||||
if err != nil {
|
||||
return // e.g., client disconnected
|
||||
}
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
listener, err := net.Listen("tcp", "localhost:8000")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
//!+
|
||||
for {
|
||||
conn, err := listener.Accept()
|
||||
if err != nil {
|
||||
log.Print(err) // e.g., connection aborted
|
||||
continue
|
||||
}
|
||||
go handleConn(conn) // handle connections concurrently
|
||||
}
|
||||
//!-
|
||||
}
|
||||
29
vendor/gopl.io/ch8/countdown1/countdown.go
generated
vendored
Normal file
29
vendor/gopl.io/ch8/countdown1/countdown.go
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 244.
|
||||
|
||||
// Countdown implements the countdown for a rocket launch.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
//!+
|
||||
func main() {
|
||||
fmt.Println("Commencing countdown.")
|
||||
tick := time.Tick(1 * time.Second)
|
||||
for countdown := 10; countdown > 0; countdown-- {
|
||||
fmt.Println(countdown)
|
||||
<-tick
|
||||
}
|
||||
launch()
|
||||
}
|
||||
|
||||
//!-
|
||||
|
||||
func launch() {
|
||||
fmt.Println("Lift off!")
|
||||
}
|
||||
46
vendor/gopl.io/ch8/countdown2/countdown.go
generated
vendored
Normal file
46
vendor/gopl.io/ch8/countdown2/countdown.go
generated
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 244.
|
||||
|
||||
// Countdown implements the countdown for a rocket launch.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
//!+
|
||||
|
||||
func main() {
|
||||
// ...create abort channel...
|
||||
|
||||
//!-
|
||||
|
||||
//!+abort
|
||||
abort := make(chan struct{})
|
||||
go func() {
|
||||
os.Stdin.Read(make([]byte, 1)) // read a single byte
|
||||
abort <- struct{}{}
|
||||
}()
|
||||
//!-abort
|
||||
|
||||
//!+
|
||||
fmt.Println("Commencing countdown. Press return to abort.")
|
||||
select {
|
||||
case <-time.After(10 * time.Second):
|
||||
// Do nothing.
|
||||
case <-abort:
|
||||
fmt.Println("Launch aborted!")
|
||||
return
|
||||
}
|
||||
launch()
|
||||
}
|
||||
|
||||
//!-
|
||||
|
||||
func launch() {
|
||||
fmt.Println("Lift off!")
|
||||
}
|
||||
51
vendor/gopl.io/ch8/countdown3/countdown.go
generated
vendored
Normal file
51
vendor/gopl.io/ch8/countdown3/countdown.go
generated
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 246.
|
||||
|
||||
// Countdown implements the countdown for a rocket launch.
|
||||
package main
|
||||
|
||||
// NOTE: the ticker goroutine never terminates if the launch is aborted.
|
||||
// This is a "goroutine leak".
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
//!+
|
||||
|
||||
func main() {
|
||||
// ...create abort channel...
|
||||
|
||||
//!-
|
||||
|
||||
abort := make(chan struct{})
|
||||
go func() {
|
||||
os.Stdin.Read(make([]byte, 1)) // read a single byte
|
||||
abort <- struct{}{}
|
||||
}()
|
||||
|
||||
//!+
|
||||
fmt.Println("Commencing countdown. Press return to abort.")
|
||||
tick := time.Tick(1 * time.Second)
|
||||
for countdown := 10; countdown > 0; countdown-- {
|
||||
fmt.Println(countdown)
|
||||
select {
|
||||
case <-tick:
|
||||
// Do nothing.
|
||||
case <-abort:
|
||||
fmt.Println("Launch aborted!")
|
||||
return
|
||||
}
|
||||
}
|
||||
launch()
|
||||
}
|
||||
|
||||
//!-
|
||||
|
||||
func launch() {
|
||||
fmt.Println("Lift off!")
|
||||
}
|
||||
72
vendor/gopl.io/ch8/crawl1/findlinks.go
generated
vendored
Normal file
72
vendor/gopl.io/ch8/crawl1/findlinks.go
generated
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 240.
|
||||
|
||||
// Crawl1 crawls web links starting with the command-line arguments.
|
||||
//
|
||||
// This version quickly exhausts available file descriptors
|
||||
// due to excessive concurrent calls to links.Extract.
|
||||
//
|
||||
// Also, it never terminates because the worklist is never closed.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"gopl.io/ch5/links"
|
||||
)
|
||||
|
||||
//!+crawl
|
||||
func crawl(url string) []string {
|
||||
fmt.Println(url)
|
||||
list, err := links.Extract(url)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
//!-crawl
|
||||
|
||||
//!+main
|
||||
func main() {
|
||||
worklist := make(chan []string)
|
||||
|
||||
// Start with the command-line arguments.
|
||||
go func() { worklist <- os.Args[1:] }()
|
||||
|
||||
// Crawl the web concurrently.
|
||||
seen := make(map[string]bool)
|
||||
for list := range worklist {
|
||||
for _, link := range list {
|
||||
if !seen[link] {
|
||||
seen[link] = true
|
||||
go func(link string) {
|
||||
worklist <- crawl(link)
|
||||
}(link)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//!-main
|
||||
|
||||
/*
|
||||
//!+output
|
||||
$ go build gopl.io/ch8/crawl1
|
||||
$ ./crawl1 http://gopl.io/
|
||||
http://gopl.io/
|
||||
https://golang.org/help/
|
||||
|
||||
https://golang.org/doc/
|
||||
https://golang.org/blog/
|
||||
...
|
||||
2015/07/15 18:22:12 Get ...: dial tcp: lookup blog.golang.org: no such host
|
||||
2015/07/15 18:22:12 Get ...: dial tcp 23.21.222.120:443: socket:
|
||||
too many open files
|
||||
...
|
||||
//!-output
|
||||
*/
|
||||
64
vendor/gopl.io/ch8/crawl2/findlinks.go
generated
vendored
Normal file
64
vendor/gopl.io/ch8/crawl2/findlinks.go
generated
vendored
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/
|
||||
|
||||
// See page 241.
|
||||
|
||||
// Crawl2 crawls web links starting with the command-line arguments.
|
||||
//
|
||||
// This version uses a buffered channel as a counting semaphore
|
||||
// to limit the number of concurrent calls to links.Extract.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"gopl.io/ch5/links"
|
||||
)
|
||||
|
||||
//!+sema
|
||||
// tokens is a counting semaphore used to
|
||||
// enforce a limit of 20 concurrent requests.
|
||||
var tokens = make(chan struct{}, 20)
|
||||
|
||||
func crawl(url string) []string {
|
||||
fmt.Println(url)
|
||||
tokens <- struct{}{} // acquire a token
|
||||
list, err := links.Extract(url)
|
||||
<-tokens // release the token
|
||||
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
//!-sema
|
||||
|
||||
//!+
|
||||
func main() {
|
||||
worklist := make(chan []string)
|
||||
var n int // number of pending sends to worklist
|
||||
|
||||
// Start with the command-line arguments.
|
||||
n++
|
||||
go func() { worklist <- os.Args[1:] }()
|
||||
|
||||
// Crawl the web concurrently.
|
||||
seen := make(map[string]bool)
|
||||
for ; n > 0; n-- {
|
||||
list := <-worklist
|
||||
for _, link := range list {
|
||||
if !seen[link] {
|
||||
seen[link] = true
|
||||
n++
|
||||
go func(link string) {
|
||||
worklist <- crawl(link)
|
||||
}(link)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//!-
|
||||
61
vendor/gopl.io/ch8/crawl3/findlinks.go
generated
vendored
Normal file
61
vendor/gopl.io/ch8/crawl3/findlinks.go
generated
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 243.
|
||||
|
||||
// Crawl3 crawls web links starting with the command-line arguments.
|
||||
//
|
||||
// This version uses bounded parallelism.
|
||||
// For simplicity, it does not address the termination problem.
|
||||
//
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"gopl.io/ch5/links"
|
||||
)
|
||||
|
||||
func crawl(url string) []string {
|
||||
fmt.Println(url)
|
||||
list, err := links.Extract(url)
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
//!+
|
||||
func main() {
|
||||
worklist := make(chan []string) // lists of URLs, may have duplicates
|
||||
unseenLinks := make(chan string) // de-duplicated URLs
|
||||
|
||||
// Add command-line arguments to worklist.
|
||||
go func() { worklist <- os.Args[1:] }()
|
||||
|
||||
// Create 20 crawler goroutines to fetch each unseen link.
|
||||
for i := 0; i < 20; i++ {
|
||||
go func() {
|
||||
for link := range unseenLinks {
|
||||
foundLinks := crawl(link)
|
||||
go func() { worklist <- foundLinks }()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// The main goroutine de-duplicates worklist items
|
||||
// and sends the unseen ones to the crawlers.
|
||||
seen := make(map[string]bool)
|
||||
for list := range worklist {
|
||||
for _, link := range list {
|
||||
if !seen[link] {
|
||||
seen[link] = true
|
||||
unseenLinks <- link
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//!-
|
||||
79
vendor/gopl.io/ch8/du1/main.go
generated
vendored
Normal file
79
vendor/gopl.io/ch8/du1/main.go
generated
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 247.
|
||||
|
||||
//!+main
|
||||
|
||||
// The du1 command computes the disk usage of the files in a directory.
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Determine the initial directories.
|
||||
flag.Parse()
|
||||
roots := flag.Args()
|
||||
if len(roots) == 0 {
|
||||
roots = []string{"."}
|
||||
}
|
||||
|
||||
// Traverse the file tree.
|
||||
fileSizes := make(chan int64)
|
||||
go func() {
|
||||
for _, root := range roots {
|
||||
walkDir(root, fileSizes)
|
||||
}
|
||||
close(fileSizes)
|
||||
}()
|
||||
|
||||
// Print the results.
|
||||
var nfiles, nbytes int64
|
||||
for size := range fileSizes {
|
||||
nfiles++
|
||||
nbytes += size
|
||||
}
|
||||
printDiskUsage(nfiles, nbytes)
|
||||
}
|
||||
|
||||
func printDiskUsage(nfiles, nbytes int64) {
|
||||
fmt.Printf("%d files %.1f GB\n", nfiles, float64(nbytes)/1e9)
|
||||
}
|
||||
|
||||
//!-main
|
||||
|
||||
//!+walkDir
|
||||
|
||||
// walkDir recursively walks the file tree rooted at dir
|
||||
// and sends the size of each found file on fileSizes.
|
||||
func walkDir(dir string, fileSizes chan<- int64) {
|
||||
for _, entry := range dirents(dir) {
|
||||
if entry.IsDir() {
|
||||
subdir := filepath.Join(dir, entry.Name())
|
||||
walkDir(subdir, fileSizes)
|
||||
} else {
|
||||
fileSizes <- entry.Size()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// dirents returns the entries of directory dir.
|
||||
func dirents(dir string) []os.FileInfo {
|
||||
entries, err := ioutil.ReadDir(dir)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "du1: %v\n", err)
|
||||
return nil
|
||||
}
|
||||
return entries
|
||||
}
|
||||
|
||||
//!-walkDir
|
||||
|
||||
// The du1 variant uses two goroutines and
|
||||
// prints the total after every file is found.
|
||||
94
vendor/gopl.io/ch8/du2/main.go
generated
vendored
Normal file
94
vendor/gopl.io/ch8/du2/main.go
generated
vendored
Normal file
@@ -0,0 +1,94 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 249.
|
||||
|
||||
// The du2 command computes the disk usage of the files in a directory.
|
||||
package main
|
||||
|
||||
// The du2 variant uses select and a time.Ticker
|
||||
// to print the totals periodically if -v is set.
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
)
|
||||
|
||||
//!+
|
||||
var verbose = flag.Bool("v", false, "show verbose progress messages")
|
||||
|
||||
func main() {
|
||||
// ...start background goroutine...
|
||||
|
||||
//!-
|
||||
// Determine the initial directories.
|
||||
flag.Parse()
|
||||
roots := flag.Args()
|
||||
if len(roots) == 0 {
|
||||
roots = []string{"."}
|
||||
}
|
||||
|
||||
// Traverse the file tree.
|
||||
fileSizes := make(chan int64)
|
||||
go func() {
|
||||
for _, root := range roots {
|
||||
walkDir(root, fileSizes)
|
||||
}
|
||||
close(fileSizes)
|
||||
}()
|
||||
|
||||
//!+
|
||||
// Print the results periodically.
|
||||
var tick <-chan time.Time
|
||||
if *verbose {
|
||||
tick = time.Tick(500 * time.Millisecond)
|
||||
}
|
||||
var nfiles, nbytes int64
|
||||
loop:
|
||||
for {
|
||||
select {
|
||||
case size, ok := <-fileSizes:
|
||||
if !ok {
|
||||
break loop // fileSizes was closed
|
||||
}
|
||||
nfiles++
|
||||
nbytes += size
|
||||
case <-tick:
|
||||
printDiskUsage(nfiles, nbytes)
|
||||
}
|
||||
}
|
||||
printDiskUsage(nfiles, nbytes) // final totals
|
||||
}
|
||||
|
||||
//!-
|
||||
|
||||
func printDiskUsage(nfiles, nbytes int64) {
|
||||
fmt.Printf("%d files %.1f GB\n", nfiles, float64(nbytes)/1e9)
|
||||
}
|
||||
|
||||
// walkDir recursively walks the file tree rooted at dir
|
||||
// and sends the size of each found file on fileSizes.
|
||||
func walkDir(dir string, fileSizes chan<- int64) {
|
||||
for _, entry := range dirents(dir) {
|
||||
if entry.IsDir() {
|
||||
subdir := filepath.Join(dir, entry.Name())
|
||||
walkDir(subdir, fileSizes)
|
||||
} else {
|
||||
fileSizes <- entry.Size()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// dirents returns the entries of directory dir.
|
||||
func dirents(dir string) []os.FileInfo {
|
||||
entries, err := ioutil.ReadDir(dir)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "du: %v\n", err)
|
||||
return nil
|
||||
}
|
||||
return entries
|
||||
}
|
||||
118
vendor/gopl.io/ch8/du3/main.go
generated
vendored
Normal file
118
vendor/gopl.io/ch8/du3/main.go
generated
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 250.
|
||||
|
||||
// The du3 command computes the disk usage of the files in a directory.
|
||||
package main
|
||||
|
||||
// The du3 variant traverses all directories in parallel.
|
||||
// It uses a concurrency-limiting counting semaphore
|
||||
// to avoid opening too many files at once.
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var vFlag = flag.Bool("v", false, "show verbose progress messages")
|
||||
|
||||
//!+
|
||||
func main() {
|
||||
// ...determine roots...
|
||||
|
||||
//!-
|
||||
flag.Parse()
|
||||
|
||||
// Determine the initial directories.
|
||||
roots := flag.Args()
|
||||
if len(roots) == 0 {
|
||||
roots = []string{"."}
|
||||
}
|
||||
|
||||
//!+
|
||||
// Traverse each root of the file tree in parallel.
|
||||
fileSizes := make(chan int64)
|
||||
var n sync.WaitGroup
|
||||
for _, root := range roots {
|
||||
n.Add(1)
|
||||
go walkDir(root, &n, fileSizes)
|
||||
}
|
||||
go func() {
|
||||
n.Wait()
|
||||
close(fileSizes)
|
||||
}()
|
||||
//!-
|
||||
|
||||
// Print the results periodically.
|
||||
var tick <-chan time.Time
|
||||
if *vFlag {
|
||||
tick = time.Tick(500 * time.Millisecond)
|
||||
}
|
||||
var nfiles, nbytes int64
|
||||
loop:
|
||||
for {
|
||||
select {
|
||||
case size, ok := <-fileSizes:
|
||||
if !ok {
|
||||
break loop // fileSizes was closed
|
||||
}
|
||||
nfiles++
|
||||
nbytes += size
|
||||
case <-tick:
|
||||
printDiskUsage(nfiles, nbytes)
|
||||
}
|
||||
}
|
||||
|
||||
printDiskUsage(nfiles, nbytes) // final totals
|
||||
//!+
|
||||
// ...select loop...
|
||||
}
|
||||
|
||||
//!-
|
||||
|
||||
func printDiskUsage(nfiles, nbytes int64) {
|
||||
fmt.Printf("%d files %.1f GB\n", nfiles, float64(nbytes)/1e9)
|
||||
}
|
||||
|
||||
// walkDir recursively walks the file tree rooted at dir
|
||||
// and sends the size of each found file on fileSizes.
|
||||
//!+walkDir
|
||||
func walkDir(dir string, n *sync.WaitGroup, fileSizes chan<- int64) {
|
||||
defer n.Done()
|
||||
for _, entry := range dirents(dir) {
|
||||
if entry.IsDir() {
|
||||
n.Add(1)
|
||||
subdir := filepath.Join(dir, entry.Name())
|
||||
go walkDir(subdir, n, fileSizes)
|
||||
} else {
|
||||
fileSizes <- entry.Size()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//!-walkDir
|
||||
|
||||
//!+sema
|
||||
// sema is a counting semaphore for limiting concurrency in dirents.
|
||||
var sema = make(chan struct{}, 20)
|
||||
|
||||
// dirents returns the entries of directory dir.
|
||||
func dirents(dir string) []os.FileInfo {
|
||||
sema <- struct{}{} // acquire token
|
||||
defer func() { <-sema }() // release token
|
||||
// ...
|
||||
//!-sema
|
||||
|
||||
entries, err := ioutil.ReadDir(dir)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "du: %v\n", err)
|
||||
return nil
|
||||
}
|
||||
return entries
|
||||
}
|
||||
145
vendor/gopl.io/ch8/du4/main.go
generated
vendored
Normal file
145
vendor/gopl.io/ch8/du4/main.go
generated
vendored
Normal file
@@ -0,0 +1,145 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 251.
|
||||
|
||||
// The du4 command computes the disk usage of the files in a directory.
|
||||
package main
|
||||
|
||||
// The du4 variant includes cancellation:
|
||||
// it terminates quickly when the user hits return.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
//!+1
|
||||
var done = make(chan struct{})
|
||||
|
||||
func cancelled() bool {
|
||||
select {
|
||||
case <-done:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
//!-1
|
||||
|
||||
func main() {
|
||||
// Determine the initial directories.
|
||||
roots := os.Args[1:]
|
||||
if len(roots) == 0 {
|
||||
roots = []string{"."}
|
||||
}
|
||||
|
||||
//!+2
|
||||
// Cancel traversal when input is detected.
|
||||
go func() {
|
||||
os.Stdin.Read(make([]byte, 1)) // read a single byte
|
||||
close(done)
|
||||
}()
|
||||
//!-2
|
||||
|
||||
// Traverse each root of the file tree in parallel.
|
||||
fileSizes := make(chan int64)
|
||||
var n sync.WaitGroup
|
||||
for _, root := range roots {
|
||||
n.Add(1)
|
||||
go walkDir(root, &n, fileSizes)
|
||||
}
|
||||
go func() {
|
||||
n.Wait()
|
||||
close(fileSizes)
|
||||
}()
|
||||
|
||||
// Print the results periodically.
|
||||
tick := time.Tick(500 * time.Millisecond)
|
||||
var nfiles, nbytes int64
|
||||
loop:
|
||||
//!+3
|
||||
for {
|
||||
select {
|
||||
case <-done:
|
||||
// Drain fileSizes to allow existing goroutines to finish.
|
||||
for range fileSizes {
|
||||
// Do nothing.
|
||||
}
|
||||
return
|
||||
case size, ok := <-fileSizes:
|
||||
// ...
|
||||
//!-3
|
||||
if !ok {
|
||||
break loop // fileSizes was closed
|
||||
}
|
||||
nfiles++
|
||||
nbytes += size
|
||||
case <-tick:
|
||||
printDiskUsage(nfiles, nbytes)
|
||||
}
|
||||
}
|
||||
printDiskUsage(nfiles, nbytes) // final totals
|
||||
}
|
||||
|
||||
func printDiskUsage(nfiles, nbytes int64) {
|
||||
fmt.Printf("%d files %.1f GB\n", nfiles, float64(nbytes)/1e9)
|
||||
}
|
||||
|
||||
// walkDir recursively walks the file tree rooted at dir
|
||||
// and sends the size of each found file on fileSizes.
|
||||
//!+4
|
||||
func walkDir(dir string, n *sync.WaitGroup, fileSizes chan<- int64) {
|
||||
defer n.Done()
|
||||
if cancelled() {
|
||||
return
|
||||
}
|
||||
for _, entry := range dirents(dir) {
|
||||
// ...
|
||||
//!-4
|
||||
if entry.IsDir() {
|
||||
n.Add(1)
|
||||
subdir := filepath.Join(dir, entry.Name())
|
||||
go walkDir(subdir, n, fileSizes)
|
||||
} else {
|
||||
fileSizes <- entry.Size()
|
||||
}
|
||||
//!+4
|
||||
}
|
||||
}
|
||||
|
||||
//!-4
|
||||
|
||||
var sema = make(chan struct{}, 20) // concurrency-limiting counting semaphore
|
||||
|
||||
// dirents returns the entries of directory dir.
|
||||
//!+5
|
||||
func dirents(dir string) []os.FileInfo {
|
||||
select {
|
||||
case sema <- struct{}{}: // acquire token
|
||||
case <-done:
|
||||
return nil // cancelled
|
||||
}
|
||||
defer func() { <-sema }() // release token
|
||||
|
||||
// ...read directory...
|
||||
//!-5
|
||||
|
||||
f, err := os.Open(dir)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "du: %v\n", err)
|
||||
return nil
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
entries, err := f.Readdir(0) // 0 => no limit; read all entries
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "du: %v\n", err)
|
||||
// Don't return: Readdir may return partial results.
|
||||
}
|
||||
return entries
|
||||
}
|
||||
32
vendor/gopl.io/ch8/netcat1/netcat.go
generated
vendored
Normal file
32
vendor/gopl.io/ch8/netcat1/netcat.go
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 221.
|
||||
//!+
|
||||
|
||||
// Netcat1 is a read-only TCP client.
|
||||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
conn, err := net.Dial("tcp", "localhost:8000")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer conn.Close()
|
||||
mustCopy(os.Stdout, conn)
|
||||
}
|
||||
|
||||
func mustCopy(dst io.Writer, src io.Reader) {
|
||||
if _, err := io.Copy(dst, src); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
//!-
|
||||
33
vendor/gopl.io/ch8/netcat2/netcat.go
generated
vendored
Normal file
33
vendor/gopl.io/ch8/netcat2/netcat.go
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 223.
|
||||
|
||||
// Netcat is a simple read/write client for TCP servers.
|
||||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
)
|
||||
|
||||
//!+
|
||||
func main() {
|
||||
conn, err := net.Dial("tcp", "localhost:8000")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer conn.Close()
|
||||
go mustCopy(os.Stdout, conn)
|
||||
mustCopy(conn, os.Stdin)
|
||||
}
|
||||
|
||||
//!-
|
||||
|
||||
func mustCopy(dst io.Writer, src io.Reader) {
|
||||
if _, err := io.Copy(dst, src); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
39
vendor/gopl.io/ch8/netcat3/netcat.go
generated
vendored
Normal file
39
vendor/gopl.io/ch8/netcat3/netcat.go
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 227.
|
||||
|
||||
// Netcat is a simple read/write client for TCP servers.
|
||||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
)
|
||||
|
||||
//!+
|
||||
func main() {
|
||||
conn, err := net.Dial("tcp", "localhost:8000")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
io.Copy(os.Stdout, conn) // NOTE: ignoring errors
|
||||
log.Println("done")
|
||||
done <- struct{}{} // signal the main goroutine
|
||||
}()
|
||||
mustCopy(conn, os.Stdin)
|
||||
conn.Close()
|
||||
<-done // wait for background goroutine to finish
|
||||
}
|
||||
|
||||
//!-
|
||||
|
||||
func mustCopy(dst io.Writer, src io.Reader) {
|
||||
if _, err := io.Copy(dst, src); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
37
vendor/gopl.io/ch8/pipeline1/main.go
generated
vendored
Normal file
37
vendor/gopl.io/ch8/pipeline1/main.go
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 228.
|
||||
|
||||
// Pipeline1 demonstrates an infinite 3-stage pipeline.
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
//!+
|
||||
func main() {
|
||||
naturals := make(chan int)
|
||||
squares := make(chan int)
|
||||
|
||||
// Counter
|
||||
go func() {
|
||||
for x := 0; ; x++ {
|
||||
naturals <- x
|
||||
}
|
||||
}()
|
||||
|
||||
// Squarer
|
||||
go func() {
|
||||
for {
|
||||
x := <-naturals
|
||||
squares <- x * x
|
||||
}
|
||||
}()
|
||||
|
||||
// Printer (in main goroutine)
|
||||
for {
|
||||
fmt.Println(<-squares)
|
||||
}
|
||||
}
|
||||
|
||||
//!-
|
||||
38
vendor/gopl.io/ch8/pipeline2/main.go
generated
vendored
Normal file
38
vendor/gopl.io/ch8/pipeline2/main.go
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 229.
|
||||
|
||||
// Pipeline2 demonstrates a finite 3-stage pipeline.
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
//!+
|
||||
func main() {
|
||||
naturals := make(chan int)
|
||||
squares := make(chan int)
|
||||
|
||||
// Counter
|
||||
go func() {
|
||||
for x := 0; x < 100; x++ {
|
||||
naturals <- x
|
||||
}
|
||||
close(naturals)
|
||||
}()
|
||||
|
||||
// Squarer
|
||||
go func() {
|
||||
for x := range naturals {
|
||||
squares <- x * x
|
||||
}
|
||||
close(squares)
|
||||
}()
|
||||
|
||||
// Printer (in main goroutine)
|
||||
for x := range squares {
|
||||
fmt.Println(x)
|
||||
}
|
||||
}
|
||||
|
||||
//!-
|
||||
42
vendor/gopl.io/ch8/pipeline3/main.go
generated
vendored
Normal file
42
vendor/gopl.io/ch8/pipeline3/main.go
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 231.
|
||||
|
||||
// Pipeline3 demonstrates a finite 3-stage pipeline
|
||||
// with range, close, and unidirectional channel types.
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
//!+
|
||||
func counter(out chan<- int) {
|
||||
for x := 0; x < 100; x++ {
|
||||
out <- x
|
||||
}
|
||||
close(out)
|
||||
}
|
||||
|
||||
func squarer(out chan<- int, in <-chan int) {
|
||||
for v := range in {
|
||||
out <- v * v
|
||||
}
|
||||
close(out)
|
||||
}
|
||||
|
||||
func printer(in <-chan int) {
|
||||
for v := range in {
|
||||
fmt.Println(v)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
naturals := make(chan int)
|
||||
squares := make(chan int)
|
||||
|
||||
go counter(naturals)
|
||||
go squarer(squares, naturals)
|
||||
printer(squares)
|
||||
}
|
||||
|
||||
//!-
|
||||
51
vendor/gopl.io/ch8/reverb1/reverb.go
generated
vendored
Normal file
51
vendor/gopl.io/ch8/reverb1/reverb.go
generated
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 223.
|
||||
|
||||
// Reverb1 is a TCP server that simulates an echo.
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
//!+
|
||||
func echo(c net.Conn, shout string, delay time.Duration) {
|
||||
fmt.Fprintln(c, "\t", strings.ToUpper(shout))
|
||||
time.Sleep(delay)
|
||||
fmt.Fprintln(c, "\t", shout)
|
||||
time.Sleep(delay)
|
||||
fmt.Fprintln(c, "\t", strings.ToLower(shout))
|
||||
}
|
||||
|
||||
func handleConn(c net.Conn) {
|
||||
input := bufio.NewScanner(c)
|
||||
for input.Scan() {
|
||||
echo(c, input.Text(), 1*time.Second)
|
||||
}
|
||||
// NOTE: ignoring potential errors from input.Err()
|
||||
c.Close()
|
||||
}
|
||||
|
||||
//!-
|
||||
|
||||
func main() {
|
||||
l, err := net.Listen("tcp", "localhost:8000")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
for {
|
||||
conn, err := l.Accept()
|
||||
if err != nil {
|
||||
log.Print(err) // e.g., connection aborted
|
||||
continue
|
||||
}
|
||||
go handleConn(conn)
|
||||
}
|
||||
}
|
||||
51
vendor/gopl.io/ch8/reverb2/reverb.go
generated
vendored
Normal file
51
vendor/gopl.io/ch8/reverb2/reverb.go
generated
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 224.
|
||||
|
||||
// Reverb2 is a TCP server that simulates an echo.
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func echo(c net.Conn, shout string, delay time.Duration) {
|
||||
fmt.Fprintln(c, "\t", strings.ToUpper(shout))
|
||||
time.Sleep(delay)
|
||||
fmt.Fprintln(c, "\t", shout)
|
||||
time.Sleep(delay)
|
||||
fmt.Fprintln(c, "\t", strings.ToLower(shout))
|
||||
}
|
||||
|
||||
//!+
|
||||
func handleConn(c net.Conn) {
|
||||
input := bufio.NewScanner(c)
|
||||
for input.Scan() {
|
||||
go echo(c, input.Text(), 1*time.Second)
|
||||
}
|
||||
// NOTE: ignoring potential errors from input.Err()
|
||||
c.Close()
|
||||
}
|
||||
|
||||
//!-
|
||||
|
||||
func main() {
|
||||
l, err := net.Listen("tcp", "localhost:8000")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
for {
|
||||
conn, err := l.Accept()
|
||||
if err != nil {
|
||||
log.Print(err) // e.g., connection aborted
|
||||
continue
|
||||
}
|
||||
go handleConn(conn)
|
||||
}
|
||||
}
|
||||
38
vendor/gopl.io/ch8/spinner/main.go
generated
vendored
Normal file
38
vendor/gopl.io/ch8/spinner/main.go
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 218.
|
||||
|
||||
// Spinner displays an animation while computing the 45th Fibonacci number.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
//!+
|
||||
func main() {
|
||||
go spinner(100 * time.Millisecond)
|
||||
const n = 45
|
||||
fibN := fib(n) // slow
|
||||
fmt.Printf("\rFibonacci(%d) = %d\n", n, fibN)
|
||||
}
|
||||
|
||||
func spinner(delay time.Duration) {
|
||||
for {
|
||||
for _, r := range `-\|/` {
|
||||
fmt.Printf("\r%c", r)
|
||||
time.Sleep(delay)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func fib(x int) int {
|
||||
if x < 2 {
|
||||
return x
|
||||
}
|
||||
return fib(x-1) + fib(x-2)
|
||||
}
|
||||
|
||||
//!-
|
||||
42
vendor/gopl.io/ch8/thumbnail/main.go
generated
vendored
Normal file
42
vendor/gopl.io/ch8/thumbnail/main.go
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// +build ignore
|
||||
|
||||
// The thumbnail command produces thumbnails of JPEG files
|
||||
// whose names are provided on each line of the standard input.
|
||||
//
|
||||
// The "+build ignore" tag (see p.295) excludes this file from the
|
||||
// thumbnail package, but it can be compiled as a command and run like
|
||||
// this:
|
||||
//
|
||||
// Run with:
|
||||
// $ go run $GOPATH/src/gopl.io/ch8/thumbnail/main.go
|
||||
// foo.jpeg
|
||||
// ^D
|
||||
//
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"gopl.io/ch8/thumbnail"
|
||||
)
|
||||
|
||||
func main() {
|
||||
input := bufio.NewScanner(os.Stdin)
|
||||
for input.Scan() {
|
||||
thumb, err := thumbnail.ImageFile(input.Text())
|
||||
if err != nil {
|
||||
log.Print(err)
|
||||
continue
|
||||
}
|
||||
fmt.Println(thumb)
|
||||
}
|
||||
if err := input.Err(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
86
vendor/gopl.io/ch8/thumbnail/thumbnail.go
generated
vendored
Normal file
86
vendor/gopl.io/ch8/thumbnail/thumbnail.go
generated
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 234.
|
||||
|
||||
// The thumbnail package produces thumbnail-size images from
|
||||
// larger images. Only JPEG images are currently supported.
|
||||
package thumbnail
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image"
|
||||
"image/jpeg"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Image returns a thumbnail-size version of src.
|
||||
func Image(src image.Image) image.Image {
|
||||
// Compute thumbnail size, preserving aspect ratio.
|
||||
xs := src.Bounds().Size().X
|
||||
ys := src.Bounds().Size().Y
|
||||
width, height := 128, 128
|
||||
if aspect := float64(xs) / float64(ys); aspect < 1.0 {
|
||||
width = int(128 * aspect) // portrait
|
||||
} else {
|
||||
height = int(128 / aspect) // landscape
|
||||
}
|
||||
xscale := float64(xs) / float64(width)
|
||||
yscale := float64(ys) / float64(height)
|
||||
|
||||
dst := image.NewRGBA(image.Rect(0, 0, width, height))
|
||||
|
||||
// a very crude scaling algorithm
|
||||
for x := 0; x < width; x++ {
|
||||
for y := 0; y < height; y++ {
|
||||
srcx := int(float64(x) * xscale)
|
||||
srcy := int(float64(y) * yscale)
|
||||
dst.Set(x, y, src.At(srcx, srcy))
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
// ImageStream reads an image from r and
|
||||
// writes a thumbnail-size version of it to w.
|
||||
func ImageStream(w io.Writer, r io.Reader) error {
|
||||
src, _, err := image.Decode(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dst := Image(src)
|
||||
return jpeg.Encode(w, dst, nil)
|
||||
}
|
||||
|
||||
// ImageFile2 reads an image from infile and writes
|
||||
// a thumbnail-size version of it to outfile.
|
||||
func ImageFile2(outfile, infile string) (err error) {
|
||||
in, err := os.Open(infile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer in.Close()
|
||||
|
||||
out, err := os.Create(outfile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ImageStream(out, in); err != nil {
|
||||
out.Close()
|
||||
return fmt.Errorf("scaling %s to %s: %s", infile, outfile, err)
|
||||
}
|
||||
return out.Close()
|
||||
}
|
||||
|
||||
// ImageFile reads an image from infile and writes
|
||||
// a thumbnail-size version of it in the same directory.
|
||||
// It returns the generated file name, e.g. "foo.thumb.jpeg".
|
||||
func ImageFile(infile string) (string, error) {
|
||||
ext := filepath.Ext(infile) // e.g., ".jpg", ".JPEG"
|
||||
outfile := strings.TrimSuffix(infile, ext) + ".thumb" + ext
|
||||
return outfile, ImageFile2(outfile, infile)
|
||||
}
|
||||
148
vendor/gopl.io/ch8/thumbnail/thumbnail_test.go
generated
vendored
Normal file
148
vendor/gopl.io/ch8/thumbnail/thumbnail_test.go
generated
vendored
Normal file
@@ -0,0 +1,148 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// This file is just a place to put example code from the book.
|
||||
// It does not actually run any code in gopl.io/ch8/thumbnail.
|
||||
|
||||
package thumbnail_test
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"gopl.io/ch8/thumbnail"
|
||||
)
|
||||
|
||||
//!+1
|
||||
// makeThumbnails makes thumbnails of the specified files.
|
||||
func makeThumbnails(filenames []string) {
|
||||
for _, f := range filenames {
|
||||
if _, err := thumbnail.ImageFile(f); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//!-1
|
||||
|
||||
//!+2
|
||||
// NOTE: incorrect!
|
||||
func makeThumbnails2(filenames []string) {
|
||||
for _, f := range filenames {
|
||||
go thumbnail.ImageFile(f) // NOTE: ignoring errors
|
||||
}
|
||||
}
|
||||
|
||||
//!-2
|
||||
|
||||
//!+3
|
||||
// makeThumbnails3 makes thumbnails of the specified files in parallel.
|
||||
func makeThumbnails3(filenames []string) {
|
||||
ch := make(chan struct{})
|
||||
for _, f := range filenames {
|
||||
go func(f string) {
|
||||
thumbnail.ImageFile(f) // NOTE: ignoring errors
|
||||
ch <- struct{}{}
|
||||
}(f)
|
||||
}
|
||||
|
||||
// Wait for goroutines to complete.
|
||||
for range filenames {
|
||||
<-ch
|
||||
}
|
||||
}
|
||||
|
||||
//!-3
|
||||
|
||||
//!+4
|
||||
// makeThumbnails4 makes thumbnails for the specified files in parallel.
|
||||
// It returns an error if any step failed.
|
||||
func makeThumbnails4(filenames []string) error {
|
||||
errors := make(chan error)
|
||||
|
||||
for _, f := range filenames {
|
||||
go func(f string) {
|
||||
_, err := thumbnail.ImageFile(f)
|
||||
errors <- err
|
||||
}(f)
|
||||
}
|
||||
|
||||
for range filenames {
|
||||
if err := <-errors; err != nil {
|
||||
return err // NOTE: incorrect: goroutine leak!
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
//!-4
|
||||
|
||||
//!+5
|
||||
// makeThumbnails5 makes thumbnails for the specified files in parallel.
|
||||
// It returns the generated file names in an arbitrary order,
|
||||
// or an error if any step failed.
|
||||
func makeThumbnails5(filenames []string) (thumbfiles []string, err error) {
|
||||
type item struct {
|
||||
thumbfile string
|
||||
err error
|
||||
}
|
||||
|
||||
ch := make(chan item, len(filenames))
|
||||
for _, f := range filenames {
|
||||
go func(f string) {
|
||||
var it item
|
||||
it.thumbfile, it.err = thumbnail.ImageFile(f)
|
||||
ch <- it
|
||||
}(f)
|
||||
}
|
||||
|
||||
for range filenames {
|
||||
it := <-ch
|
||||
if it.err != nil {
|
||||
return nil, it.err
|
||||
}
|
||||
thumbfiles = append(thumbfiles, it.thumbfile)
|
||||
}
|
||||
|
||||
return thumbfiles, nil
|
||||
}
|
||||
|
||||
//!-5
|
||||
|
||||
//!+6
|
||||
// makeThumbnails6 makes thumbnails for each file received from the channel.
|
||||
// It returns the number of bytes occupied by the files it creates.
|
||||
func makeThumbnails6(filenames <-chan string) int64 {
|
||||
sizes := make(chan int64)
|
||||
var wg sync.WaitGroup // number of working goroutines
|
||||
for f := range filenames {
|
||||
wg.Add(1)
|
||||
// worker
|
||||
go func(f string) {
|
||||
defer wg.Done()
|
||||
thumb, err := thumbnail.ImageFile(f)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
info, _ := os.Stat(thumb) // OK to ignore error
|
||||
sizes <- info.Size()
|
||||
}(f)
|
||||
}
|
||||
|
||||
// closer
|
||||
go func() {
|
||||
wg.Wait()
|
||||
close(sizes)
|
||||
}()
|
||||
|
||||
var total int64
|
||||
for size := range sizes {
|
||||
total += size
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
//!-6
|
||||
Reference in New Issue
Block a user