mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2025-12-18 19:54:21 +08:00
deploy: 06a1bdf735
This commit is contained in:
28
vendor/github.com/chai2010/image/qrencoder/hello.go
generated
vendored
Normal file
28
vendor/github.com/chai2010/image/qrencoder/hello.go
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
// Copyright 2015 <chaishushan{AT}gmail.com>. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build ingore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
|
||||
qr "github.com/chai2010/image/qrencoder"
|
||||
)
|
||||
|
||||
func main() {
|
||||
c, err := qr.Encode("hello, world", qr.L)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = ioutil.WriteFile("zz_qrout.png", c.PNG(), 0666)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Print("output: zz_qrout.png\n")
|
||||
}
|
||||
149
vendor/github.com/chai2010/image/qrencoder/internal/coding/gen.go
generated
vendored
Normal file
149
vendor/github.com/chai2010/image/qrencoder/internal/coding/gen.go
generated
vendored
Normal file
@@ -0,0 +1,149 @@
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
// tables from qrencode-3.1.1/qrspec.c
|
||||
|
||||
var capacity = [41]struct {
|
||||
width int
|
||||
words int
|
||||
remainder int
|
||||
ec [4]int
|
||||
}{
|
||||
{0, 0, 0, [4]int{0, 0, 0, 0}},
|
||||
{21, 26, 0, [4]int{7, 10, 13, 17}}, // 1
|
||||
{25, 44, 7, [4]int{10, 16, 22, 28}},
|
||||
{29, 70, 7, [4]int{15, 26, 36, 44}},
|
||||
{33, 100, 7, [4]int{20, 36, 52, 64}},
|
||||
{37, 134, 7, [4]int{26, 48, 72, 88}}, // 5
|
||||
{41, 172, 7, [4]int{36, 64, 96, 112}},
|
||||
{45, 196, 0, [4]int{40, 72, 108, 130}},
|
||||
{49, 242, 0, [4]int{48, 88, 132, 156}},
|
||||
{53, 292, 0, [4]int{60, 110, 160, 192}},
|
||||
{57, 346, 0, [4]int{72, 130, 192, 224}}, //10
|
||||
{61, 404, 0, [4]int{80, 150, 224, 264}},
|
||||
{65, 466, 0, [4]int{96, 176, 260, 308}},
|
||||
{69, 532, 0, [4]int{104, 198, 288, 352}},
|
||||
{73, 581, 3, [4]int{120, 216, 320, 384}},
|
||||
{77, 655, 3, [4]int{132, 240, 360, 432}}, //15
|
||||
{81, 733, 3, [4]int{144, 280, 408, 480}},
|
||||
{85, 815, 3, [4]int{168, 308, 448, 532}},
|
||||
{89, 901, 3, [4]int{180, 338, 504, 588}},
|
||||
{93, 991, 3, [4]int{196, 364, 546, 650}},
|
||||
{97, 1085, 3, [4]int{224, 416, 600, 700}}, //20
|
||||
{101, 1156, 4, [4]int{224, 442, 644, 750}},
|
||||
{105, 1258, 4, [4]int{252, 476, 690, 816}},
|
||||
{109, 1364, 4, [4]int{270, 504, 750, 900}},
|
||||
{113, 1474, 4, [4]int{300, 560, 810, 960}},
|
||||
{117, 1588, 4, [4]int{312, 588, 870, 1050}}, //25
|
||||
{121, 1706, 4, [4]int{336, 644, 952, 1110}},
|
||||
{125, 1828, 4, [4]int{360, 700, 1020, 1200}},
|
||||
{129, 1921, 3, [4]int{390, 728, 1050, 1260}},
|
||||
{133, 2051, 3, [4]int{420, 784, 1140, 1350}},
|
||||
{137, 2185, 3, [4]int{450, 812, 1200, 1440}}, //30
|
||||
{141, 2323, 3, [4]int{480, 868, 1290, 1530}},
|
||||
{145, 2465, 3, [4]int{510, 924, 1350, 1620}},
|
||||
{149, 2611, 3, [4]int{540, 980, 1440, 1710}},
|
||||
{153, 2761, 3, [4]int{570, 1036, 1530, 1800}},
|
||||
{157, 2876, 0, [4]int{570, 1064, 1590, 1890}}, //35
|
||||
{161, 3034, 0, [4]int{600, 1120, 1680, 1980}},
|
||||
{165, 3196, 0, [4]int{630, 1204, 1770, 2100}},
|
||||
{169, 3362, 0, [4]int{660, 1260, 1860, 2220}},
|
||||
{173, 3532, 0, [4]int{720, 1316, 1950, 2310}},
|
||||
{177, 3706, 0, [4]int{750, 1372, 2040, 2430}}, //40
|
||||
}
|
||||
|
||||
var eccTable = [41][4][2]int{
|
||||
{{0, 0}, {0, 0}, {0, 0}, {0, 0}},
|
||||
{{1, 0}, {1, 0}, {1, 0}, {1, 0}}, // 1
|
||||
{{1, 0}, {1, 0}, {1, 0}, {1, 0}},
|
||||
{{1, 0}, {1, 0}, {2, 0}, {2, 0}},
|
||||
{{1, 0}, {2, 0}, {2, 0}, {4, 0}},
|
||||
{{1, 0}, {2, 0}, {2, 2}, {2, 2}}, // 5
|
||||
{{2, 0}, {4, 0}, {4, 0}, {4, 0}},
|
||||
{{2, 0}, {4, 0}, {2, 4}, {4, 1}},
|
||||
{{2, 0}, {2, 2}, {4, 2}, {4, 2}},
|
||||
{{2, 0}, {3, 2}, {4, 4}, {4, 4}},
|
||||
{{2, 2}, {4, 1}, {6, 2}, {6, 2}}, //10
|
||||
{{4, 0}, {1, 4}, {4, 4}, {3, 8}},
|
||||
{{2, 2}, {6, 2}, {4, 6}, {7, 4}},
|
||||
{{4, 0}, {8, 1}, {8, 4}, {12, 4}},
|
||||
{{3, 1}, {4, 5}, {11, 5}, {11, 5}},
|
||||
{{5, 1}, {5, 5}, {5, 7}, {11, 7}}, //15
|
||||
{{5, 1}, {7, 3}, {15, 2}, {3, 13}},
|
||||
{{1, 5}, {10, 1}, {1, 15}, {2, 17}},
|
||||
{{5, 1}, {9, 4}, {17, 1}, {2, 19}},
|
||||
{{3, 4}, {3, 11}, {17, 4}, {9, 16}},
|
||||
{{3, 5}, {3, 13}, {15, 5}, {15, 10}}, //20
|
||||
{{4, 4}, {17, 0}, {17, 6}, {19, 6}},
|
||||
{{2, 7}, {17, 0}, {7, 16}, {34, 0}},
|
||||
{{4, 5}, {4, 14}, {11, 14}, {16, 14}},
|
||||
{{6, 4}, {6, 14}, {11, 16}, {30, 2}},
|
||||
{{8, 4}, {8, 13}, {7, 22}, {22, 13}}, //25
|
||||
{{10, 2}, {19, 4}, {28, 6}, {33, 4}},
|
||||
{{8, 4}, {22, 3}, {8, 26}, {12, 28}},
|
||||
{{3, 10}, {3, 23}, {4, 31}, {11, 31}},
|
||||
{{7, 7}, {21, 7}, {1, 37}, {19, 26}},
|
||||
{{5, 10}, {19, 10}, {15, 25}, {23, 25}}, //30
|
||||
{{13, 3}, {2, 29}, {42, 1}, {23, 28}},
|
||||
{{17, 0}, {10, 23}, {10, 35}, {19, 35}},
|
||||
{{17, 1}, {14, 21}, {29, 19}, {11, 46}},
|
||||
{{13, 6}, {14, 23}, {44, 7}, {59, 1}},
|
||||
{{12, 7}, {12, 26}, {39, 14}, {22, 41}}, //35
|
||||
{{6, 14}, {6, 34}, {46, 10}, {2, 64}},
|
||||
{{17, 4}, {29, 14}, {49, 10}, {24, 46}},
|
||||
{{4, 18}, {13, 32}, {48, 14}, {42, 32}},
|
||||
{{20, 4}, {40, 7}, {43, 22}, {10, 67}},
|
||||
{{19, 6}, {18, 31}, {34, 34}, {20, 61}}, //40
|
||||
}
|
||||
|
||||
var align = [41][2]int{
|
||||
{0, 0},
|
||||
{0, 0}, {18, 0}, {22, 0}, {26, 0}, {30, 0}, // 1- 5
|
||||
{34, 0}, {22, 38}, {24, 42}, {26, 46}, {28, 50}, // 6-10
|
||||
{30, 54}, {32, 58}, {34, 62}, {26, 46}, {26, 48}, //11-15
|
||||
{26, 50}, {30, 54}, {30, 56}, {30, 58}, {34, 62}, //16-20
|
||||
{28, 50}, {26, 50}, {30, 54}, {28, 54}, {32, 58}, //21-25
|
||||
{30, 58}, {34, 62}, {26, 50}, {30, 54}, {26, 52}, //26-30
|
||||
{30, 56}, {34, 60}, {30, 58}, {34, 62}, {30, 54}, //31-35
|
||||
{24, 50}, {28, 54}, {32, 58}, {26, 54}, {30, 58}, //35-40
|
||||
}
|
||||
|
||||
var versionPattern = [41]int{
|
||||
0,
|
||||
0, 0, 0, 0, 0, 0,
|
||||
0x07c94, 0x085bc, 0x09a99, 0x0a4d3, 0x0bbf6, 0x0c762, 0x0d847, 0x0e60d,
|
||||
0x0f928, 0x10b78, 0x1145d, 0x12a17, 0x13532, 0x149a6, 0x15683, 0x168c9,
|
||||
0x177ec, 0x18ec4, 0x191e1, 0x1afab, 0x1b08e, 0x1cc1a, 0x1d33f, 0x1ed75,
|
||||
0x1f250, 0x209d5, 0x216f0, 0x228ba, 0x2379f, 0x24b0b, 0x2542e, 0x26a64,
|
||||
0x27541, 0x28c69,
|
||||
}
|
||||
|
||||
func main() {
|
||||
fmt.Printf("\t{},\n")
|
||||
for i := 1; i <= 40; i++ {
|
||||
apos := align[i][0] - 2
|
||||
if apos < 0 {
|
||||
apos = 100
|
||||
}
|
||||
astride := align[i][1] - align[i][0]
|
||||
if astride < 1 {
|
||||
astride = 100
|
||||
}
|
||||
fmt.Printf("\t{%v, %v, %v, %#x, [4]level{{%v, %v}, {%v, %v}, {%v, %v}, {%v, %v}}}, // %v\n",
|
||||
apos, astride, capacity[i].words,
|
||||
versionPattern[i],
|
||||
eccTable[i][0][0]+eccTable[i][0][1],
|
||||
float64(capacity[i].ec[0])/float64(eccTable[i][0][0]+eccTable[i][0][1]),
|
||||
eccTable[i][1][0]+eccTable[i][1][1],
|
||||
float64(capacity[i].ec[1])/float64(eccTable[i][1][0]+eccTable[i][1][1]),
|
||||
eccTable[i][2][0]+eccTable[i][2][1],
|
||||
float64(capacity[i].ec[2])/float64(eccTable[i][2][0]+eccTable[i][2][1]),
|
||||
eccTable[i][3][0]+eccTable[i][3][1],
|
||||
float64(capacity[i].ec[3])/float64(eccTable[i][3][0]+eccTable[i][3][1]),
|
||||
i,
|
||||
)
|
||||
}
|
||||
}
|
||||
815
vendor/github.com/chai2010/image/qrencoder/internal/coding/qr.go
generated
vendored
Normal file
815
vendor/github.com/chai2010/image/qrencoder/internal/coding/qr.go
generated
vendored
Normal file
@@ -0,0 +1,815 @@
|
||||
// Copyright 2011 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 coding implements low-level QR coding details.
|
||||
package coding
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/chai2010/image/qrencoder/internal/gf256"
|
||||
)
|
||||
|
||||
// Field is the field for QR error correction.
|
||||
var Field = gf256.NewField(0x11d, 2)
|
||||
|
||||
// A Version represents a QR version.
|
||||
// The version specifies the size of the QR code:
|
||||
// a QR code with version v has 4v+17 pixels on a side.
|
||||
// Versions number from 1 to 40: the larger the version,
|
||||
// the more information the code can store.
|
||||
type Version int
|
||||
|
||||
const MinVersion = 1
|
||||
const MaxVersion = 40
|
||||
|
||||
func (v Version) String() string {
|
||||
return strconv.Itoa(int(v))
|
||||
}
|
||||
|
||||
func (v Version) sizeClass() int {
|
||||
if v <= 9 {
|
||||
return 0
|
||||
}
|
||||
if v <= 26 {
|
||||
return 1
|
||||
}
|
||||
return 2
|
||||
}
|
||||
|
||||
// DataBytes returns the number of data bytes that can be
|
||||
// stored in a QR code with the given version and level.
|
||||
func (v Version) DataBytes(l Level) int {
|
||||
vt := &vtab[v]
|
||||
lev := &vt.level[l]
|
||||
return vt.bytes - lev.nblock*lev.check
|
||||
}
|
||||
|
||||
// Encoding implements a QR data encoding scheme.
|
||||
// The implementations--Numeric, Alphanumeric, and String--specify
|
||||
// the character set and the mapping from UTF-8 to code bits.
|
||||
// The more restrictive the mode, the fewer code bits are needed.
|
||||
type Encoding interface {
|
||||
Check() error
|
||||
Bits(v Version) int
|
||||
Encode(b *Bits, v Version)
|
||||
}
|
||||
|
||||
type Bits struct {
|
||||
b []byte
|
||||
nbit int
|
||||
}
|
||||
|
||||
func (b *Bits) Reset() {
|
||||
b.b = b.b[:0]
|
||||
b.nbit = 0
|
||||
}
|
||||
|
||||
func (b *Bits) Bits() int {
|
||||
return b.nbit
|
||||
}
|
||||
|
||||
func (b *Bits) Bytes() []byte {
|
||||
if b.nbit%8 != 0 {
|
||||
panic("fractional byte")
|
||||
}
|
||||
return b.b
|
||||
}
|
||||
|
||||
func (b *Bits) Append(p []byte) {
|
||||
if b.nbit%8 != 0 {
|
||||
panic("fractional byte")
|
||||
}
|
||||
b.b = append(b.b, p...)
|
||||
b.nbit += 8 * len(p)
|
||||
}
|
||||
|
||||
func (b *Bits) Write(v uint, nbit int) {
|
||||
for nbit > 0 {
|
||||
n := nbit
|
||||
if n > 8 {
|
||||
n = 8
|
||||
}
|
||||
if b.nbit%8 == 0 {
|
||||
b.b = append(b.b, 0)
|
||||
} else {
|
||||
m := -b.nbit & 7
|
||||
if n > m {
|
||||
n = m
|
||||
}
|
||||
}
|
||||
b.nbit += n
|
||||
sh := uint(nbit - n)
|
||||
b.b[len(b.b)-1] |= uint8(v >> sh << uint(-b.nbit&7))
|
||||
v -= v >> sh << sh
|
||||
nbit -= n
|
||||
}
|
||||
}
|
||||
|
||||
// Num is the encoding for numeric data.
|
||||
// The only valid characters are the decimal digits 0 through 9.
|
||||
type Num string
|
||||
|
||||
func (s Num) String() string {
|
||||
return fmt.Sprintf("Num(%#q)", string(s))
|
||||
}
|
||||
|
||||
func (s Num) Check() error {
|
||||
for _, c := range s {
|
||||
if c < '0' || '9' < c {
|
||||
return fmt.Errorf("non-numeric string %#q", string(s))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var numLen = [3]int{10, 12, 14}
|
||||
|
||||
func (s Num) Bits(v Version) int {
|
||||
return 4 + numLen[v.sizeClass()] + (10*len(s)+2)/3
|
||||
}
|
||||
|
||||
func (s Num) Encode(b *Bits, v Version) {
|
||||
b.Write(1, 4)
|
||||
b.Write(uint(len(s)), numLen[v.sizeClass()])
|
||||
var i int
|
||||
for i = 0; i+3 <= len(s); i += 3 {
|
||||
w := uint(s[i]-'0')*100 + uint(s[i+1]-'0')*10 + uint(s[i+2]-'0')
|
||||
b.Write(w, 10)
|
||||
}
|
||||
switch len(s) - i {
|
||||
case 1:
|
||||
w := uint(s[i] - '0')
|
||||
b.Write(w, 4)
|
||||
case 2:
|
||||
w := uint(s[i]-'0')*10 + uint(s[i+1]-'0')
|
||||
b.Write(w, 7)
|
||||
}
|
||||
}
|
||||
|
||||
// Alpha is the encoding for alphanumeric data.
|
||||
// The valid characters are 0-9A-Z$%*+-./: and space.
|
||||
type Alpha string
|
||||
|
||||
const alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"
|
||||
|
||||
func (s Alpha) String() string {
|
||||
return fmt.Sprintf("Alpha(%#q)", string(s))
|
||||
}
|
||||
|
||||
func (s Alpha) Check() error {
|
||||
for _, c := range s {
|
||||
if strings.IndexRune(alphabet, c) < 0 {
|
||||
return fmt.Errorf("non-alphanumeric string %#q", string(s))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var alphaLen = [3]int{9, 11, 13}
|
||||
|
||||
func (s Alpha) Bits(v Version) int {
|
||||
return 4 + alphaLen[v.sizeClass()] + (11*len(s)+1)/2
|
||||
}
|
||||
|
||||
func (s Alpha) Encode(b *Bits, v Version) {
|
||||
b.Write(2, 4)
|
||||
b.Write(uint(len(s)), alphaLen[v.sizeClass()])
|
||||
var i int
|
||||
for i = 0; i+2 <= len(s); i += 2 {
|
||||
w := uint(strings.IndexRune(alphabet, rune(s[i])))*45 +
|
||||
uint(strings.IndexRune(alphabet, rune(s[i+1])))
|
||||
b.Write(w, 11)
|
||||
}
|
||||
|
||||
if i < len(s) {
|
||||
w := uint(strings.IndexRune(alphabet, rune(s[i])))
|
||||
b.Write(w, 6)
|
||||
}
|
||||
}
|
||||
|
||||
// String is the encoding for 8-bit data. All bytes are valid.
|
||||
type String string
|
||||
|
||||
func (s String) String() string {
|
||||
return fmt.Sprintf("String(%#q)", string(s))
|
||||
}
|
||||
|
||||
func (s String) Check() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var stringLen = [3]int{8, 16, 16}
|
||||
|
||||
func (s String) Bits(v Version) int {
|
||||
return 4 + stringLen[v.sizeClass()] + 8*len(s)
|
||||
}
|
||||
|
||||
func (s String) Encode(b *Bits, v Version) {
|
||||
b.Write(4, 4)
|
||||
b.Write(uint(len(s)), stringLen[v.sizeClass()])
|
||||
for i := 0; i < len(s); i++ {
|
||||
b.Write(uint(s[i]), 8)
|
||||
}
|
||||
}
|
||||
|
||||
// A Pixel describes a single pixel in a QR code.
|
||||
type Pixel uint32
|
||||
|
||||
const (
|
||||
Black Pixel = 1 << iota
|
||||
Invert
|
||||
)
|
||||
|
||||
func (p Pixel) Offset() uint {
|
||||
return uint(p >> 6)
|
||||
}
|
||||
|
||||
func OffsetPixel(o uint) Pixel {
|
||||
return Pixel(o << 6)
|
||||
}
|
||||
|
||||
func (r PixelRole) Pixel() Pixel {
|
||||
return Pixel(r << 2)
|
||||
}
|
||||
|
||||
func (p Pixel) Role() PixelRole {
|
||||
return PixelRole(p>>2) & 15
|
||||
}
|
||||
|
||||
func (p Pixel) String() string {
|
||||
s := p.Role().String()
|
||||
if p&Black != 0 {
|
||||
s += "+black"
|
||||
}
|
||||
if p&Invert != 0 {
|
||||
s += "+invert"
|
||||
}
|
||||
s += "+" + strconv.FormatUint(uint64(p.Offset()), 10)
|
||||
return s
|
||||
}
|
||||
|
||||
// A PixelRole describes the role of a QR pixel.
|
||||
type PixelRole uint32
|
||||
|
||||
const (
|
||||
_ PixelRole = iota
|
||||
Position // position squares (large)
|
||||
Alignment // alignment squares (small)
|
||||
Timing // timing strip between position squares
|
||||
Format // format metadata
|
||||
PVersion // version pattern
|
||||
Unused // unused pixel
|
||||
Data // data bit
|
||||
Check // error correction check bit
|
||||
Extra
|
||||
)
|
||||
|
||||
var roles = []string{
|
||||
"",
|
||||
"position",
|
||||
"alignment",
|
||||
"timing",
|
||||
"format",
|
||||
"pversion",
|
||||
"unused",
|
||||
"data",
|
||||
"check",
|
||||
"extra",
|
||||
}
|
||||
|
||||
func (r PixelRole) String() string {
|
||||
if Position <= r && r <= Check {
|
||||
return roles[r]
|
||||
}
|
||||
return strconv.Itoa(int(r))
|
||||
}
|
||||
|
||||
// A Level represents a QR error correction level.
|
||||
// From least to most tolerant of errors, they are L, M, Q, H.
|
||||
type Level int
|
||||
|
||||
const (
|
||||
L Level = iota
|
||||
M
|
||||
Q
|
||||
H
|
||||
)
|
||||
|
||||
func (l Level) String() string {
|
||||
if L <= l && l <= H {
|
||||
return "LMQH"[l : l+1]
|
||||
}
|
||||
return strconv.Itoa(int(l))
|
||||
}
|
||||
|
||||
// A Code is a square pixel grid.
|
||||
type Code struct {
|
||||
Bitmap []byte // 1 is black, 0 is white
|
||||
Size int // number of pixels on a side
|
||||
Stride int // number of bytes per row
|
||||
}
|
||||
|
||||
func (c *Code) Black(x, y int) bool {
|
||||
return 0 <= x && x < c.Size && 0 <= y && y < c.Size &&
|
||||
c.Bitmap[y*c.Stride+x/8]&(1<<uint(7-x&7)) != 0
|
||||
}
|
||||
|
||||
// A Mask describes a mask that is applied to the QR
|
||||
// code to avoid QR artifacts being interpreted as
|
||||
// alignment and timing patterns (such as the squares
|
||||
// in the corners). Valid masks are integers from 0 to 7.
|
||||
type Mask int
|
||||
|
||||
// http://www.swetake.com/qr/qr5_en.html
|
||||
var mfunc = []func(int, int) bool{
|
||||
func(i, j int) bool { return (i+j)%2 == 0 },
|
||||
func(i, j int) bool { return i%2 == 0 },
|
||||
func(i, j int) bool { return j%3 == 0 },
|
||||
func(i, j int) bool { return (i+j)%3 == 0 },
|
||||
func(i, j int) bool { return (i/2+j/3)%2 == 0 },
|
||||
func(i, j int) bool { return i*j%2+i*j%3 == 0 },
|
||||
func(i, j int) bool { return (i*j%2+i*j%3)%2 == 0 },
|
||||
func(i, j int) bool { return (i*j%3+(i+j)%2)%2 == 0 },
|
||||
}
|
||||
|
||||
func (m Mask) Invert(y, x int) bool {
|
||||
if m < 0 {
|
||||
return false
|
||||
}
|
||||
return mfunc[m](y, x)
|
||||
}
|
||||
|
||||
// A Plan describes how to construct a QR code
|
||||
// with a specific version, level, and mask.
|
||||
type Plan struct {
|
||||
Version Version
|
||||
Level Level
|
||||
Mask Mask
|
||||
|
||||
DataBytes int // number of data bytes
|
||||
CheckBytes int // number of error correcting (checksum) bytes
|
||||
Blocks int // number of data blocks
|
||||
|
||||
Pixel [][]Pixel // pixel map
|
||||
}
|
||||
|
||||
// NewPlan returns a Plan for a QR code with the given
|
||||
// version, level, and mask.
|
||||
func NewPlan(version Version, level Level, mask Mask) (*Plan, error) {
|
||||
p, err := vplan(version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := fplan(level, mask, p); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := lplan(version, level, p); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := mplan(mask, p); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func (b *Bits) Pad(n int) {
|
||||
if n < 0 {
|
||||
panic("qr: invalid pad size")
|
||||
}
|
||||
if n <= 4 {
|
||||
b.Write(0, n)
|
||||
} else {
|
||||
b.Write(0, 4)
|
||||
n -= 4
|
||||
n -= -b.Bits() & 7
|
||||
b.Write(0, -b.Bits()&7)
|
||||
pad := n / 8
|
||||
for i := 0; i < pad; i += 2 {
|
||||
b.Write(0xec, 8)
|
||||
if i+1 >= pad {
|
||||
break
|
||||
}
|
||||
b.Write(0x11, 8)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Bits) AddCheckBytes(v Version, l Level) {
|
||||
nd := v.DataBytes(l)
|
||||
if b.nbit < nd*8 {
|
||||
b.Pad(nd*8 - b.nbit)
|
||||
}
|
||||
if b.nbit != nd*8 {
|
||||
panic("qr: too much data")
|
||||
}
|
||||
|
||||
dat := b.Bytes()
|
||||
vt := &vtab[v]
|
||||
lev := &vt.level[l]
|
||||
db := nd / lev.nblock
|
||||
extra := nd % lev.nblock
|
||||
chk := make([]byte, lev.check)
|
||||
rs := gf256.NewRSEncoder(Field, lev.check)
|
||||
for i := 0; i < lev.nblock; i++ {
|
||||
if i == lev.nblock-extra {
|
||||
db++
|
||||
}
|
||||
rs.ECC(dat[:db], chk)
|
||||
b.Append(chk)
|
||||
dat = dat[db:]
|
||||
}
|
||||
|
||||
if len(b.Bytes()) != vt.bytes {
|
||||
panic("qr: internal error")
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Plan) Encode(text ...Encoding) (*Code, error) {
|
||||
var b Bits
|
||||
for _, t := range text {
|
||||
if err := t.Check(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t.Encode(&b, p.Version)
|
||||
}
|
||||
if b.Bits() > p.DataBytes*8 {
|
||||
return nil, fmt.Errorf("cannot encode %d bits into %d-bit code", b.Bits(), p.DataBytes*8)
|
||||
}
|
||||
b.AddCheckBytes(p.Version, p.Level)
|
||||
bytes := b.Bytes()
|
||||
|
||||
// Now we have the checksum bytes and the data bytes.
|
||||
// Construct the actual code.
|
||||
c := &Code{Size: len(p.Pixel), Stride: (len(p.Pixel) + 7) &^ 7}
|
||||
c.Bitmap = make([]byte, c.Stride*c.Size)
|
||||
crow := c.Bitmap
|
||||
for _, row := range p.Pixel {
|
||||
for x, pix := range row {
|
||||
switch pix.Role() {
|
||||
case Data, Check:
|
||||
o := pix.Offset()
|
||||
if bytes[o/8]&(1<<uint(7-o&7)) != 0 {
|
||||
pix ^= Black
|
||||
}
|
||||
}
|
||||
if pix&Black != 0 {
|
||||
crow[x/8] |= 1 << uint(7-x&7)
|
||||
}
|
||||
}
|
||||
crow = crow[c.Stride:]
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// A version describes metadata associated with a version.
|
||||
type version struct {
|
||||
apos int
|
||||
astride int
|
||||
bytes int
|
||||
pattern int
|
||||
level [4]level
|
||||
}
|
||||
|
||||
type level struct {
|
||||
nblock int
|
||||
check int
|
||||
}
|
||||
|
||||
var vtab = []version{
|
||||
{},
|
||||
{100, 100, 26, 0x0, [4]level{{1, 7}, {1, 10}, {1, 13}, {1, 17}}}, // 1
|
||||
{16, 100, 44, 0x0, [4]level{{1, 10}, {1, 16}, {1, 22}, {1, 28}}}, // 2
|
||||
{20, 100, 70, 0x0, [4]level{{1, 15}, {1, 26}, {2, 18}, {2, 22}}}, // 3
|
||||
{24, 100, 100, 0x0, [4]level{{1, 20}, {2, 18}, {2, 26}, {4, 16}}}, // 4
|
||||
{28, 100, 134, 0x0, [4]level{{1, 26}, {2, 24}, {4, 18}, {4, 22}}}, // 5
|
||||
{32, 100, 172, 0x0, [4]level{{2, 18}, {4, 16}, {4, 24}, {4, 28}}}, // 6
|
||||
{20, 16, 196, 0x7c94, [4]level{{2, 20}, {4, 18}, {6, 18}, {5, 26}}}, // 7
|
||||
{22, 18, 242, 0x85bc, [4]level{{2, 24}, {4, 22}, {6, 22}, {6, 26}}}, // 8
|
||||
{24, 20, 292, 0x9a99, [4]level{{2, 30}, {5, 22}, {8, 20}, {8, 24}}}, // 9
|
||||
{26, 22, 346, 0xa4d3, [4]level{{4, 18}, {5, 26}, {8, 24}, {8, 28}}}, // 10
|
||||
{28, 24, 404, 0xbbf6, [4]level{{4, 20}, {5, 30}, {8, 28}, {11, 24}}}, // 11
|
||||
{30, 26, 466, 0xc762, [4]level{{4, 24}, {8, 22}, {10, 26}, {11, 28}}}, // 12
|
||||
{32, 28, 532, 0xd847, [4]level{{4, 26}, {9, 22}, {12, 24}, {16, 22}}}, // 13
|
||||
{24, 20, 581, 0xe60d, [4]level{{4, 30}, {9, 24}, {16, 20}, {16, 24}}}, // 14
|
||||
{24, 22, 655, 0xf928, [4]level{{6, 22}, {10, 24}, {12, 30}, {18, 24}}}, // 15
|
||||
{24, 24, 733, 0x10b78, [4]level{{6, 24}, {10, 28}, {17, 24}, {16, 30}}}, // 16
|
||||
{28, 24, 815, 0x1145d, [4]level{{6, 28}, {11, 28}, {16, 28}, {19, 28}}}, // 17
|
||||
{28, 26, 901, 0x12a17, [4]level{{6, 30}, {13, 26}, {18, 28}, {21, 28}}}, // 18
|
||||
{28, 28, 991, 0x13532, [4]level{{7, 28}, {14, 26}, {21, 26}, {25, 26}}}, // 19
|
||||
{32, 28, 1085, 0x149a6, [4]level{{8, 28}, {16, 26}, {20, 30}, {25, 28}}}, // 20
|
||||
{26, 22, 1156, 0x15683, [4]level{{8, 28}, {17, 26}, {23, 28}, {25, 30}}}, // 21
|
||||
{24, 24, 1258, 0x168c9, [4]level{{9, 28}, {17, 28}, {23, 30}, {34, 24}}}, // 22
|
||||
{28, 24, 1364, 0x177ec, [4]level{{9, 30}, {18, 28}, {25, 30}, {30, 30}}}, // 23
|
||||
{26, 26, 1474, 0x18ec4, [4]level{{10, 30}, {20, 28}, {27, 30}, {32, 30}}}, // 24
|
||||
{30, 26, 1588, 0x191e1, [4]level{{12, 26}, {21, 28}, {29, 30}, {35, 30}}}, // 25
|
||||
{28, 28, 1706, 0x1afab, [4]level{{12, 28}, {23, 28}, {34, 28}, {37, 30}}}, // 26
|
||||
{32, 28, 1828, 0x1b08e, [4]level{{12, 30}, {25, 28}, {34, 30}, {40, 30}}}, // 27
|
||||
{24, 24, 1921, 0x1cc1a, [4]level{{13, 30}, {26, 28}, {35, 30}, {42, 30}}}, // 28
|
||||
{28, 24, 2051, 0x1d33f, [4]level{{14, 30}, {28, 28}, {38, 30}, {45, 30}}}, // 29
|
||||
{24, 26, 2185, 0x1ed75, [4]level{{15, 30}, {29, 28}, {40, 30}, {48, 30}}}, // 30
|
||||
{28, 26, 2323, 0x1f250, [4]level{{16, 30}, {31, 28}, {43, 30}, {51, 30}}}, // 31
|
||||
{32, 26, 2465, 0x209d5, [4]level{{17, 30}, {33, 28}, {45, 30}, {54, 30}}}, // 32
|
||||
{28, 28, 2611, 0x216f0, [4]level{{18, 30}, {35, 28}, {48, 30}, {57, 30}}}, // 33
|
||||
{32, 28, 2761, 0x228ba, [4]level{{19, 30}, {37, 28}, {51, 30}, {60, 30}}}, // 34
|
||||
{28, 24, 2876, 0x2379f, [4]level{{19, 30}, {38, 28}, {53, 30}, {63, 30}}}, // 35
|
||||
{22, 26, 3034, 0x24b0b, [4]level{{20, 30}, {40, 28}, {56, 30}, {66, 30}}}, // 36
|
||||
{26, 26, 3196, 0x2542e, [4]level{{21, 30}, {43, 28}, {59, 30}, {70, 30}}}, // 37
|
||||
{30, 26, 3362, 0x26a64, [4]level{{22, 30}, {45, 28}, {62, 30}, {74, 30}}}, // 38
|
||||
{24, 28, 3532, 0x27541, [4]level{{24, 30}, {47, 28}, {65, 30}, {77, 30}}}, // 39
|
||||
{28, 28, 3706, 0x28c69, [4]level{{25, 30}, {49, 28}, {68, 30}, {81, 30}}}, // 40
|
||||
}
|
||||
|
||||
func grid(siz int) [][]Pixel {
|
||||
m := make([][]Pixel, siz)
|
||||
pix := make([]Pixel, siz*siz)
|
||||
for i := range m {
|
||||
m[i], pix = pix[:siz], pix[siz:]
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// vplan creates a Plan for the given version.
|
||||
func vplan(v Version) (*Plan, error) {
|
||||
p := &Plan{Version: v}
|
||||
if v < 1 || v > 40 {
|
||||
return nil, fmt.Errorf("invalid QR version %d", int(v))
|
||||
}
|
||||
siz := 17 + int(v)*4
|
||||
m := grid(siz)
|
||||
p.Pixel = m
|
||||
|
||||
// Timing markers (overwritten by boxes).
|
||||
const ti = 6 // timing is in row/column 6 (counting from 0)
|
||||
for i := range m {
|
||||
p := Timing.Pixel()
|
||||
if i&1 == 0 {
|
||||
p |= Black
|
||||
}
|
||||
m[i][ti] = p
|
||||
m[ti][i] = p
|
||||
}
|
||||
|
||||
// Position boxes.
|
||||
posBox(m, 0, 0)
|
||||
posBox(m, siz-7, 0)
|
||||
posBox(m, 0, siz-7)
|
||||
|
||||
// Alignment boxes.
|
||||
info := &vtab[v]
|
||||
for x := 4; x+5 < siz; {
|
||||
for y := 4; y+5 < siz; {
|
||||
// don't overwrite timing markers
|
||||
if (x < 7 && y < 7) || (x < 7 && y+5 >= siz-7) || (x+5 >= siz-7 && y < 7) {
|
||||
} else {
|
||||
alignBox(m, x, y)
|
||||
}
|
||||
if y == 4 {
|
||||
y = info.apos
|
||||
} else {
|
||||
y += info.astride
|
||||
}
|
||||
}
|
||||
if x == 4 {
|
||||
x = info.apos
|
||||
} else {
|
||||
x += info.astride
|
||||
}
|
||||
}
|
||||
|
||||
// Version pattern.
|
||||
pat := vtab[v].pattern
|
||||
if pat != 0 {
|
||||
v := pat
|
||||
for x := 0; x < 6; x++ {
|
||||
for y := 0; y < 3; y++ {
|
||||
p := PVersion.Pixel()
|
||||
if v&1 != 0 {
|
||||
p |= Black
|
||||
}
|
||||
m[siz-11+y][x] = p
|
||||
m[x][siz-11+y] = p
|
||||
v >>= 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// One lonely black pixel
|
||||
m[siz-8][8] = Unused.Pixel() | Black
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// fplan adds the format pixels
|
||||
func fplan(l Level, m Mask, p *Plan) error {
|
||||
// Format pixels.
|
||||
fb := uint32(l^1) << 13 // level: L=01, M=00, Q=11, H=10
|
||||
fb |= uint32(m) << 10 // mask
|
||||
const formatPoly = 0x537
|
||||
rem := fb
|
||||
for i := 14; i >= 10; i-- {
|
||||
if rem&(1<<uint(i)) != 0 {
|
||||
rem ^= formatPoly << uint(i-10)
|
||||
}
|
||||
}
|
||||
fb |= rem
|
||||
invert := uint32(0x5412)
|
||||
siz := len(p.Pixel)
|
||||
for i := uint(0); i < 15; i++ {
|
||||
pix := Format.Pixel() + OffsetPixel(i)
|
||||
if (fb>>i)&1 == 1 {
|
||||
pix |= Black
|
||||
}
|
||||
if (invert>>i)&1 == 1 {
|
||||
pix ^= Invert | Black
|
||||
}
|
||||
// top left
|
||||
switch {
|
||||
case i < 6:
|
||||
p.Pixel[i][8] = pix
|
||||
case i < 8:
|
||||
p.Pixel[i+1][8] = pix
|
||||
case i < 9:
|
||||
p.Pixel[8][7] = pix
|
||||
default:
|
||||
p.Pixel[8][14-i] = pix
|
||||
}
|
||||
// bottom right
|
||||
switch {
|
||||
case i < 8:
|
||||
p.Pixel[8][siz-1-int(i)] = pix
|
||||
default:
|
||||
p.Pixel[siz-1-int(14-i)][8] = pix
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// lplan edits a version-only Plan to add information
|
||||
// about the error correction levels.
|
||||
func lplan(v Version, l Level, p *Plan) error {
|
||||
p.Level = l
|
||||
|
||||
nblock := vtab[v].level[l].nblock
|
||||
ne := vtab[v].level[l].check
|
||||
nde := (vtab[v].bytes - ne*nblock) / nblock
|
||||
extra := (vtab[v].bytes - ne*nblock) % nblock
|
||||
dataBits := (nde*nblock + extra) * 8
|
||||
checkBits := ne * nblock * 8
|
||||
|
||||
p.DataBytes = vtab[v].bytes - ne*nblock
|
||||
p.CheckBytes = ne * nblock
|
||||
p.Blocks = nblock
|
||||
|
||||
// Make data + checksum pixels.
|
||||
data := make([]Pixel, dataBits)
|
||||
for i := range data {
|
||||
data[i] = Data.Pixel() | OffsetPixel(uint(i))
|
||||
}
|
||||
check := make([]Pixel, checkBits)
|
||||
for i := range check {
|
||||
check[i] = Check.Pixel() | OffsetPixel(uint(i+dataBits))
|
||||
}
|
||||
|
||||
// Split into blocks.
|
||||
dataList := make([][]Pixel, nblock)
|
||||
checkList := make([][]Pixel, nblock)
|
||||
for i := 0; i < nblock; i++ {
|
||||
// The last few blocks have an extra data byte (8 pixels).
|
||||
nd := nde
|
||||
if i >= nblock-extra {
|
||||
nd++
|
||||
}
|
||||
dataList[i], data = data[0:nd*8], data[nd*8:]
|
||||
checkList[i], check = check[0:ne*8], check[ne*8:]
|
||||
}
|
||||
if len(data) != 0 || len(check) != 0 {
|
||||
panic("data/check math")
|
||||
}
|
||||
|
||||
// Build up bit sequence, taking first byte of each block,
|
||||
// then second byte, and so on. Then checksums.
|
||||
bits := make([]Pixel, dataBits+checkBits)
|
||||
dst := bits
|
||||
for i := 0; i < nde+1; i++ {
|
||||
for _, b := range dataList {
|
||||
if i*8 < len(b) {
|
||||
copy(dst, b[i*8:(i+1)*8])
|
||||
dst = dst[8:]
|
||||
}
|
||||
}
|
||||
}
|
||||
for i := 0; i < ne; i++ {
|
||||
for _, b := range checkList {
|
||||
if i*8 < len(b) {
|
||||
copy(dst, b[i*8:(i+1)*8])
|
||||
dst = dst[8:]
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(dst) != 0 {
|
||||
panic("dst math")
|
||||
}
|
||||
|
||||
// Sweep up pair of columns,
|
||||
// then down, assigning to right then left pixel.
|
||||
// Repeat.
|
||||
// See Figure 2 of http://www.pclviewer.com/rs2/qrtopology.htm
|
||||
siz := len(p.Pixel)
|
||||
rem := make([]Pixel, 7)
|
||||
for i := range rem {
|
||||
rem[i] = Extra.Pixel()
|
||||
}
|
||||
src := append(bits, rem...)
|
||||
for x := siz; x > 0; {
|
||||
for y := siz - 1; y >= 0; y-- {
|
||||
if p.Pixel[y][x-1].Role() == 0 {
|
||||
p.Pixel[y][x-1], src = src[0], src[1:]
|
||||
}
|
||||
if p.Pixel[y][x-2].Role() == 0 {
|
||||
p.Pixel[y][x-2], src = src[0], src[1:]
|
||||
}
|
||||
}
|
||||
x -= 2
|
||||
if x == 7 { // vertical timing strip
|
||||
x--
|
||||
}
|
||||
for y := 0; y < siz; y++ {
|
||||
if p.Pixel[y][x-1].Role() == 0 {
|
||||
p.Pixel[y][x-1], src = src[0], src[1:]
|
||||
}
|
||||
if p.Pixel[y][x-2].Role() == 0 {
|
||||
p.Pixel[y][x-2], src = src[0], src[1:]
|
||||
}
|
||||
}
|
||||
x -= 2
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// mplan edits a version+level-only Plan to add the mask.
|
||||
func mplan(m Mask, p *Plan) error {
|
||||
p.Mask = m
|
||||
for y, row := range p.Pixel {
|
||||
for x, pix := range row {
|
||||
if r := pix.Role(); (r == Data || r == Check || r == Extra) && p.Mask.Invert(y, x) {
|
||||
row[x] ^= Black | Invert
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// posBox draws a position (large) box at upper left x, y.
|
||||
func posBox(m [][]Pixel, x, y int) {
|
||||
pos := Position.Pixel()
|
||||
// box
|
||||
for dy := 0; dy < 7; dy++ {
|
||||
for dx := 0; dx < 7; dx++ {
|
||||
p := pos
|
||||
if dx == 0 || dx == 6 || dy == 0 || dy == 6 || 2 <= dx && dx <= 4 && 2 <= dy && dy <= 4 {
|
||||
p |= Black
|
||||
}
|
||||
m[y+dy][x+dx] = p
|
||||
}
|
||||
}
|
||||
// white border
|
||||
for dy := -1; dy < 8; dy++ {
|
||||
if 0 <= y+dy && y+dy < len(m) {
|
||||
if x > 0 {
|
||||
m[y+dy][x-1] = pos
|
||||
}
|
||||
if x+7 < len(m) {
|
||||
m[y+dy][x+7] = pos
|
||||
}
|
||||
}
|
||||
}
|
||||
for dx := -1; dx < 8; dx++ {
|
||||
if 0 <= x+dx && x+dx < len(m) {
|
||||
if y > 0 {
|
||||
m[y-1][x+dx] = pos
|
||||
}
|
||||
if y+7 < len(m) {
|
||||
m[y+7][x+dx] = pos
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// alignBox draw an alignment (small) box at upper left x, y.
|
||||
func alignBox(m [][]Pixel, x, y int) {
|
||||
// box
|
||||
align := Alignment.Pixel()
|
||||
for dy := 0; dy < 5; dy++ {
|
||||
for dx := 0; dx < 5; dx++ {
|
||||
p := align
|
||||
if dx == 0 || dx == 4 || dy == 0 || dy == 4 || dx == 2 && dy == 2 {
|
||||
p |= Black
|
||||
}
|
||||
m[y+dy][x+dx] = p
|
||||
}
|
||||
}
|
||||
}
|
||||
85
vendor/github.com/chai2010/image/qrencoder/internal/gf256/blog_test.go
generated
vendored
Normal file
85
vendor/github.com/chai2010/image/qrencoder/internal/gf256/blog_test.go
generated
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
// Copyright 2012 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.
|
||||
|
||||
// This file contains a straightforward implementation of
|
||||
// Reed-Solomon encoding, along with a benchmark.
|
||||
// It goes with http://research.swtch.com/field.
|
||||
//
|
||||
// For an optimized implementation, see gf256.go.
|
||||
|
||||
package gf256
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// BlogECC writes to check the error correcting code bytes
|
||||
// for data using the given Reed-Solomon parameters.
|
||||
func BlogECC(rs *RSEncoder, m []byte, check []byte) {
|
||||
if len(check) < rs.c {
|
||||
panic("gf256: invalid check byte length")
|
||||
}
|
||||
if rs.c == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// The check bytes are the remainder after dividing
|
||||
// data padded with c zeros by the generator polynomial.
|
||||
|
||||
// p = data padded with c zeros.
|
||||
var p []byte
|
||||
n := len(m) + rs.c
|
||||
if len(rs.p) >= n {
|
||||
p = rs.p
|
||||
} else {
|
||||
p = make([]byte, n)
|
||||
}
|
||||
copy(p, m)
|
||||
for i := len(m); i < len(p); i++ {
|
||||
p[i] = 0
|
||||
}
|
||||
|
||||
gen := rs.gen
|
||||
|
||||
// Divide p by gen, leaving the remainder in p[len(data):].
|
||||
// p[0] is the most significant term in p, and
|
||||
// gen[0] is the most significant term in the generator.
|
||||
for i := 0; i < len(m); i++ {
|
||||
k := f.Mul(p[i], f.Inv(gen[0])) // k = pi / g0
|
||||
// p -= k·g
|
||||
for j, g := range gen {
|
||||
p[i+j] = f.Add(p[i+j], f.Mul(k, g))
|
||||
}
|
||||
}
|
||||
|
||||
copy(check, p[len(m):])
|
||||
rs.p = p
|
||||
}
|
||||
|
||||
func BenchmarkBlogECC(b *testing.B) {
|
||||
data := []byte{0x10, 0x20, 0x0c, 0x56, 0x61, 0x80, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0x10, 0x20, 0x0c, 0x56, 0x61, 0x80, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11}
|
||||
check := []byte{0x29, 0x41, 0xb3, 0x93, 0x8, 0xe8, 0xa3, 0xe7, 0x63, 0x8f}
|
||||
out := make([]byte, len(check))
|
||||
rs := NewRSEncoder(f, len(check))
|
||||
for i := 0; i < b.N; i++ {
|
||||
BlogECC(rs, data, out)
|
||||
}
|
||||
b.SetBytes(int64(len(data)))
|
||||
if !bytes.Equal(out, check) {
|
||||
fmt.Printf("have %#v want %#v\n", out, check)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBlogECC(t *testing.T) {
|
||||
data := []byte{0x10, 0x20, 0x0c, 0x56, 0x61, 0x80, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11}
|
||||
check := []byte{0xa5, 0x24, 0xd4, 0xc1, 0xed, 0x36, 0xc7, 0x87, 0x2c, 0x55}
|
||||
out := make([]byte, len(check))
|
||||
rs := NewRSEncoder(f, len(check))
|
||||
BlogECC(rs, data, out)
|
||||
if !bytes.Equal(out, check) {
|
||||
t.Errorf("have %x want %x", out, check)
|
||||
}
|
||||
}
|
||||
241
vendor/github.com/chai2010/image/qrencoder/internal/gf256/gf256.go
generated
vendored
Normal file
241
vendor/github.com/chai2010/image/qrencoder/internal/gf256/gf256.go
generated
vendored
Normal file
@@ -0,0 +1,241 @@
|
||||
// Copyright 2010 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 gf256 implements arithmetic over the Galois Field GF(256).
|
||||
package gf256
|
||||
|
||||
import "strconv"
|
||||
|
||||
// A Field represents an instance of GF(256) defined by a specific polynomial.
|
||||
type Field struct {
|
||||
log [256]byte // log[0] is unused
|
||||
exp [510]byte
|
||||
}
|
||||
|
||||
// NewField returns a new field corresponding to the polynomial poly
|
||||
// and generator α. The Reed-Solomon encoding in QR codes uses
|
||||
// polynomial 0x11d with generator 2.
|
||||
//
|
||||
// The choice of generator α only affects the Exp and Log operations.
|
||||
func NewField(poly, α int) *Field {
|
||||
if poly < 0x100 || poly >= 0x200 || reducible(poly) {
|
||||
panic("gf256: invalid polynomial: " + strconv.Itoa(poly))
|
||||
}
|
||||
|
||||
var f Field
|
||||
x := 1
|
||||
for i := 0; i < 255; i++ {
|
||||
if x == 1 && i != 0 {
|
||||
panic("gf256: invalid generator " + strconv.Itoa(α) +
|
||||
" for polynomial " + strconv.Itoa(poly))
|
||||
}
|
||||
f.exp[i] = byte(x)
|
||||
f.exp[i+255] = byte(x)
|
||||
f.log[x] = byte(i)
|
||||
x = mul(x, α, poly)
|
||||
}
|
||||
f.log[0] = 255
|
||||
for i := 0; i < 255; i++ {
|
||||
if f.log[f.exp[i]] != byte(i) {
|
||||
panic("bad log")
|
||||
}
|
||||
if f.log[f.exp[i+255]] != byte(i) {
|
||||
panic("bad log")
|
||||
}
|
||||
}
|
||||
for i := 1; i < 256; i++ {
|
||||
if f.exp[f.log[i]] != byte(i) {
|
||||
panic("bad log")
|
||||
}
|
||||
}
|
||||
|
||||
return &f
|
||||
}
|
||||
|
||||
// nbit returns the number of significant in p.
|
||||
func nbit(p int) uint {
|
||||
n := uint(0)
|
||||
for ; p > 0; p >>= 1 {
|
||||
n++
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// polyDiv divides the polynomial p by q and returns the remainder.
|
||||
func polyDiv(p, q int) int {
|
||||
np := nbit(p)
|
||||
nq := nbit(q)
|
||||
for ; np >= nq; np-- {
|
||||
if p&(1<<(np-1)) != 0 {
|
||||
p ^= q << (np - nq)
|
||||
}
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// mul returns the product x*y mod poly, a GF(256) multiplication.
|
||||
func mul(x, y, poly int) int {
|
||||
z := 0
|
||||
for x > 0 {
|
||||
if x&1 != 0 {
|
||||
z ^= y
|
||||
}
|
||||
x >>= 1
|
||||
y <<= 1
|
||||
if y&0x100 != 0 {
|
||||
y ^= poly
|
||||
}
|
||||
}
|
||||
return z
|
||||
}
|
||||
|
||||
// reducible reports whether p is reducible.
|
||||
func reducible(p int) bool {
|
||||
// Multiplying n-bit * n-bit produces (2n-1)-bit,
|
||||
// so if p is reducible, one of its factors must be
|
||||
// of np/2+1 bits or fewer.
|
||||
np := nbit(p)
|
||||
for q := 2; q < 1<<(np/2+1); q++ {
|
||||
if polyDiv(p, q) == 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Add returns the sum of x and y in the field.
|
||||
func (f *Field) Add(x, y byte) byte {
|
||||
return x ^ y
|
||||
}
|
||||
|
||||
// Exp returns the base-α exponential of e in the field.
|
||||
// If e < 0, Exp returns 0.
|
||||
func (f *Field) Exp(e int) byte {
|
||||
if e < 0 {
|
||||
return 0
|
||||
}
|
||||
return f.exp[e%255]
|
||||
}
|
||||
|
||||
// Log returns the base-α logarithm of x in the field.
|
||||
// If x == 0, Log returns -1.
|
||||
func (f *Field) Log(x byte) int {
|
||||
if x == 0 {
|
||||
return -1
|
||||
}
|
||||
return int(f.log[x])
|
||||
}
|
||||
|
||||
// Inv returns the multiplicative inverse of x in the field.
|
||||
// If x == 0, Inv returns 0.
|
||||
func (f *Field) Inv(x byte) byte {
|
||||
if x == 0 {
|
||||
return 0
|
||||
}
|
||||
return f.exp[255-f.log[x]]
|
||||
}
|
||||
|
||||
// Mul returns the product of x and y in the field.
|
||||
func (f *Field) Mul(x, y byte) byte {
|
||||
if x == 0 || y == 0 {
|
||||
return 0
|
||||
}
|
||||
return f.exp[int(f.log[x])+int(f.log[y])]
|
||||
}
|
||||
|
||||
// An RSEncoder implements Reed-Solomon encoding
|
||||
// over a given field using a given number of error correction bytes.
|
||||
type RSEncoder struct {
|
||||
f *Field
|
||||
c int
|
||||
gen []byte
|
||||
lgen []byte
|
||||
p []byte
|
||||
}
|
||||
|
||||
func (f *Field) gen(e int) (gen, lgen []byte) {
|
||||
// p = 1
|
||||
p := make([]byte, e+1)
|
||||
p[e] = 1
|
||||
|
||||
for i := 0; i < e; i++ {
|
||||
// p *= (x + Exp(i))
|
||||
// p[j] = p[j]*Exp(i) + p[j+1].
|
||||
c := f.Exp(i)
|
||||
for j := 0; j < e; j++ {
|
||||
p[j] = f.Mul(p[j], c) ^ p[j+1]
|
||||
}
|
||||
p[e] = f.Mul(p[e], c)
|
||||
}
|
||||
|
||||
// lp = log p.
|
||||
lp := make([]byte, e+1)
|
||||
for i, c := range p {
|
||||
if c == 0 {
|
||||
lp[i] = 255
|
||||
} else {
|
||||
lp[i] = byte(f.Log(c))
|
||||
}
|
||||
}
|
||||
|
||||
return p, lp
|
||||
}
|
||||
|
||||
// NewRSEncoder returns a new Reed-Solomon encoder
|
||||
// over the given field and number of error correction bytes.
|
||||
func NewRSEncoder(f *Field, c int) *RSEncoder {
|
||||
gen, lgen := f.gen(c)
|
||||
return &RSEncoder{f: f, c: c, gen: gen, lgen: lgen}
|
||||
}
|
||||
|
||||
// ECC writes to check the error correcting code bytes
|
||||
// for data using the given Reed-Solomon parameters.
|
||||
func (rs *RSEncoder) ECC(data []byte, check []byte) {
|
||||
if len(check) < rs.c {
|
||||
panic("gf256: invalid check byte length")
|
||||
}
|
||||
if rs.c == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// The check bytes are the remainder after dividing
|
||||
// data padded with c zeros by the generator polynomial.
|
||||
|
||||
// p = data padded with c zeros.
|
||||
var p []byte
|
||||
n := len(data) + rs.c
|
||||
if len(rs.p) >= n {
|
||||
p = rs.p
|
||||
} else {
|
||||
p = make([]byte, n)
|
||||
}
|
||||
copy(p, data)
|
||||
for i := len(data); i < len(p); i++ {
|
||||
p[i] = 0
|
||||
}
|
||||
|
||||
// Divide p by gen, leaving the remainder in p[len(data):].
|
||||
// p[0] is the most significant term in p, and
|
||||
// gen[0] is the most significant term in the generator,
|
||||
// which is always 1.
|
||||
// To avoid repeated work, we store various values as
|
||||
// lv, not v, where lv = log[v].
|
||||
f := rs.f
|
||||
lgen := rs.lgen[1:]
|
||||
for i := 0; i < len(data); i++ {
|
||||
c := p[i]
|
||||
if c == 0 {
|
||||
continue
|
||||
}
|
||||
q := p[i+1:]
|
||||
exp := f.exp[f.log[c]:]
|
||||
for j, lg := range lgen {
|
||||
if lg != 255 { // lgen uses 255 for log 0
|
||||
q[j] ^= exp[lg]
|
||||
}
|
||||
}
|
||||
}
|
||||
copy(check, p[len(data):])
|
||||
rs.p = p
|
||||
}
|
||||
194
vendor/github.com/chai2010/image/qrencoder/internal/gf256/gf256_test.go
generated
vendored
Normal file
194
vendor/github.com/chai2010/image/qrencoder/internal/gf256/gf256_test.go
generated
vendored
Normal file
@@ -0,0 +1,194 @@
|
||||
// Copyright 2010 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 gf256
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var f = NewField(0x11d, 2) // x^8 + x^4 + x^3 + x^2 + 1
|
||||
|
||||
func TestBasic(t *testing.T) {
|
||||
if f.Exp(0) != 1 || f.Exp(1) != 2 || f.Exp(255) != 1 {
|
||||
panic("bad Exp")
|
||||
}
|
||||
}
|
||||
|
||||
func TestECC(t *testing.T) {
|
||||
data := []byte{0x10, 0x20, 0x0c, 0x56, 0x61, 0x80, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11}
|
||||
check := []byte{0xa5, 0x24, 0xd4, 0xc1, 0xed, 0x36, 0xc7, 0x87, 0x2c, 0x55}
|
||||
out := make([]byte, len(check))
|
||||
rs := NewRSEncoder(f, len(check))
|
||||
rs.ECC(data, out)
|
||||
if !bytes.Equal(out, check) {
|
||||
t.Errorf("have %x want %x", out, check)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLinear(t *testing.T) {
|
||||
d1 := []byte{0x00, 0x00}
|
||||
c1 := []byte{0x00, 0x00}
|
||||
out := make([]byte, len(c1))
|
||||
rs := NewRSEncoder(f, len(c1))
|
||||
if rs.ECC(d1, out); !bytes.Equal(out, c1) {
|
||||
t.Errorf("ECBytes(%x, %d) = %x, want 0", d1, len(c1), out)
|
||||
}
|
||||
d2 := []byte{0x00, 0x01}
|
||||
c2 := make([]byte, 2)
|
||||
rs.ECC(d2, c2)
|
||||
d3 := []byte{0x00, 0x02}
|
||||
c3 := make([]byte, 2)
|
||||
rs.ECC(d3, c3)
|
||||
cx := make([]byte, 2)
|
||||
for i := range cx {
|
||||
cx[i] = c2[i] ^ c3[i]
|
||||
}
|
||||
d4 := []byte{0x00, 0x03}
|
||||
c4 := make([]byte, 2)
|
||||
rs.ECC(d4, c4)
|
||||
if !bytes.Equal(cx, c4) {
|
||||
t.Errorf("ECBytes(%x, 2) = %x\nECBytes(%x, 2) = %x\nxor = %x\nECBytes(%x, 2) = %x",
|
||||
d2, c2, d3, c3, cx, d4, c4)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGaussJordan(t *testing.T) {
|
||||
rs := NewRSEncoder(f, 2)
|
||||
m := make([][]byte, 16)
|
||||
for i := range m {
|
||||
m[i] = make([]byte, 4)
|
||||
m[i][i/8] = 1 << uint(i%8)
|
||||
rs.ECC(m[i][:2], m[i][2:])
|
||||
}
|
||||
if false {
|
||||
fmt.Printf("---\n")
|
||||
for _, row := range m {
|
||||
fmt.Printf("%x\n", row)
|
||||
}
|
||||
}
|
||||
b := []uint{0, 1, 2, 3, 12, 13, 14, 15, 20, 21, 22, 23, 24, 25, 26, 27}
|
||||
for i := 0; i < 16; i++ {
|
||||
bi := b[i]
|
||||
if m[i][bi/8]&(1<<(7-bi%8)) == 0 {
|
||||
for j := i + 1; ; j++ {
|
||||
if j >= len(m) {
|
||||
t.Errorf("lost track for %d", bi)
|
||||
break
|
||||
}
|
||||
if m[j][bi/8]&(1<<(7-bi%8)) != 0 {
|
||||
m[i], m[j] = m[j], m[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
for j := i + 1; j < len(m); j++ {
|
||||
if m[j][bi/8]&(1<<(7-bi%8)) != 0 {
|
||||
for k := range m[j] {
|
||||
m[j][k] ^= m[i][k]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if false {
|
||||
fmt.Printf("---\n")
|
||||
for _, row := range m {
|
||||
fmt.Printf("%x\n", row)
|
||||
}
|
||||
}
|
||||
for i := 15; i >= 0; i-- {
|
||||
bi := b[i]
|
||||
for j := i - 1; j >= 0; j-- {
|
||||
if m[j][bi/8]&(1<<(7-bi%8)) != 0 {
|
||||
for k := range m[j] {
|
||||
m[j][k] ^= m[i][k]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if false {
|
||||
fmt.Printf("---\n")
|
||||
for _, row := range m {
|
||||
fmt.Printf("%x", row)
|
||||
out := make([]byte, 2)
|
||||
if rs.ECC(row[:2], out); !bytes.Equal(out, row[2:]) {
|
||||
fmt.Printf(" - want %x", out)
|
||||
}
|
||||
fmt.Printf("\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkECC(b *testing.B) {
|
||||
data := []byte{0x10, 0x20, 0x0c, 0x56, 0x61, 0x80, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0x10, 0x20, 0x0c, 0x56, 0x61, 0x80, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11, 0xec, 0x11}
|
||||
check := []byte{0x29, 0x41, 0xb3, 0x93, 0x8, 0xe8, 0xa3, 0xe7, 0x63, 0x8f}
|
||||
out := make([]byte, len(check))
|
||||
rs := NewRSEncoder(f, len(check))
|
||||
for i := 0; i < b.N; i++ {
|
||||
rs.ECC(data, out)
|
||||
}
|
||||
b.SetBytes(int64(len(data)))
|
||||
if !bytes.Equal(out, check) {
|
||||
fmt.Printf("have %#v want %#v\n", out, check)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGen(t *testing.T) {
|
||||
for i := 0; i < 256; i++ {
|
||||
_, lg := f.gen(i)
|
||||
if lg[0] != 0 {
|
||||
t.Errorf("#%d: %x", i, lg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestReducible(t *testing.T) {
|
||||
var count = []int{1, 2, 3, 6, 9, 18, 30, 56, 99, 186} // oeis.org/A1037
|
||||
for i, want := range count {
|
||||
n := 0
|
||||
for p := 1 << uint(i+2); p < 1<<uint(i+3); p++ {
|
||||
if !reducible(p) {
|
||||
n++
|
||||
}
|
||||
}
|
||||
if n != want {
|
||||
t.Errorf("#reducible(%d-bit) = %d, want %d", i+2, n, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestExhaustive(t *testing.T) {
|
||||
for poly := 0x100; poly < 0x200; poly++ {
|
||||
if reducible(poly) {
|
||||
continue
|
||||
}
|
||||
α := 2
|
||||
for !generates(α, poly) {
|
||||
α++
|
||||
}
|
||||
f := NewField(poly, α)
|
||||
for p := 0; p < 256; p++ {
|
||||
for q := 0; q < 256; q++ {
|
||||
fm := int(f.Mul(byte(p), byte(q)))
|
||||
pm := mul(p, q, poly)
|
||||
if fm != pm {
|
||||
t.Errorf("NewField(%#x).Mul(%#x, %#x) = %#x, want %#x", poly, p, q, fm, pm)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func generates(α, poly int) bool {
|
||||
x := α
|
||||
for i := 0; i < 254; i++ {
|
||||
if x == 1 {
|
||||
return false
|
||||
}
|
||||
x = mul(x, α, poly)
|
||||
}
|
||||
return true
|
||||
}
|
||||
400
vendor/github.com/chai2010/image/qrencoder/png.go
generated
vendored
Normal file
400
vendor/github.com/chai2010/image/qrencoder/png.go
generated
vendored
Normal file
@@ -0,0 +1,400 @@
|
||||
// Copyright 2011 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 qrcode
|
||||
|
||||
// PNG writer for QR codes.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"hash"
|
||||
"hash/crc32"
|
||||
)
|
||||
|
||||
// PNG returns a PNG image displaying the code.
|
||||
//
|
||||
// PNG uses a custom encoder tailored to QR codes.
|
||||
// Its compressed size is about 2x away from optimal,
|
||||
// but it runs about 20x faster than calling png.Encode
|
||||
// on c.Image().
|
||||
func (c *Code) PNG() []byte {
|
||||
var p pngWriter
|
||||
return p.encode(c)
|
||||
}
|
||||
|
||||
type pngWriter struct {
|
||||
tmp [16]byte
|
||||
wctmp [4]byte
|
||||
buf bytes.Buffer
|
||||
zlib bitWriter
|
||||
crc hash.Hash32
|
||||
}
|
||||
|
||||
var pngHeader = []byte("\x89PNG\r\n\x1a\n")
|
||||
|
||||
func (w *pngWriter) encode(c *Code) []byte {
|
||||
scale := c.Scale
|
||||
siz := c.Size
|
||||
|
||||
w.buf.Reset()
|
||||
|
||||
// Header
|
||||
w.buf.Write(pngHeader)
|
||||
|
||||
// Header block
|
||||
binary.BigEndian.PutUint32(w.tmp[0:4], uint32((siz+8)*scale))
|
||||
binary.BigEndian.PutUint32(w.tmp[4:8], uint32((siz+8)*scale))
|
||||
w.tmp[8] = 1 // 1-bit
|
||||
w.tmp[9] = 0 // gray
|
||||
w.tmp[10] = 0
|
||||
w.tmp[11] = 0
|
||||
w.tmp[12] = 0
|
||||
w.writeChunk("IHDR", w.tmp[:13])
|
||||
|
||||
// Comment
|
||||
w.writeChunk("tEXt", comment)
|
||||
|
||||
// Data
|
||||
w.zlib.writeCode(c)
|
||||
w.writeChunk("IDAT", w.zlib.bytes.Bytes())
|
||||
|
||||
// End
|
||||
w.writeChunk("IEND", nil)
|
||||
|
||||
return w.buf.Bytes()
|
||||
}
|
||||
|
||||
var comment = []byte("Software\x00QR-PNG <chaishushan{AT}gmail.com>")
|
||||
|
||||
func (w *pngWriter) writeChunk(name string, data []byte) {
|
||||
if w.crc == nil {
|
||||
w.crc = crc32.NewIEEE()
|
||||
}
|
||||
binary.BigEndian.PutUint32(w.wctmp[0:4], uint32(len(data)))
|
||||
w.buf.Write(w.wctmp[0:4])
|
||||
w.crc.Reset()
|
||||
copy(w.wctmp[0:4], name)
|
||||
w.buf.Write(w.wctmp[0:4])
|
||||
w.crc.Write(w.wctmp[0:4])
|
||||
w.buf.Write(data)
|
||||
w.crc.Write(data)
|
||||
crc := w.crc.Sum32()
|
||||
binary.BigEndian.PutUint32(w.wctmp[0:4], crc)
|
||||
w.buf.Write(w.wctmp[0:4])
|
||||
}
|
||||
|
||||
func (b *bitWriter) writeCode(c *Code) {
|
||||
const ftNone = 0
|
||||
|
||||
b.adler32.Reset()
|
||||
b.bytes.Reset()
|
||||
b.nbit = 0
|
||||
|
||||
scale := c.Scale
|
||||
siz := c.Size
|
||||
|
||||
// zlib header
|
||||
b.tmp[0] = 0x78
|
||||
b.tmp[1] = 0
|
||||
b.tmp[1] += uint8(31 - (uint16(b.tmp[0])<<8+uint16(b.tmp[1]))%31)
|
||||
b.bytes.Write(b.tmp[0:2])
|
||||
|
||||
// Start flate block.
|
||||
b.writeBits(1, 1, false) // final block
|
||||
b.writeBits(1, 2, false) // compressed, fixed Huffman tables
|
||||
|
||||
// White border.
|
||||
// First row.
|
||||
b.byte(ftNone)
|
||||
n := (scale*(siz+8) + 7) / 8
|
||||
b.byte(255)
|
||||
b.repeat(n-1, 1)
|
||||
// 4*scale rows total.
|
||||
b.repeat((4*scale-1)*(1+n), 1+n)
|
||||
|
||||
for i := 0; i < 4*scale; i++ {
|
||||
b.adler32.WriteNByte(ftNone, 1)
|
||||
b.adler32.WriteNByte(255, n)
|
||||
}
|
||||
|
||||
row := make([]byte, 1+n)
|
||||
for y := 0; y < siz; y++ {
|
||||
row[0] = ftNone
|
||||
j := 1
|
||||
var z uint8
|
||||
nz := 0
|
||||
for x := -4; x < siz+4; x++ {
|
||||
// Raw data.
|
||||
for i := 0; i < scale; i++ {
|
||||
z <<= 1
|
||||
if !c.Black(x, y) {
|
||||
z |= 1
|
||||
}
|
||||
if nz++; nz == 8 {
|
||||
row[j] = z
|
||||
j++
|
||||
nz = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
if j < len(row) {
|
||||
row[j] = z
|
||||
}
|
||||
for _, z := range row {
|
||||
b.byte(z)
|
||||
}
|
||||
|
||||
// Scale-1 copies.
|
||||
b.repeat((scale-1)*(1+n), 1+n)
|
||||
|
||||
b.adler32.WriteN(row, scale)
|
||||
}
|
||||
|
||||
// White border.
|
||||
// First row.
|
||||
b.byte(ftNone)
|
||||
b.byte(255)
|
||||
b.repeat(n-1, 1)
|
||||
// 4*scale rows total.
|
||||
b.repeat((4*scale-1)*(1+n), 1+n)
|
||||
|
||||
for i := 0; i < 4*scale; i++ {
|
||||
b.adler32.WriteNByte(ftNone, 1)
|
||||
b.adler32.WriteNByte(255, n)
|
||||
}
|
||||
|
||||
// End of block.
|
||||
b.hcode(256)
|
||||
b.flushBits()
|
||||
|
||||
// adler32
|
||||
binary.BigEndian.PutUint32(b.tmp[0:], b.adler32.Sum32())
|
||||
b.bytes.Write(b.tmp[0:4])
|
||||
}
|
||||
|
||||
// A bitWriter is a write buffer for bit-oriented data like deflate.
|
||||
type bitWriter struct {
|
||||
bytes bytes.Buffer
|
||||
bit uint32
|
||||
nbit uint
|
||||
|
||||
tmp [4]byte
|
||||
adler32 adigest
|
||||
}
|
||||
|
||||
func (b *bitWriter) writeBits(bit uint32, nbit uint, rev bool) {
|
||||
// reverse, for huffman codes
|
||||
if rev {
|
||||
br := uint32(0)
|
||||
for i := uint(0); i < nbit; i++ {
|
||||
br |= ((bit >> i) & 1) << (nbit - 1 - i)
|
||||
}
|
||||
bit = br
|
||||
}
|
||||
b.bit |= bit << b.nbit
|
||||
b.nbit += nbit
|
||||
for b.nbit >= 8 {
|
||||
b.bytes.WriteByte(byte(b.bit))
|
||||
b.bit >>= 8
|
||||
b.nbit -= 8
|
||||
}
|
||||
}
|
||||
|
||||
func (b *bitWriter) flushBits() {
|
||||
if b.nbit > 0 {
|
||||
b.bytes.WriteByte(byte(b.bit))
|
||||
b.nbit = 0
|
||||
b.bit = 0
|
||||
}
|
||||
}
|
||||
|
||||
func (b *bitWriter) hcode(v int) {
|
||||
/*
|
||||
Lit Value Bits Codes
|
||||
--------- ---- -----
|
||||
0 - 143 8 00110000 through
|
||||
10111111
|
||||
144 - 255 9 110010000 through
|
||||
111111111
|
||||
256 - 279 7 0000000 through
|
||||
0010111
|
||||
280 - 287 8 11000000 through
|
||||
11000111
|
||||
*/
|
||||
switch {
|
||||
case v <= 143:
|
||||
b.writeBits(uint32(v)+0x30, 8, true)
|
||||
case v <= 255:
|
||||
b.writeBits(uint32(v-144)+0x190, 9, true)
|
||||
case v <= 279:
|
||||
b.writeBits(uint32(v-256)+0, 7, true)
|
||||
case v <= 287:
|
||||
b.writeBits(uint32(v-280)+0xc0, 8, true)
|
||||
default:
|
||||
panic("invalid hcode")
|
||||
}
|
||||
}
|
||||
|
||||
func (b *bitWriter) byte(x byte) {
|
||||
b.hcode(int(x))
|
||||
}
|
||||
|
||||
func (b *bitWriter) codex(c int, val int, nx uint) {
|
||||
b.hcode(c + val>>nx)
|
||||
b.writeBits(uint32(val)&(1<<nx-1), nx, false)
|
||||
}
|
||||
|
||||
func (b *bitWriter) repeat(n, d int) {
|
||||
for ; n >= 258+3; n -= 258 {
|
||||
b.repeat1(258, d)
|
||||
}
|
||||
if n > 258 {
|
||||
// 258 < n < 258+3
|
||||
b.repeat1(10, d)
|
||||
b.repeat1(n-10, d)
|
||||
return
|
||||
}
|
||||
if n < 3 {
|
||||
panic("invalid flate repeat")
|
||||
}
|
||||
b.repeat1(n, d)
|
||||
}
|
||||
|
||||
func (b *bitWriter) repeat1(n, d int) {
|
||||
/*
|
||||
Extra Extra Extra
|
||||
Code Bits Length(s) Code Bits Lengths Code Bits Length(s)
|
||||
---- ---- ------ ---- ---- ------- ---- ---- -------
|
||||
257 0 3 267 1 15,16 277 4 67-82
|
||||
258 0 4 268 1 17,18 278 4 83-98
|
||||
259 0 5 269 2 19-22 279 4 99-114
|
||||
260 0 6 270 2 23-26 280 4 115-130
|
||||
261 0 7 271 2 27-30 281 5 131-162
|
||||
262 0 8 272 2 31-34 282 5 163-194
|
||||
263 0 9 273 3 35-42 283 5 195-226
|
||||
264 0 10 274 3 43-50 284 5 227-257
|
||||
265 1 11,12 275 3 51-58 285 0 258
|
||||
266 1 13,14 276 3 59-66
|
||||
*/
|
||||
switch {
|
||||
case n <= 10:
|
||||
b.codex(257, n-3, 0)
|
||||
case n <= 18:
|
||||
b.codex(265, n-11, 1)
|
||||
case n <= 34:
|
||||
b.codex(269, n-19, 2)
|
||||
case n <= 66:
|
||||
b.codex(273, n-35, 3)
|
||||
case n <= 130:
|
||||
b.codex(277, n-67, 4)
|
||||
case n <= 257:
|
||||
b.codex(281, n-131, 5)
|
||||
case n == 258:
|
||||
b.hcode(285)
|
||||
default:
|
||||
panic("invalid repeat length")
|
||||
}
|
||||
|
||||
/*
|
||||
Extra Extra Extra
|
||||
Code Bits Dist Code Bits Dist Code Bits Distance
|
||||
---- ---- ---- ---- ---- ------ ---- ---- --------
|
||||
0 0 1 10 4 33-48 20 9 1025-1536
|
||||
1 0 2 11 4 49-64 21 9 1537-2048
|
||||
2 0 3 12 5 65-96 22 10 2049-3072
|
||||
3 0 4 13 5 97-128 23 10 3073-4096
|
||||
4 1 5,6 14 6 129-192 24 11 4097-6144
|
||||
5 1 7,8 15 6 193-256 25 11 6145-8192
|
||||
6 2 9-12 16 7 257-384 26 12 8193-12288
|
||||
7 2 13-16 17 7 385-512 27 12 12289-16384
|
||||
8 3 17-24 18 8 513-768 28 13 16385-24576
|
||||
9 3 25-32 19 8 769-1024 29 13 24577-32768
|
||||
*/
|
||||
if d <= 4 {
|
||||
b.writeBits(uint32(d-1), 5, true)
|
||||
} else if d <= 32768 {
|
||||
nbit := uint(16)
|
||||
for d <= 1<<(nbit-1) {
|
||||
nbit--
|
||||
}
|
||||
v := uint32(d - 1)
|
||||
v &^= 1 << (nbit - 1) // top bit is implicit
|
||||
code := uint32(2*nbit - 2) // second bit is low bit of code
|
||||
code |= v >> (nbit - 2)
|
||||
v &^= 1 << (nbit - 2)
|
||||
b.writeBits(code, 5, true)
|
||||
// rest of bits follow
|
||||
b.writeBits(uint32(v), nbit-2, false)
|
||||
} else {
|
||||
panic("invalid repeat distance")
|
||||
}
|
||||
}
|
||||
|
||||
func (b *bitWriter) run(v byte, n int) {
|
||||
if n == 0 {
|
||||
return
|
||||
}
|
||||
b.byte(v)
|
||||
if n-1 < 3 {
|
||||
for i := 0; i < n-1; i++ {
|
||||
b.byte(v)
|
||||
}
|
||||
} else {
|
||||
b.repeat(n-1, 1)
|
||||
}
|
||||
}
|
||||
|
||||
type adigest struct {
|
||||
a, b uint32
|
||||
}
|
||||
|
||||
func (d *adigest) Reset() { d.a, d.b = 1, 0 }
|
||||
|
||||
const amod = 65521
|
||||
|
||||
func aupdate(a, b uint32, pi byte, n int) (aa, bb uint32) {
|
||||
// TODO(rsc): 6g doesn't do magic multiplies for b %= amod,
|
||||
// only for b = b%amod.
|
||||
|
||||
// invariant: a, b < amod
|
||||
if pi == 0 {
|
||||
b += uint32(n%amod) * a
|
||||
b = b % amod
|
||||
return a, b
|
||||
}
|
||||
|
||||
// n times:
|
||||
// a += pi
|
||||
// b += a
|
||||
// is same as
|
||||
// b += n*a + n*(n+1)/2*pi
|
||||
// a += n*pi
|
||||
m := uint32(n)
|
||||
b += (m % amod) * a
|
||||
b = b % amod
|
||||
b += (m * (m + 1) / 2) % amod * uint32(pi)
|
||||
b = b % amod
|
||||
a += (m % amod) * uint32(pi)
|
||||
a = a % amod
|
||||
return a, b
|
||||
}
|
||||
|
||||
func afinish(a, b uint32) uint32 {
|
||||
return b<<16 | a
|
||||
}
|
||||
|
||||
func (d *adigest) WriteN(p []byte, n int) {
|
||||
for i := 0; i < n; i++ {
|
||||
for _, pi := range p {
|
||||
d.a, d.b = aupdate(d.a, d.b, pi, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (d *adigest) WriteNByte(pi byte, n int) {
|
||||
d.a, d.b = aupdate(d.a, d.b, pi, n)
|
||||
}
|
||||
|
||||
func (d *adigest) Sum32() uint32 { return afinish(d.a, d.b) }
|
||||
69
vendor/github.com/chai2010/image/qrencoder/png_test.go
generated
vendored
Normal file
69
vendor/github.com/chai2010/image/qrencoder/png_test.go
generated
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
// Copyright 2011 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 qrcode
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"image"
|
||||
"image/color"
|
||||
"image/png"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPNG(t *testing.T) {
|
||||
c, err := Encode("hello, world", L)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
pngdat := c.PNG()
|
||||
m, err := png.Decode(bytes.NewBuffer(pngdat))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
gm := m.(*image.Gray)
|
||||
|
||||
scale := c.Scale
|
||||
siz := c.Size
|
||||
nbad := 0
|
||||
for y := 0; y < scale*(8+siz); y++ {
|
||||
for x := 0; x < scale*(8+siz); x++ {
|
||||
v := byte(255)
|
||||
if c.Black(x/scale-4, y/scale-4) {
|
||||
v = 0
|
||||
}
|
||||
if gv := gm.At(x, y).(color.Gray).Y; gv != v {
|
||||
t.Errorf("%d,%d = %d, want %d", x, y, gv, v)
|
||||
if nbad++; nbad >= 20 {
|
||||
t.Fatalf("too many bad pixels")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkPNG(b *testing.B) {
|
||||
c, err := Encode("0123456789012345678901234567890123456789", L)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
var bytes []byte
|
||||
for i := 0; i < b.N; i++ {
|
||||
bytes = c.PNG()
|
||||
}
|
||||
b.SetBytes(int64(len(bytes)))
|
||||
}
|
||||
|
||||
func BenchmarkImagePNG(b *testing.B) {
|
||||
c, err := Encode("0123456789012345678901234567890123456789", L)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
for i := 0; i < b.N; i++ {
|
||||
buf.Reset()
|
||||
png.Encode(&buf, c.Image())
|
||||
}
|
||||
b.SetBytes(int64(buf.Len()))
|
||||
}
|
||||
114
vendor/github.com/chai2010/image/qrencoder/qr.go
generated
vendored
Normal file
114
vendor/github.com/chai2010/image/qrencoder/qr.go
generated
vendored
Normal file
@@ -0,0 +1,114 @@
|
||||
// Copyright 2011 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 qrencoder implements a encoder for QR code.
|
||||
package qrcode
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"image"
|
||||
"image/color"
|
||||
|
||||
"github.com/chai2010/image/qrencoder/internal/coding"
|
||||
)
|
||||
|
||||
// A Level denotes a QR error correction level.
|
||||
// From least to most tolerant of errors, they are L, M, Q, H.
|
||||
type Level int
|
||||
|
||||
const (
|
||||
L Level = iota // 20% redundant
|
||||
M // 38% redundant
|
||||
Q // 55% redundant
|
||||
H // 65% redundant
|
||||
)
|
||||
|
||||
// Encode returns an encoding of text at the given error correction level.
|
||||
func Encode(text string, level Level) (*Code, error) {
|
||||
// Pick data encoding, smallest first.
|
||||
// We could split the string and use different encodings
|
||||
// but that seems like overkill for now.
|
||||
var enc coding.Encoding
|
||||
switch {
|
||||
case coding.Num(text).Check() == nil:
|
||||
enc = coding.Num(text)
|
||||
case coding.Alpha(text).Check() == nil:
|
||||
enc = coding.Alpha(text)
|
||||
default:
|
||||
enc = coding.String(text)
|
||||
}
|
||||
|
||||
// Pick size.
|
||||
l := coding.Level(level)
|
||||
var v coding.Version
|
||||
for v = coding.MinVersion; ; v++ {
|
||||
if v > coding.MaxVersion {
|
||||
return nil, errors.New("text too long to encode as QR")
|
||||
}
|
||||
if enc.Bits(v) <= v.DataBytes(l)*8 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Build and execute plan.
|
||||
p, err := coding.NewPlan(v, l, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cc, err := p.Encode(enc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO: Pick appropriate mask.
|
||||
|
||||
return &Code{cc.Bitmap, cc.Size, cc.Stride, 8}, nil
|
||||
}
|
||||
|
||||
// A Code is a square pixel grid.
|
||||
// It implements image.Image and direct PNG encoding.
|
||||
type Code struct {
|
||||
Bitmap []byte // 1 is black, 0 is white
|
||||
Size int // number of pixels on a side
|
||||
Stride int // number of bytes per row
|
||||
Scale int // number of image pixels per QR pixel
|
||||
}
|
||||
|
||||
// Black returns true if the pixel at (x,y) is black.
|
||||
func (c *Code) Black(x, y int) bool {
|
||||
return 0 <= x && x < c.Size && 0 <= y && y < c.Size &&
|
||||
c.Bitmap[y*c.Stride+x/8]&(1<<uint(7-x&7)) != 0
|
||||
}
|
||||
|
||||
// Image returns an Image displaying the code.
|
||||
func (c *Code) Image() image.Image {
|
||||
return &codeImage{c}
|
||||
|
||||
}
|
||||
|
||||
// codeImage implements image.Image
|
||||
type codeImage struct {
|
||||
*Code
|
||||
}
|
||||
|
||||
var (
|
||||
whiteColor color.Color = color.Gray{0xFF}
|
||||
blackColor color.Color = color.Gray{0x00}
|
||||
)
|
||||
|
||||
func (c *codeImage) Bounds() image.Rectangle {
|
||||
d := (c.Size + 8) * c.Scale
|
||||
return image.Rect(0, 0, d, d)
|
||||
}
|
||||
|
||||
func (c *codeImage) At(x, y int) color.Color {
|
||||
if c.Black(x, y) {
|
||||
return blackColor
|
||||
}
|
||||
return whiteColor
|
||||
}
|
||||
|
||||
func (c *codeImage) ColorModel() color.Model {
|
||||
return color.GrayModel
|
||||
}
|
||||
31
vendor/gopl.io/ch1/dup1/main.go
generated
vendored
Normal file
31
vendor/gopl.io/ch1/dup1/main.go
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 8.
|
||||
//!+
|
||||
|
||||
// Dup1 prints the text of each line that appears more than
|
||||
// once in the standard input, preceded by its count.
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
counts := make(map[string]int)
|
||||
input := bufio.NewScanner(os.Stdin)
|
||||
for input.Scan() {
|
||||
counts[input.Text()]++
|
||||
}
|
||||
// NOTE: ignoring potential errors from input.Err()
|
||||
for line, n := range counts {
|
||||
if n > 1 {
|
||||
fmt.Printf("%d\t%s\n", n, line)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//!-
|
||||
48
vendor/gopl.io/ch1/dup2/main.go
generated
vendored
Normal file
48
vendor/gopl.io/ch1/dup2/main.go
generated
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 10.
|
||||
//!+
|
||||
|
||||
// Dup2 prints the count and text of lines that appear more than once
|
||||
// in the input. It reads from stdin or from a list of named files.
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
counts := make(map[string]int)
|
||||
files := os.Args[1:]
|
||||
if len(files) == 0 {
|
||||
countLines(os.Stdin, counts)
|
||||
} else {
|
||||
for _, arg := range files {
|
||||
f, err := os.Open(arg)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "dup2: %v\n", err)
|
||||
continue
|
||||
}
|
||||
countLines(f, counts)
|
||||
f.Close()
|
||||
}
|
||||
}
|
||||
for line, n := range counts {
|
||||
if n > 1 {
|
||||
fmt.Printf("%d\t%s\n", n, line)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func countLines(f *os.File, counts map[string]int) {
|
||||
input := bufio.NewScanner(f)
|
||||
for input.Scan() {
|
||||
counts[input.Text()]++
|
||||
}
|
||||
// NOTE: ignoring potential errors from input.Err()
|
||||
}
|
||||
|
||||
//!-
|
||||
38
vendor/gopl.io/ch1/dup3/main.go
generated
vendored
Normal file
38
vendor/gopl.io/ch1/dup3/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 12.
|
||||
|
||||
//!+
|
||||
|
||||
// Dup3 prints the count and text of lines that
|
||||
// appear more than once in the named input files.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func main() {
|
||||
counts := make(map[string]int)
|
||||
for _, filename := range os.Args[1:] {
|
||||
data, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "dup3: %v\n", err)
|
||||
continue
|
||||
}
|
||||
for _, line := range strings.Split(string(data), "\n") {
|
||||
counts[line]++
|
||||
}
|
||||
}
|
||||
for line, n := range counts {
|
||||
if n > 1 {
|
||||
fmt.Printf("%d\t%s\n", n, line)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//!-
|
||||
24
vendor/gopl.io/ch1/echo1/main.go
generated
vendored
Normal file
24
vendor/gopl.io/ch1/echo1/main.go
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 4.
|
||||
//!+
|
||||
|
||||
// Echo1 prints its command-line arguments.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var s, sep string
|
||||
for i := 1; i < len(os.Args); i++ {
|
||||
s += sep + os.Args[i]
|
||||
sep = " "
|
||||
}
|
||||
fmt.Println(s)
|
||||
}
|
||||
|
||||
//!-
|
||||
24
vendor/gopl.io/ch1/echo2/main.go
generated
vendored
Normal file
24
vendor/gopl.io/ch1/echo2/main.go
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 6.
|
||||
//!+
|
||||
|
||||
// Echo2 prints its command-line arguments.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s, sep := "", ""
|
||||
for _, arg := range os.Args[1:] {
|
||||
s += sep + arg
|
||||
sep = " "
|
||||
}
|
||||
fmt.Println(s)
|
||||
}
|
||||
|
||||
//!-
|
||||
20
vendor/gopl.io/ch1/echo3/main.go
generated
vendored
Normal file
20
vendor/gopl.io/ch1/echo3/main.go
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 8.
|
||||
|
||||
// Echo3 prints its command-line arguments.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
//!+
|
||||
func main() {
|
||||
fmt.Println(strings.Join(os.Args[1:], " "))
|
||||
}
|
||||
|
||||
//!-
|
||||
34
vendor/gopl.io/ch1/fetch/main.go
generated
vendored
Normal file
34
vendor/gopl.io/ch1/fetch/main.go
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 16.
|
||||
//!+
|
||||
|
||||
// Fetch prints the content found at each specified URL.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
for _, url := range os.Args[1:] {
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "fetch: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
b, err := ioutil.ReadAll(resp.Body)
|
||||
resp.Body.Close()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "fetch: reading %s: %v\n", url, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("%s", b)
|
||||
}
|
||||
}
|
||||
|
||||
//!-
|
||||
49
vendor/gopl.io/ch1/fetchall/main.go
generated
vendored
Normal file
49
vendor/gopl.io/ch1/fetchall/main.go
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 17.
|
||||
//!+
|
||||
|
||||
// Fetchall fetches URLs in parallel and reports their times and sizes.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
start := time.Now()
|
||||
ch := make(chan string)
|
||||
for _, url := range os.Args[1:] {
|
||||
go fetch(url, ch) // start a goroutine
|
||||
}
|
||||
for range os.Args[1:] {
|
||||
fmt.Println(<-ch) // receive from channel ch
|
||||
}
|
||||
fmt.Printf("%.2fs elapsed\n", time.Since(start).Seconds())
|
||||
}
|
||||
|
||||
func fetch(url string, ch chan<- string) {
|
||||
start := time.Now()
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
ch <- fmt.Sprint(err) // send to channel ch
|
||||
return
|
||||
}
|
||||
|
||||
nbytes, err := io.Copy(ioutil.Discard, resp.Body)
|
||||
resp.Body.Close() // don't leak resources
|
||||
if err != nil {
|
||||
ch <- fmt.Sprintf("while reading %s: %v", url, err)
|
||||
return
|
||||
}
|
||||
secs := time.Since(start).Seconds()
|
||||
ch <- fmt.Sprintf("%.2fs %7d %s", secs, nbytes, url)
|
||||
}
|
||||
|
||||
//!-
|
||||
16
vendor/gopl.io/ch1/helloworld/main.go
generated
vendored
Normal file
16
vendor/gopl.io/ch1/helloworld/main.go
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 1.
|
||||
|
||||
// Helloworld is our first Go program.
|
||||
//!+
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
fmt.Println("Hello, 世界")
|
||||
}
|
||||
|
||||
//!-
|
||||
86
vendor/gopl.io/ch1/lissajous/main.go
generated
vendored
Normal file
86
vendor/gopl.io/ch1/lissajous/main.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/
|
||||
|
||||
// Run with "web" command-line argument for web server.
|
||||
// See page 13.
|
||||
//!+main
|
||||
|
||||
// Lissajous generates GIF animations of random Lissajous figures.
|
||||
package main
|
||||
|
||||
import (
|
||||
"image"
|
||||
"image/color"
|
||||
"image/gif"
|
||||
"io"
|
||||
"math"
|
||||
"math/rand"
|
||||
"os"
|
||||
)
|
||||
|
||||
//!-main
|
||||
// Packages not needed by version in book.
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
//!+main
|
||||
|
||||
var palette = []color.Color{color.White, color.Black}
|
||||
|
||||
const (
|
||||
whiteIndex = 0 // first color in palette
|
||||
blackIndex = 1 // next color in palette
|
||||
)
|
||||
|
||||
func main() {
|
||||
//!-main
|
||||
// The sequence of images is deterministic unless we seed
|
||||
// the pseudo-random number generator using the current time.
|
||||
// Thanks to Randall McPherson for pointing out the omission.
|
||||
rand.Seed(time.Now().UTC().UnixNano())
|
||||
|
||||
if len(os.Args) > 1 && os.Args[1] == "web" {
|
||||
//!+http
|
||||
handler := func(w http.ResponseWriter, r *http.Request) {
|
||||
lissajous(w)
|
||||
}
|
||||
http.HandleFunc("/", handler)
|
||||
//!-http
|
||||
log.Fatal(http.ListenAndServe("localhost:8000", nil))
|
||||
return
|
||||
}
|
||||
//!+main
|
||||
lissajous(os.Stdout)
|
||||
}
|
||||
|
||||
func lissajous(out io.Writer) {
|
||||
const (
|
||||
cycles = 5 // number of complete x oscillator revolutions
|
||||
res = 0.001 // angular resolution
|
||||
size = 100 // image canvas covers [-size..+size]
|
||||
nframes = 64 // number of animation frames
|
||||
delay = 8 // delay between frames in 10ms units
|
||||
)
|
||||
freq := rand.Float64() * 3.0 // relative frequency of y oscillator
|
||||
anim := gif.GIF{LoopCount: nframes}
|
||||
phase := 0.0 // phase difference
|
||||
for i := 0; i < nframes; i++ {
|
||||
rect := image.Rect(0, 0, 2*size+1, 2*size+1)
|
||||
img := image.NewPaletted(rect, palette)
|
||||
for t := 0.0; t < cycles*2*math.Pi; t += res {
|
||||
x := math.Sin(t)
|
||||
y := math.Sin(t*freq + phase)
|
||||
img.SetColorIndex(size+int(x*size+0.5), size+int(y*size+0.5),
|
||||
blackIndex)
|
||||
}
|
||||
phase += 0.1
|
||||
anim.Delay = append(anim.Delay, delay)
|
||||
anim.Image = append(anim.Image, img)
|
||||
}
|
||||
gif.EncodeAll(out, &anim) // NOTE: ignoring encoding errors
|
||||
}
|
||||
|
||||
//!-main
|
||||
26
vendor/gopl.io/ch1/server1/main.go
generated
vendored
Normal file
26
vendor/gopl.io/ch1/server1/main.go
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 19.
|
||||
//!+
|
||||
|
||||
// Server1 is a minimal "echo" server.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func main() {
|
||||
http.HandleFunc("/", handler) // each request calls handler
|
||||
log.Fatal(http.ListenAndServe("localhost:8000", nil))
|
||||
}
|
||||
|
||||
// handler echoes the Path component of the requested URL.
|
||||
func handler(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, "URL.Path = %q\n", r.URL.Path)
|
||||
}
|
||||
|
||||
//!-
|
||||
41
vendor/gopl.io/ch1/server2/main.go
generated
vendored
Normal file
41
vendor/gopl.io/ch1/server2/main.go
generated
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 20.
|
||||
//!+
|
||||
|
||||
// Server2 is a minimal "echo" and counter server.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var mu sync.Mutex
|
||||
var count int
|
||||
|
||||
func main() {
|
||||
http.HandleFunc("/", handler)
|
||||
http.HandleFunc("/count", counter)
|
||||
log.Fatal(http.ListenAndServe("localhost:8000", nil))
|
||||
}
|
||||
|
||||
// handler echoes the Path component of the requested URL.
|
||||
func handler(w http.ResponseWriter, r *http.Request) {
|
||||
mu.Lock()
|
||||
count++
|
||||
mu.Unlock()
|
||||
fmt.Fprintf(w, "URL.Path = %q\n", r.URL.Path)
|
||||
}
|
||||
|
||||
// counter echoes the number of calls so far.
|
||||
func counter(w http.ResponseWriter, r *http.Request) {
|
||||
mu.Lock()
|
||||
fmt.Fprintf(w, "Count %d\n", count)
|
||||
mu.Unlock()
|
||||
}
|
||||
|
||||
//!-
|
||||
37
vendor/gopl.io/ch1/server3/main.go
generated
vendored
Normal file
37
vendor/gopl.io/ch1/server3/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 21.
|
||||
|
||||
// Server3 is an "echo" server that displays request parameters.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func main() {
|
||||
http.HandleFunc("/", handler)
|
||||
log.Fatal(http.ListenAndServe("localhost:8000", nil))
|
||||
}
|
||||
|
||||
//!+handler
|
||||
// handler echoes the HTTP request.
|
||||
func handler(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, "%s %s %s\n", r.Method, r.URL, r.Proto)
|
||||
for k, v := range r.Header {
|
||||
fmt.Fprintf(w, "Header[%q] = %q\n", k, v)
|
||||
}
|
||||
fmt.Fprintf(w, "Host = %q\n", r.Host)
|
||||
fmt.Fprintf(w, "RemoteAddr = %q\n", r.RemoteAddr)
|
||||
if err := r.ParseForm(); err != nil {
|
||||
log.Print(err)
|
||||
}
|
||||
for k, v := range r.Form {
|
||||
fmt.Fprintf(w, "Form[%q] = %q\n", k, v)
|
||||
}
|
||||
}
|
||||
|
||||
//!-handler
|
||||
19
vendor/gopl.io/ch10/cross/main.go
generated
vendored
Normal file
19
vendor/gopl.io/ch10/cross/main.go
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 295.
|
||||
|
||||
// The cross command prints the values of GOOS and GOARCH for this target.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
//!+
|
||||
func main() {
|
||||
fmt.Println(runtime.GOOS, runtime.GOARCH)
|
||||
}
|
||||
|
||||
//!-
|
||||
52
vendor/gopl.io/ch10/jpeg/main.go
generated
vendored
Normal file
52
vendor/gopl.io/ch10/jpeg/main.go
generated
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 287.
|
||||
|
||||
//!+main
|
||||
|
||||
// The jpeg command reads a PNG image from the standard input
|
||||
// and writes it as a JPEG image to the standard output.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image"
|
||||
"image/jpeg"
|
||||
_ "image/png" // register PNG decoder
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if err := toJPEG(os.Stdin, os.Stdout); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "jpeg: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func toJPEG(in io.Reader, out io.Writer) error {
|
||||
img, kind, err := image.Decode(in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintln(os.Stderr, "Input format =", kind)
|
||||
return jpeg.Encode(out, img, &jpeg.Options{Quality: 95})
|
||||
}
|
||||
|
||||
//!-main
|
||||
|
||||
/*
|
||||
//!+with
|
||||
$ go build gopl.io/ch3/mandelbrot
|
||||
$ go build gopl.io/ch10/jpeg
|
||||
$ ./mandelbrot | ./jpeg >mandelbrot.jpg
|
||||
Input format = png
|
||||
//!-with
|
||||
|
||||
//!+without
|
||||
$ go build gopl.io/ch10/jpeg
|
||||
$ ./mandelbrot | ./jpeg >mandelbrot.jpg
|
||||
jpeg: image: unknown format
|
||||
//!-without
|
||||
*/
|
||||
41
vendor/gopl.io/ch11/echo/echo.go
generated
vendored
Normal file
41
vendor/gopl.io/ch11/echo/echo.go
generated
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 308.
|
||||
//!+
|
||||
|
||||
// Echo prints its command-line arguments.
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
n = flag.Bool("n", false, "omit trailing newline")
|
||||
s = flag.String("s", " ", "separator")
|
||||
)
|
||||
|
||||
var out io.Writer = os.Stdout // modified during testing
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
if err := echo(!*n, *s, flag.Args()); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "echo: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func echo(newline bool, sep string, args []string) error {
|
||||
fmt.Fprint(out, strings.Join(args, sep))
|
||||
if newline {
|
||||
fmt.Fprintln(out)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//!-
|
||||
45
vendor/gopl.io/ch11/echo/echo_test.go
generated
vendored
Normal file
45
vendor/gopl.io/ch11/echo/echo_test.go
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// Test of echo command. Run with: go test gopl.io/ch11/echo
|
||||
|
||||
//!+
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestEcho(t *testing.T) {
|
||||
var tests = []struct {
|
||||
newline bool
|
||||
sep string
|
||||
args []string
|
||||
want string
|
||||
}{
|
||||
{true, "", []string{}, "\n"},
|
||||
{false, "", []string{}, ""},
|
||||
{true, "\t", []string{"one", "two", "three"}, "one\ttwo\tthree\n"},
|
||||
{true, ",", []string{"a", "b", "c"}, "a,b,c\n"},
|
||||
{false, ":", []string{"1", "2", "3"}, "1:2:3"},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
descr := fmt.Sprintf("echo(%v, %q, %q)",
|
||||
test.newline, test.sep, test.args)
|
||||
|
||||
out = new(bytes.Buffer) // captured output
|
||||
if err := echo(test.newline, test.sep, test.args); err != nil {
|
||||
t.Errorf("%s failed: %v", descr, err)
|
||||
continue
|
||||
}
|
||||
got := out.(*bytes.Buffer).String()
|
||||
if got != test.want {
|
||||
t.Errorf("%s = %q, want %q", descr, got, test.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//!-
|
||||
45
vendor/gopl.io/ch11/storage1/storage.go
generated
vendored
Normal file
45
vendor/gopl.io/ch11/storage1/storage.go
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 311.
|
||||
|
||||
// Package storage is part of a hypothetical cloud storage server.
|
||||
//!+main
|
||||
package storage
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/smtp"
|
||||
)
|
||||
|
||||
var usage = make(map[string]int64)
|
||||
|
||||
func bytesInUse(username string) int64 { return usage[username] }
|
||||
|
||||
// Email sender configuration.
|
||||
// NOTE: never put passwords in source code!
|
||||
const sender = "notifications@example.com"
|
||||
const password = "correcthorsebatterystaple"
|
||||
const hostname = "smtp.example.com"
|
||||
|
||||
const template = `Warning: you are using %d bytes of storage,
|
||||
%d%% of your quota.`
|
||||
|
||||
func CheckQuota(username string) {
|
||||
used := bytesInUse(username)
|
||||
const quota = 1000000000 // 1GB
|
||||
percent := 100 * used / quota
|
||||
if percent < 90 {
|
||||
return // OK
|
||||
}
|
||||
msg := fmt.Sprintf(template, used, percent)
|
||||
auth := smtp.PlainAuth("", sender, password, hostname)
|
||||
err := smtp.SendMail(hostname+":587", auth, sender,
|
||||
[]string{username}, []byte(msg))
|
||||
if err != nil {
|
||||
log.Printf("smtp.SendMail(%s) failed: %s", username, err)
|
||||
}
|
||||
}
|
||||
|
||||
//!-main
|
||||
53
vendor/gopl.io/ch11/storage2/quota_test.go
generated
vendored
Normal file
53
vendor/gopl.io/ch11/storage2/quota_test.go
generated
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
//!+test
|
||||
package storage
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCheckQuotaNotifiesUser(t *testing.T) {
|
||||
var notifiedUser, notifiedMsg string
|
||||
notifyUser = func(user, msg string) {
|
||||
notifiedUser, notifiedMsg = user, msg
|
||||
}
|
||||
|
||||
const user = "joe@example.org"
|
||||
usage[user] = 980000000 // simulate a 980MB-used condition
|
||||
|
||||
CheckQuota(user)
|
||||
if notifiedUser == "" && notifiedMsg == "" {
|
||||
t.Fatalf("notifyUser not called")
|
||||
}
|
||||
if notifiedUser != user {
|
||||
t.Errorf("wrong user (%s) notified, want %s",
|
||||
notifiedUser, user)
|
||||
}
|
||||
const wantSubstring = "98% of your quota"
|
||||
if !strings.Contains(notifiedMsg, wantSubstring) {
|
||||
t.Errorf("unexpected notification message <<%s>>, "+
|
||||
"want substring %q", notifiedMsg, wantSubstring)
|
||||
}
|
||||
}
|
||||
|
||||
//!-test
|
||||
|
||||
/*
|
||||
//!+defer
|
||||
func TestCheckQuotaNotifiesUser(t *testing.T) {
|
||||
// Save and restore original notifyUser.
|
||||
saved := notifyUser
|
||||
defer func() { notifyUser = saved }()
|
||||
|
||||
// Install the test's fake notifyUser.
|
||||
var notifiedUser, notifiedMsg string
|
||||
notifyUser = func(user, msg string) {
|
||||
notifiedUser, notifiedMsg = user, msg
|
||||
}
|
||||
// ...rest of test...
|
||||
}
|
||||
//!-defer
|
||||
*/
|
||||
49
vendor/gopl.io/ch11/storage2/storage.go
generated
vendored
Normal file
49
vendor/gopl.io/ch11/storage2/storage.go
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 312.
|
||||
|
||||
// Package storage is part of a hypothetical cloud storage server.
|
||||
package storage
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/smtp"
|
||||
)
|
||||
|
||||
var usage = make(map[string]int64)
|
||||
|
||||
func bytesInUse(username string) int64 { return usage[username] }
|
||||
|
||||
// E-mail sender configuration.
|
||||
// NOTE: never put passwords in source code!
|
||||
const sender = "notifications@example.com"
|
||||
const password = "correcthorsebatterystaple"
|
||||
const hostname = "smtp.example.com"
|
||||
|
||||
const template = `Warning: you are using %d bytes of storage,
|
||||
%d%% of your quota.`
|
||||
|
||||
//!+factored
|
||||
var notifyUser = func(username, msg string) {
|
||||
auth := smtp.PlainAuth("", sender, password, hostname)
|
||||
err := smtp.SendMail(hostname+":587", auth, sender,
|
||||
[]string{username}, []byte(msg))
|
||||
if err != nil {
|
||||
log.Printf("smtp.SendEmail(%s) failed: %s", username, err)
|
||||
}
|
||||
}
|
||||
|
||||
func CheckQuota(username string) {
|
||||
used := bytesInUse(username)
|
||||
const quota = 1000000000 // 1GB
|
||||
percent := 100 * used / quota
|
||||
if percent < 90 {
|
||||
return // OK
|
||||
}
|
||||
msg := fmt.Sprintf(template, used, percent)
|
||||
notifyUser(username, msg)
|
||||
}
|
||||
|
||||
//!-factored
|
||||
21
vendor/gopl.io/ch11/word1/word.go
generated
vendored
Normal file
21
vendor/gopl.io/ch11/word1/word.go
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 303.
|
||||
//!+
|
||||
|
||||
// Package word provides utilities for word games.
|
||||
package word
|
||||
|
||||
// IsPalindrome reports whether s reads the same forward and backward.
|
||||
// (Our first attempt.)
|
||||
func IsPalindrome(s string) bool {
|
||||
for i := range s {
|
||||
if s[i] != s[len(s)-1-i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
//!-
|
||||
43
vendor/gopl.io/ch11/word1/word_test.go
generated
vendored
Normal file
43
vendor/gopl.io/ch11/word1/word_test.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/
|
||||
|
||||
//!+test
|
||||
package word
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestPalindrome(t *testing.T) {
|
||||
if !IsPalindrome("detartrated") {
|
||||
t.Error(`IsPalindrome("detartrated") = false`)
|
||||
}
|
||||
if !IsPalindrome("kayak") {
|
||||
t.Error(`IsPalindrome("kayak") = false`)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNonPalindrome(t *testing.T) {
|
||||
if IsPalindrome("palindrome") {
|
||||
t.Error(`IsPalindrome("palindrome") = true`)
|
||||
}
|
||||
}
|
||||
|
||||
//!-test
|
||||
|
||||
// The tests below are expected to fail.
|
||||
// See package gopl.io/ch11/word2 for the fix.
|
||||
|
||||
//!+more
|
||||
func TestFrenchPalindrome(t *testing.T) {
|
||||
if !IsPalindrome("été") {
|
||||
t.Error(`IsPalindrome("été") = false`)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCanalPalindrome(t *testing.T) {
|
||||
input := "A man, a plan, a canal: Panama"
|
||||
if !IsPalindrome(input) {
|
||||
t.Errorf(`IsPalindrome(%q) = false`, input)
|
||||
}
|
||||
}
|
||||
|
||||
//!-more
|
||||
29
vendor/gopl.io/ch11/word2/word.go
generated
vendored
Normal file
29
vendor/gopl.io/ch11/word2/word.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 305.
|
||||
//!+
|
||||
|
||||
// Package word provides utilities for word games.
|
||||
package word
|
||||
|
||||
import "unicode"
|
||||
|
||||
// IsPalindrome reports whether s reads the same forward and backward.
|
||||
// Letter case is ignored, as are non-letters.
|
||||
func IsPalindrome(s string) bool {
|
||||
var letters []rune
|
||||
for _, r := range s {
|
||||
if unicode.IsLetter(r) {
|
||||
letters = append(letters, unicode.ToLower(r))
|
||||
}
|
||||
}
|
||||
for i := range letters {
|
||||
if letters[i] != letters[len(letters)-1-i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
//!-
|
||||
148
vendor/gopl.io/ch11/word2/word_test.go
generated
vendored
Normal file
148
vendor/gopl.io/ch11/word2/word_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/
|
||||
|
||||
package word
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
//!+bench
|
||||
|
||||
import "testing"
|
||||
|
||||
//!-bench
|
||||
|
||||
//!+test
|
||||
func TestIsPalindrome(t *testing.T) {
|
||||
var tests = []struct {
|
||||
input string
|
||||
want bool
|
||||
}{
|
||||
{"", true},
|
||||
{"a", true},
|
||||
{"aa", true},
|
||||
{"ab", false},
|
||||
{"kayak", true},
|
||||
{"detartrated", true},
|
||||
{"A man, a plan, a canal: Panama", true},
|
||||
{"Evil I did dwell; lewd did I live.", true},
|
||||
{"Able was I ere I saw Elba", true},
|
||||
{"été", true},
|
||||
{"Et se resservir, ivresse reste.", true},
|
||||
{"palindrome", false}, // non-palindrome
|
||||
{"desserts", false}, // semi-palindrome
|
||||
}
|
||||
for _, test := range tests {
|
||||
if got := IsPalindrome(test.input); got != test.want {
|
||||
t.Errorf("IsPalindrome(%q) = %v", test.input, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//!-test
|
||||
|
||||
//!+bench
|
||||
func BenchmarkIsPalindrome(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
IsPalindrome("A man, a plan, a canal: Panama")
|
||||
}
|
||||
}
|
||||
|
||||
//!-bench
|
||||
|
||||
//!+example
|
||||
|
||||
func ExampleIsPalindrome() {
|
||||
fmt.Println(IsPalindrome("A man, a plan, a canal: Panama"))
|
||||
fmt.Println(IsPalindrome("palindrome"))
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
}
|
||||
|
||||
//!-example
|
||||
|
||||
/*
|
||||
//!+random
|
||||
import "math/rand"
|
||||
|
||||
//!-random
|
||||
*/
|
||||
|
||||
//!+random
|
||||
// randomPalindrome returns a palindrome whose length and contents
|
||||
// are derived from the pseudo-random number generator rng.
|
||||
func randomPalindrome(rng *rand.Rand) string {
|
||||
n := rng.Intn(25) // random length up to 24
|
||||
runes := make([]rune, n)
|
||||
for i := 0; i < (n+1)/2; i++ {
|
||||
r := rune(rng.Intn(0x1000)) // random rune up to '\u0999'
|
||||
runes[i] = r
|
||||
runes[n-1-i] = r
|
||||
}
|
||||
return string(runes)
|
||||
}
|
||||
|
||||
func TestRandomPalindromes(t *testing.T) {
|
||||
// Initialize a pseudo-random number generator.
|
||||
seed := time.Now().UTC().UnixNano()
|
||||
t.Logf("Random seed: %d", seed)
|
||||
rng := rand.New(rand.NewSource(seed))
|
||||
|
||||
for i := 0; i < 1000; i++ {
|
||||
p := randomPalindrome(rng)
|
||||
if !IsPalindrome(p) {
|
||||
t.Errorf("IsPalindrome(%q) = false", p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//!-random
|
||||
|
||||
/*
|
||||
// Answer for Exercicse 11.1: Modify randomPalindrome to exercise
|
||||
// IsPalindrome's handling of punctuation and spaces.
|
||||
|
||||
// WARNING: the conversion r -> upper -> lower doesn't preserve
|
||||
// the value of r in some cases, e.g., µ Μ, ſ S, ı I
|
||||
|
||||
// randomPalindrome returns a palindrome whose length and contents
|
||||
// are derived from the pseudo-random number generator rng.
|
||||
func randomNoisyPalindrome(rng *rand.Rand) string {
|
||||
n := rng.Intn(25) // random length up to 24
|
||||
runes := make([]rune, n)
|
||||
for i := 0; i < (n+1)/2; i++ {
|
||||
r := rune(rng.Intn(0x200)) // random rune up to \u99
|
||||
runes[i] = r
|
||||
r1 := r
|
||||
if unicode.IsLetter(r) && unicode.IsLower(r) {
|
||||
r = unicode.ToUpper(r)
|
||||
if unicode.ToLower(r) != r1 {
|
||||
fmt.Printf("cap? %c %c\n", r1, r)
|
||||
}
|
||||
}
|
||||
runes[n-1-i] = r
|
||||
}
|
||||
return "?" + string(runes) + "!"
|
||||
}
|
||||
|
||||
func TestRandomNoisyPalindromes(t *testing.T) {
|
||||
// Initialize a pseudo-random number generator.
|
||||
seed := time.Now().UTC().UnixNano()
|
||||
t.Logf("Random seed: %d", seed)
|
||||
rng := rand.New(rand.NewSource(seed))
|
||||
|
||||
n := 0
|
||||
for i := 0; i < 1000; i++ {
|
||||
p := randomNoisyPalindrome(rng)
|
||||
if !IsPalindrome(p) {
|
||||
t.Errorf("IsNoisyPalindrome(%q) = false", p)
|
||||
n++
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "fail = %d\n", n)
|
||||
}
|
||||
*/
|
||||
90
vendor/gopl.io/ch12/display/display.go
generated
vendored
Normal file
90
vendor/gopl.io/ch12/display/display.go
generated
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 333.
|
||||
|
||||
// Package display provides a means to display structured data.
|
||||
package display
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
//!+Display
|
||||
|
||||
func Display(name string, x interface{}) {
|
||||
fmt.Printf("Display %s (%T):\n", name, x)
|
||||
display(name, reflect.ValueOf(x))
|
||||
}
|
||||
|
||||
//!-Display
|
||||
|
||||
// formatAtom formats a value without inspecting its internal structure.
|
||||
// It is a copy of the the function in gopl.io/ch11/format.
|
||||
func formatAtom(v reflect.Value) string {
|
||||
switch v.Kind() {
|
||||
case reflect.Invalid:
|
||||
return "invalid"
|
||||
case reflect.Int, reflect.Int8, reflect.Int16,
|
||||
reflect.Int32, reflect.Int64:
|
||||
return strconv.FormatInt(v.Int(), 10)
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16,
|
||||
reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
return strconv.FormatUint(v.Uint(), 10)
|
||||
// ...floating-point and complex cases omitted for brevity...
|
||||
case reflect.Bool:
|
||||
if v.Bool() {
|
||||
return "true"
|
||||
}
|
||||
return "false"
|
||||
case reflect.String:
|
||||
return strconv.Quote(v.String())
|
||||
case reflect.Chan, reflect.Func, reflect.Ptr,
|
||||
reflect.Slice, reflect.Map:
|
||||
return v.Type().String() + " 0x" +
|
||||
strconv.FormatUint(uint64(v.Pointer()), 16)
|
||||
default: // reflect.Array, reflect.Struct, reflect.Interface
|
||||
return v.Type().String() + " value"
|
||||
}
|
||||
}
|
||||
|
||||
//!+display
|
||||
func display(path string, v reflect.Value) {
|
||||
switch v.Kind() {
|
||||
case reflect.Invalid:
|
||||
fmt.Printf("%s = invalid\n", path)
|
||||
case reflect.Slice, reflect.Array:
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
display(fmt.Sprintf("%s[%d]", path, i), v.Index(i))
|
||||
}
|
||||
case reflect.Struct:
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
fieldPath := fmt.Sprintf("%s.%s", path, v.Type().Field(i).Name)
|
||||
display(fieldPath, v.Field(i))
|
||||
}
|
||||
case reflect.Map:
|
||||
for _, key := range v.MapKeys() {
|
||||
display(fmt.Sprintf("%s[%s]", path,
|
||||
formatAtom(key)), v.MapIndex(key))
|
||||
}
|
||||
case reflect.Ptr:
|
||||
if v.IsNil() {
|
||||
fmt.Printf("%s = nil\n", path)
|
||||
} else {
|
||||
display(fmt.Sprintf("(*%s)", path), v.Elem())
|
||||
}
|
||||
case reflect.Interface:
|
||||
if v.IsNil() {
|
||||
fmt.Printf("%s = nil\n", path)
|
||||
} else {
|
||||
fmt.Printf("%s.type = %s\n", path, v.Elem().Type())
|
||||
display(path+".value", v.Elem())
|
||||
}
|
||||
default: // basic types, channels, funcs
|
||||
fmt.Printf("%s = %s\n", path, formatAtom(v))
|
||||
}
|
||||
}
|
||||
|
||||
//!-display
|
||||
259
vendor/gopl.io/ch12/display/display_test.go
generated
vendored
Normal file
259
vendor/gopl.io/ch12/display/display_test.go
generated
vendored
Normal file
@@ -0,0 +1,259 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
package display
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"reflect"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"gopl.io/ch7/eval"
|
||||
)
|
||||
|
||||
// NOTE: we can't use !+..!- comments to excerpt these tests
|
||||
// into the book because it defeats the Example mechanism,
|
||||
// which requires the // Output comment to be at the end
|
||||
// of the function.
|
||||
|
||||
func Example_expr() {
|
||||
e, _ := eval.Parse("sqrt(A / pi)")
|
||||
Display("e", e)
|
||||
// Output:
|
||||
// Display e (eval.call):
|
||||
// e.fn = "sqrt"
|
||||
// e.args[0].type = eval.binary
|
||||
// e.args[0].value.op = 47
|
||||
// e.args[0].value.x.type = eval.Var
|
||||
// e.args[0].value.x.value = "A"
|
||||
// e.args[0].value.y.type = eval.Var
|
||||
// e.args[0].value.y.value = "pi"
|
||||
}
|
||||
|
||||
func Example_slice() {
|
||||
Display("slice", []*int{new(int), nil})
|
||||
// Output:
|
||||
// Display slice ([]*int):
|
||||
// (*slice[0]) = 0
|
||||
// slice[1] = nil
|
||||
}
|
||||
|
||||
func Example_nilInterface() {
|
||||
var w io.Writer
|
||||
Display("w", w)
|
||||
// Output:
|
||||
// Display w (<nil>):
|
||||
// w = invalid
|
||||
}
|
||||
|
||||
func Example_ptrToInterface() {
|
||||
var w io.Writer
|
||||
Display("&w", &w)
|
||||
// Output:
|
||||
// Display &w (*io.Writer):
|
||||
// (*&w) = nil
|
||||
}
|
||||
|
||||
func Example_struct() {
|
||||
Display("x", struct{ x interface{} }{3})
|
||||
// Output:
|
||||
// Display x (struct { x interface {} }):
|
||||
// x.x.type = int
|
||||
// x.x.value = 3
|
||||
}
|
||||
|
||||
func Example_interface() {
|
||||
var i interface{} = 3
|
||||
Display("i", i)
|
||||
// Output:
|
||||
// Display i (int):
|
||||
// i = 3
|
||||
}
|
||||
|
||||
func Example_ptrToInterface2() {
|
||||
var i interface{} = 3
|
||||
Display("&i", &i)
|
||||
// Output:
|
||||
// Display &i (*interface {}):
|
||||
// (*&i).type = int
|
||||
// (*&i).value = 3
|
||||
}
|
||||
|
||||
func Example_array() {
|
||||
Display("x", [1]interface{}{3})
|
||||
// Output:
|
||||
// Display x ([1]interface {}):
|
||||
// x[0].type = int
|
||||
// x[0].value = 3
|
||||
}
|
||||
|
||||
func Example_movie() {
|
||||
//!+movie
|
||||
type Movie struct {
|
||||
Title, Subtitle string
|
||||
Year int
|
||||
Color bool
|
||||
Actor map[string]string
|
||||
Oscars []string
|
||||
Sequel *string
|
||||
}
|
||||
//!-movie
|
||||
//!+strangelove
|
||||
strangelove := Movie{
|
||||
Title: "Dr. Strangelove",
|
||||
Subtitle: "How I Learned to Stop Worrying and Love the Bomb",
|
||||
Year: 1964,
|
||||
Color: false,
|
||||
Actor: map[string]string{
|
||||
"Dr. Strangelove": "Peter Sellers",
|
||||
"Grp. Capt. Lionel Mandrake": "Peter Sellers",
|
||||
"Pres. Merkin Muffley": "Peter Sellers",
|
||||
"Gen. Buck Turgidson": "George C. Scott",
|
||||
"Brig. Gen. Jack D. Ripper": "Sterling Hayden",
|
||||
`Maj. T.J. "King" Kong`: "Slim Pickens",
|
||||
},
|
||||
|
||||
Oscars: []string{
|
||||
"Best Actor (Nomin.)",
|
||||
"Best Adapted Screenplay (Nomin.)",
|
||||
"Best Director (Nomin.)",
|
||||
"Best Picture (Nomin.)",
|
||||
},
|
||||
}
|
||||
//!-strangelove
|
||||
Display("strangelove", strangelove)
|
||||
|
||||
// We don't use an Output: comment since displaying
|
||||
// a map is nondeterministic.
|
||||
/*
|
||||
//!+output
|
||||
Display strangelove (display.Movie):
|
||||
strangelove.Title = "Dr. Strangelove"
|
||||
strangelove.Subtitle = "How I Learned to Stop Worrying and Love the Bomb"
|
||||
strangelove.Year = 1964
|
||||
strangelove.Color = false
|
||||
strangelove.Actor["Gen. Buck Turgidson"] = "George C. Scott"
|
||||
strangelove.Actor["Brig. Gen. Jack D. Ripper"] = "Sterling Hayden"
|
||||
strangelove.Actor["Maj. T.J. \"King\" Kong"] = "Slim Pickens"
|
||||
strangelove.Actor["Dr. Strangelove"] = "Peter Sellers"
|
||||
strangelove.Actor["Grp. Capt. Lionel Mandrake"] = "Peter Sellers"
|
||||
strangelove.Actor["Pres. Merkin Muffley"] = "Peter Sellers"
|
||||
strangelove.Oscars[0] = "Best Actor (Nomin.)"
|
||||
strangelove.Oscars[1] = "Best Adapted Screenplay (Nomin.)"
|
||||
strangelove.Oscars[2] = "Best Director (Nomin.)"
|
||||
strangelove.Oscars[3] = "Best Picture (Nomin.)"
|
||||
strangelove.Sequel = nil
|
||||
//!-output
|
||||
*/
|
||||
}
|
||||
|
||||
// This test ensures that the program terminates without crashing.
|
||||
func Test(t *testing.T) {
|
||||
// Some other values (YMMV)
|
||||
Display("os.Stderr", os.Stderr)
|
||||
// Output:
|
||||
// Display os.Stderr (*os.File):
|
||||
// (*(*os.Stderr).file).fd = 2
|
||||
// (*(*os.Stderr).file).name = "/dev/stderr"
|
||||
// (*(*os.Stderr).file).nepipe = 0
|
||||
|
||||
var w io.Writer = os.Stderr
|
||||
Display("&w", &w)
|
||||
// Output:
|
||||
// Display &w (*io.Writer):
|
||||
// (*&w).type = *os.File
|
||||
// (*(*(*&w).value).file).fd = 2
|
||||
// (*(*(*&w).value).file).name = "/dev/stderr"
|
||||
// (*(*(*&w).value).file).nepipe = 0
|
||||
|
||||
var locker sync.Locker = new(sync.Mutex)
|
||||
Display("(&locker)", &locker)
|
||||
// Output:
|
||||
// Display (&locker) (*sync.Locker):
|
||||
// (*(&locker)).type = *sync.Mutex
|
||||
// (*(*(&locker)).value).state = 0
|
||||
// (*(*(&locker)).value).sema = 0
|
||||
|
||||
Display("locker", locker)
|
||||
// Output:
|
||||
// Display locker (*sync.Mutex):
|
||||
// (*locker).state = 0
|
||||
// (*locker).sema = 0
|
||||
// (*(&locker)) = nil
|
||||
|
||||
locker = nil
|
||||
Display("(&locker)", &locker)
|
||||
// Output:
|
||||
// Display (&locker) (*sync.Locker):
|
||||
// (*(&locker)) = nil
|
||||
|
||||
ips, _ := net.LookupHost("golang.org")
|
||||
Display("ips", ips)
|
||||
// Output:
|
||||
// Display ips ([]string):
|
||||
// ips[0] = "173.194.68.141"
|
||||
// ips[1] = "2607:f8b0:400d:c06::8d"
|
||||
|
||||
// Even metarecursion! (YMMV)
|
||||
Display("rV", reflect.ValueOf(os.Stderr))
|
||||
// Output:
|
||||
// Display rV (reflect.Value):
|
||||
// (*rV.typ).size = 8
|
||||
// (*rV.typ).ptrdata = 8
|
||||
// (*rV.typ).hash = 871609668
|
||||
// (*rV.typ)._ = 0
|
||||
// ...
|
||||
|
||||
// a pointer that points to itself
|
||||
type P *P
|
||||
var p P
|
||||
p = &p
|
||||
if false {
|
||||
Display("p", p)
|
||||
// Output:
|
||||
// Display p (display.P):
|
||||
// ...stuck, no output...
|
||||
}
|
||||
|
||||
// a map that contains itself
|
||||
type M map[string]M
|
||||
m := make(M)
|
||||
m[""] = m
|
||||
if false {
|
||||
Display("m", m)
|
||||
// Output:
|
||||
// Display m (display.M):
|
||||
// ...stuck, no output...
|
||||
}
|
||||
|
||||
// a slice that contains itself
|
||||
type S []S
|
||||
s := make(S, 1)
|
||||
s[0] = s
|
||||
if false {
|
||||
Display("s", s)
|
||||
// Output:
|
||||
// Display s (display.S):
|
||||
// ...stuck, no output...
|
||||
}
|
||||
|
||||
// a linked list that eats its own tail
|
||||
type Cycle struct {
|
||||
Value int
|
||||
Tail *Cycle
|
||||
}
|
||||
var c Cycle
|
||||
c = Cycle{42, &c}
|
||||
if false {
|
||||
Display("c", c)
|
||||
// Output:
|
||||
// Display c (display.Cycle):
|
||||
// c.Value = 42
|
||||
// (*c.Tail).Value = 42
|
||||
// (*(*c.Tail).Tail).Value = 42
|
||||
// ...ad infinitum...
|
||||
}
|
||||
}
|
||||
44
vendor/gopl.io/ch12/format/format.go
generated
vendored
Normal file
44
vendor/gopl.io/ch12/format/format.go
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 332.
|
||||
|
||||
// Package format provides an Any function that can format any value.
|
||||
//!+
|
||||
package format
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Any formats any value as a string.
|
||||
func Any(value interface{}) string {
|
||||
return formatAtom(reflect.ValueOf(value))
|
||||
}
|
||||
|
||||
// formatAtom formats a value without inspecting its internal structure.
|
||||
func formatAtom(v reflect.Value) string {
|
||||
switch v.Kind() {
|
||||
case reflect.Invalid:
|
||||
return "invalid"
|
||||
case reflect.Int, reflect.Int8, reflect.Int16,
|
||||
reflect.Int32, reflect.Int64:
|
||||
return strconv.FormatInt(v.Int(), 10)
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16,
|
||||
reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
return strconv.FormatUint(v.Uint(), 10)
|
||||
// ...floating-point and complex cases omitted for brevity...
|
||||
case reflect.Bool:
|
||||
return strconv.FormatBool(v.Bool())
|
||||
case reflect.String:
|
||||
return strconv.Quote(v.String())
|
||||
case reflect.Chan, reflect.Func, reflect.Ptr, reflect.Slice, reflect.Map:
|
||||
return v.Type().String() + " 0x" +
|
||||
strconv.FormatUint(uint64(v.Pointer()), 16)
|
||||
default: // reflect.Array, reflect.Struct, reflect.Interface
|
||||
return v.Type().String() + " value"
|
||||
}
|
||||
}
|
||||
|
||||
//!-
|
||||
24
vendor/gopl.io/ch12/format/format_test.go
generated
vendored
Normal file
24
vendor/gopl.io/ch12/format/format_test.go
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
package format_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"gopl.io/ch12/format"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
// The pointer values are just examples, and may vary from run to run.
|
||||
//!+time
|
||||
var x int64 = 1
|
||||
var d time.Duration = 1 * time.Nanosecond
|
||||
fmt.Println(format.Any(x)) // "1"
|
||||
fmt.Println(format.Any(d)) // "1"
|
||||
fmt.Println(format.Any([]int64{x})) // "[]int64 0x8202b87b0"
|
||||
fmt.Println(format.Any([]time.Duration{d})) // "[]time.Duration 0x8202b87e0"
|
||||
//!-time
|
||||
}
|
||||
29
vendor/gopl.io/ch12/methods/methods.go
generated
vendored
Normal file
29
vendor/gopl.io/ch12/methods/methods.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 351.
|
||||
|
||||
// Package methods provides a function to print the methods of any value.
|
||||
package methods
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
//!+print
|
||||
// Print prints the method set of the value x.
|
||||
func Print(x interface{}) {
|
||||
v := reflect.ValueOf(x)
|
||||
t := v.Type()
|
||||
fmt.Printf("type %s\n", t)
|
||||
|
||||
for i := 0; i < v.NumMethod(); i++ {
|
||||
methType := v.Method(i).Type()
|
||||
fmt.Printf("func (%s) %s%s\n", t, t.Method(i).Name,
|
||||
strings.TrimPrefix(methType.String(), "func"))
|
||||
}
|
||||
}
|
||||
|
||||
//!-print
|
||||
49
vendor/gopl.io/ch12/methods/methods_test.go
generated
vendored
Normal file
49
vendor/gopl.io/ch12/methods/methods_test.go
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
package methods_test
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gopl.io/ch12/methods"
|
||||
)
|
||||
|
||||
func ExamplePrintDuration() {
|
||||
methods.Print(time.Hour)
|
||||
// Output:
|
||||
// type time.Duration
|
||||
// func (time.Duration) Hours() float64
|
||||
// func (time.Duration) Minutes() float64
|
||||
// func (time.Duration) Nanoseconds() int64
|
||||
// func (time.Duration) Seconds() float64
|
||||
// func (time.Duration) String() string
|
||||
}
|
||||
|
||||
func ExamplePrintReplacer() {
|
||||
methods.Print(new(strings.Replacer))
|
||||
// Output:
|
||||
// type *strings.Replacer
|
||||
// func (*strings.Replacer) Replace(string) string
|
||||
// func (*strings.Replacer) WriteString(io.Writer, string) (int, error)
|
||||
}
|
||||
|
||||
/*
|
||||
//!+output
|
||||
methods.Print(time.Hour)
|
||||
// Output:
|
||||
// type time.Duration
|
||||
// func (time.Duration) Hours() float64
|
||||
// func (time.Duration) Minutes() float64
|
||||
// func (time.Duration) Nanoseconds() int64
|
||||
// func (time.Duration) Seconds() float64
|
||||
// func (time.Duration) String() string
|
||||
|
||||
methods.Print(new(strings.Replacer))
|
||||
// Output:
|
||||
// type *strings.Replacer
|
||||
// func (*strings.Replacer) Replace(string) string
|
||||
// func (*strings.Replacer) WriteString(io.Writer, string) (int, error)
|
||||
//!-output
|
||||
*/
|
||||
90
vendor/gopl.io/ch12/params/params.go
generated
vendored
Normal file
90
vendor/gopl.io/ch12/params/params.go
generated
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 349.
|
||||
|
||||
// Package params provides a reflection-based parser for URL parameters.
|
||||
package params
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
//!+Unpack
|
||||
|
||||
// Unpack populates the fields of the struct pointed to by ptr
|
||||
// from the HTTP request parameters in req.
|
||||
func Unpack(req *http.Request, ptr interface{}) error {
|
||||
if err := req.ParseForm(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Build map of fields keyed by effective name.
|
||||
fields := make(map[string]reflect.Value)
|
||||
v := reflect.ValueOf(ptr).Elem() // the struct variable
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
fieldInfo := v.Type().Field(i) // a reflect.StructField
|
||||
tag := fieldInfo.Tag // a reflect.StructTag
|
||||
name := tag.Get("http")
|
||||
if name == "" {
|
||||
name = strings.ToLower(fieldInfo.Name)
|
||||
}
|
||||
fields[name] = v.Field(i)
|
||||
}
|
||||
|
||||
// Update struct field for each parameter in the request.
|
||||
for name, values := range req.Form {
|
||||
f := fields[name]
|
||||
if !f.IsValid() {
|
||||
continue // ignore unrecognized HTTP parameters
|
||||
}
|
||||
for _, value := range values {
|
||||
if f.Kind() == reflect.Slice {
|
||||
elem := reflect.New(f.Type().Elem()).Elem()
|
||||
if err := populate(elem, value); err != nil {
|
||||
return fmt.Errorf("%s: %v", name, err)
|
||||
}
|
||||
f.Set(reflect.Append(f, elem))
|
||||
} else {
|
||||
if err := populate(f, value); err != nil {
|
||||
return fmt.Errorf("%s: %v", name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//!-Unpack
|
||||
|
||||
//!+populate
|
||||
func populate(v reflect.Value, value string) error {
|
||||
switch v.Kind() {
|
||||
case reflect.String:
|
||||
v.SetString(value)
|
||||
|
||||
case reflect.Int:
|
||||
i, err := strconv.ParseInt(value, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v.SetInt(i)
|
||||
|
||||
case reflect.Bool:
|
||||
b, err := strconv.ParseBool(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v.SetBool(b)
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unsupported kind %s", v.Type())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//!-populate
|
||||
60
vendor/gopl.io/ch12/search/main.go
generated
vendored
Normal file
60
vendor/gopl.io/ch12/search/main.go
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 348.
|
||||
|
||||
// Search is a demo of the params.Unpack function.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
//!+
|
||||
|
||||
import "gopl.io/ch12/params"
|
||||
|
||||
// search implements the /search URL endpoint.
|
||||
func search(resp http.ResponseWriter, req *http.Request) {
|
||||
var data struct {
|
||||
Labels []string `http:"l"`
|
||||
MaxResults int `http:"max"`
|
||||
Exact bool `http:"x"`
|
||||
}
|
||||
data.MaxResults = 10 // set default
|
||||
if err := params.Unpack(req, &data); err != nil {
|
||||
http.Error(resp, err.Error(), http.StatusBadRequest) // 400
|
||||
return
|
||||
}
|
||||
|
||||
// ...rest of handler...
|
||||
fmt.Fprintf(resp, "Search: %+v\n", data)
|
||||
}
|
||||
|
||||
//!-
|
||||
|
||||
func main() {
|
||||
http.HandleFunc("/search", search)
|
||||
log.Fatal(http.ListenAndServe(":12345", nil))
|
||||
}
|
||||
|
||||
/*
|
||||
//!+output
|
||||
$ go build gopl.io/ch12/search
|
||||
$ ./search &
|
||||
$ ./fetch 'http://localhost:12345/search'
|
||||
Search: {Labels:[] MaxResults:10 Exact:false}
|
||||
$ ./fetch 'http://localhost:12345/search?l=golang&l=programming'
|
||||
Search: {Labels:[golang programming] MaxResults:10 Exact:false}
|
||||
$ ./fetch 'http://localhost:12345/search?l=golang&l=programming&max=100'
|
||||
Search: {Labels:[golang programming] MaxResults:100 Exact:false}
|
||||
$ ./fetch 'http://localhost:12345/search?x=true&l=golang&l=programming'
|
||||
Search: {Labels:[golang programming] MaxResults:10 Exact:true}
|
||||
$ ./fetch 'http://localhost:12345/search?q=hello&x=123'
|
||||
x: strconv.ParseBool: parsing "123": invalid syntax
|
||||
$ ./fetch 'http://localhost:12345/search?q=hello&max=lots'
|
||||
max: strconv.ParseInt: parsing "lots": invalid syntax
|
||||
//!-output
|
||||
*/
|
||||
162
vendor/gopl.io/ch12/sexpr/decode.go
generated
vendored
Normal file
162
vendor/gopl.io/ch12/sexpr/decode.go
generated
vendored
Normal file
@@ -0,0 +1,162 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 344.
|
||||
|
||||
// Package sexpr provides a means for converting Go objects to and
|
||||
// from S-expressions.
|
||||
package sexpr
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"text/scanner"
|
||||
)
|
||||
|
||||
//!+Unmarshal
|
||||
// Unmarshal parses S-expression data and populates the variable
|
||||
// whose address is in the non-nil pointer out.
|
||||
func Unmarshal(data []byte, out interface{}) (err error) {
|
||||
lex := &lexer{scan: scanner.Scanner{Mode: scanner.GoTokens}}
|
||||
lex.scan.Init(bytes.NewReader(data))
|
||||
lex.next() // get the first token
|
||||
defer func() {
|
||||
// NOTE: this is not an example of ideal error handling.
|
||||
if x := recover(); x != nil {
|
||||
err = fmt.Errorf("error at %s: %v", lex.scan.Position, x)
|
||||
}
|
||||
}()
|
||||
read(lex, reflect.ValueOf(out).Elem())
|
||||
return nil
|
||||
}
|
||||
|
||||
//!-Unmarshal
|
||||
|
||||
//!+lexer
|
||||
type lexer struct {
|
||||
scan scanner.Scanner
|
||||
token rune // the current token
|
||||
}
|
||||
|
||||
func (lex *lexer) next() { lex.token = lex.scan.Scan() }
|
||||
func (lex *lexer) text() string { return lex.scan.TokenText() }
|
||||
|
||||
func (lex *lexer) consume(want rune) {
|
||||
if lex.token != want { // NOTE: Not an example of good error handling.
|
||||
panic(fmt.Sprintf("got %q, want %q", lex.text(), want))
|
||||
}
|
||||
lex.next()
|
||||
}
|
||||
|
||||
//!-lexer
|
||||
|
||||
// The read function is a decoder for a small subset of well-formed
|
||||
// S-expressions. For brevity of our example, it takes many dubious
|
||||
// shortcuts.
|
||||
//
|
||||
// The parser assumes
|
||||
// - that the S-expression input is well-formed; it does no error checking.
|
||||
// - that the S-expression input corresponds to the type of the variable.
|
||||
// - that all numbers in the input are non-negative decimal integers.
|
||||
// - that all keys in ((key value) ...) struct syntax are unquoted symbols.
|
||||
// - that the input does not contain dotted lists such as (1 2 . 3).
|
||||
// - that the input does not contain Lisp reader macros such 'x and #'x.
|
||||
//
|
||||
// The reflection logic assumes
|
||||
// - that v is always a variable of the appropriate type for the
|
||||
// S-expression value. For example, v must not be a boolean,
|
||||
// interface, channel, or function, and if v is an array, the input
|
||||
// must have the correct number of elements.
|
||||
// - that v in the top-level call to read has the zero value of its
|
||||
// type and doesn't need clearing.
|
||||
// - that if v is a numeric variable, it is a signed integer.
|
||||
|
||||
//!+read
|
||||
func read(lex *lexer, v reflect.Value) {
|
||||
switch lex.token {
|
||||
case scanner.Ident:
|
||||
// The only valid identifiers are
|
||||
// "nil" and struct field names.
|
||||
if lex.text() == "nil" {
|
||||
v.Set(reflect.Zero(v.Type()))
|
||||
lex.next()
|
||||
return
|
||||
}
|
||||
case scanner.String:
|
||||
s, _ := strconv.Unquote(lex.text()) // NOTE: ignoring errors
|
||||
v.SetString(s)
|
||||
lex.next()
|
||||
return
|
||||
case scanner.Int:
|
||||
i, _ := strconv.Atoi(lex.text()) // NOTE: ignoring errors
|
||||
v.SetInt(int64(i))
|
||||
lex.next()
|
||||
return
|
||||
case '(':
|
||||
lex.next()
|
||||
readList(lex, v)
|
||||
lex.next() // consume ')'
|
||||
return
|
||||
}
|
||||
panic(fmt.Sprintf("unexpected token %q", lex.text()))
|
||||
}
|
||||
|
||||
//!-read
|
||||
|
||||
//!+readlist
|
||||
func readList(lex *lexer, v reflect.Value) {
|
||||
switch v.Kind() {
|
||||
case reflect.Array: // (item ...)
|
||||
for i := 0; !endList(lex); i++ {
|
||||
read(lex, v.Index(i))
|
||||
}
|
||||
|
||||
case reflect.Slice: // (item ...)
|
||||
for !endList(lex) {
|
||||
item := reflect.New(v.Type().Elem()).Elem()
|
||||
read(lex, item)
|
||||
v.Set(reflect.Append(v, item))
|
||||
}
|
||||
|
||||
case reflect.Struct: // ((name value) ...)
|
||||
for !endList(lex) {
|
||||
lex.consume('(')
|
||||
if lex.token != scanner.Ident {
|
||||
panic(fmt.Sprintf("got token %q, want field name", lex.text()))
|
||||
}
|
||||
name := lex.text()
|
||||
lex.next()
|
||||
read(lex, v.FieldByName(name))
|
||||
lex.consume(')')
|
||||
}
|
||||
|
||||
case reflect.Map: // ((key value) ...)
|
||||
v.Set(reflect.MakeMap(v.Type()))
|
||||
for !endList(lex) {
|
||||
lex.consume('(')
|
||||
key := reflect.New(v.Type().Key()).Elem()
|
||||
read(lex, key)
|
||||
value := reflect.New(v.Type().Elem()).Elem()
|
||||
read(lex, value)
|
||||
v.SetMapIndex(key, value)
|
||||
lex.consume(')')
|
||||
}
|
||||
|
||||
default:
|
||||
panic(fmt.Sprintf("cannot decode list into %v", v.Type()))
|
||||
}
|
||||
}
|
||||
|
||||
func endList(lex *lexer) bool {
|
||||
switch lex.token {
|
||||
case scanner.EOF:
|
||||
panic("end of file")
|
||||
case ')':
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
//!-readlist
|
||||
97
vendor/gopl.io/ch12/sexpr/encode.go
generated
vendored
Normal file
97
vendor/gopl.io/ch12/sexpr/encode.go
generated
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 339.
|
||||
|
||||
package sexpr
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
//!+Marshal
|
||||
// Marshal encodes a Go value in S-expression form.
|
||||
func Marshal(v interface{}) ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
if err := encode(&buf, reflect.ValueOf(v)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
//!-Marshal
|
||||
|
||||
// encode writes to buf an S-expression representation of v.
|
||||
//!+encode
|
||||
func encode(buf *bytes.Buffer, v reflect.Value) error {
|
||||
switch v.Kind() {
|
||||
case reflect.Invalid:
|
||||
buf.WriteString("nil")
|
||||
|
||||
case reflect.Int, reflect.Int8, reflect.Int16,
|
||||
reflect.Int32, reflect.Int64:
|
||||
fmt.Fprintf(buf, "%d", v.Int())
|
||||
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16,
|
||||
reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
fmt.Fprintf(buf, "%d", v.Uint())
|
||||
|
||||
case reflect.String:
|
||||
fmt.Fprintf(buf, "%q", v.String())
|
||||
|
||||
case reflect.Ptr:
|
||||
return encode(buf, v.Elem())
|
||||
|
||||
case reflect.Array, reflect.Slice: // (value ...)
|
||||
buf.WriteByte('(')
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
if i > 0 {
|
||||
buf.WriteByte(' ')
|
||||
}
|
||||
if err := encode(buf, v.Index(i)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
buf.WriteByte(')')
|
||||
|
||||
case reflect.Struct: // ((name value) ...)
|
||||
buf.WriteByte('(')
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
if i > 0 {
|
||||
buf.WriteByte(' ')
|
||||
}
|
||||
fmt.Fprintf(buf, "(%s ", v.Type().Field(i).Name)
|
||||
if err := encode(buf, v.Field(i)); err != nil {
|
||||
return err
|
||||
}
|
||||
buf.WriteByte(')')
|
||||
}
|
||||
buf.WriteByte(')')
|
||||
|
||||
case reflect.Map: // ((key value) ...)
|
||||
buf.WriteByte('(')
|
||||
for i, key := range v.MapKeys() {
|
||||
if i > 0 {
|
||||
buf.WriteByte(' ')
|
||||
}
|
||||
buf.WriteByte('(')
|
||||
if err := encode(buf, key); err != nil {
|
||||
return err
|
||||
}
|
||||
buf.WriteByte(' ')
|
||||
if err := encode(buf, v.MapIndex(key)); err != nil {
|
||||
return err
|
||||
}
|
||||
buf.WriteByte(')')
|
||||
}
|
||||
buf.WriteByte(')')
|
||||
|
||||
default: // float, complex, bool, chan, func, interface
|
||||
return fmt.Errorf("unsupported type: %s", v.Type())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//!-encode
|
||||
183
vendor/gopl.io/ch12/sexpr/pretty.go
generated
vendored
Normal file
183
vendor/gopl.io/ch12/sexpr/pretty.go
generated
vendored
Normal file
@@ -0,0 +1,183 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
package sexpr
|
||||
|
||||
// This file implements the algorithm described in Derek C. Oppen's
|
||||
// 1979 Stanford technical report, "Pretty Printing".
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
func MarshalIndent(v interface{}) ([]byte, error) {
|
||||
p := printer{width: margin}
|
||||
if err := pretty(&p, reflect.ValueOf(v)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return p.Bytes(), nil
|
||||
}
|
||||
|
||||
const margin = 80
|
||||
|
||||
type token struct {
|
||||
kind rune // one of "s ()" (string, blank, start, end)
|
||||
str string
|
||||
size int
|
||||
}
|
||||
|
||||
type printer struct {
|
||||
tokens []*token // FIFO buffer
|
||||
stack []*token // stack of open ' ' and '(' tokens
|
||||
rtotal int // total number of spaces needed to print stream
|
||||
|
||||
bytes.Buffer
|
||||
indents []int
|
||||
width int // remaining space
|
||||
}
|
||||
|
||||
func (p *printer) string(str string) {
|
||||
tok := &token{kind: 's', str: str, size: len(str)}
|
||||
if len(p.stack) == 0 {
|
||||
p.print(tok)
|
||||
} else {
|
||||
p.tokens = append(p.tokens, tok)
|
||||
p.rtotal += len(str)
|
||||
}
|
||||
}
|
||||
func (p *printer) pop() (top *token) {
|
||||
last := len(p.stack) - 1
|
||||
top, p.stack = p.stack[last], p.stack[:last]
|
||||
return
|
||||
}
|
||||
func (p *printer) begin() {
|
||||
if len(p.stack) == 0 {
|
||||
p.rtotal = 1
|
||||
}
|
||||
t := &token{kind: '(', size: -p.rtotal}
|
||||
p.tokens = append(p.tokens, t)
|
||||
p.stack = append(p.stack, t) // push
|
||||
p.string("(")
|
||||
}
|
||||
func (p *printer) end() {
|
||||
p.string(")")
|
||||
p.tokens = append(p.tokens, &token{kind: ')'})
|
||||
x := p.pop()
|
||||
x.size += p.rtotal
|
||||
if x.kind == ' ' {
|
||||
p.pop().size += p.rtotal
|
||||
}
|
||||
if len(p.stack) == 0 {
|
||||
for _, tok := range p.tokens {
|
||||
p.print(tok)
|
||||
}
|
||||
p.tokens = nil
|
||||
}
|
||||
}
|
||||
func (p *printer) space() {
|
||||
last := len(p.stack) - 1
|
||||
x := p.stack[last]
|
||||
if x.kind == ' ' {
|
||||
x.size += p.rtotal
|
||||
p.stack = p.stack[:last] // pop
|
||||
}
|
||||
t := &token{kind: ' ', size: -p.rtotal}
|
||||
p.tokens = append(p.tokens, t)
|
||||
p.stack = append(p.stack, t)
|
||||
p.rtotal++
|
||||
}
|
||||
func (p *printer) print(t *token) {
|
||||
switch t.kind {
|
||||
case 's':
|
||||
p.WriteString(t.str)
|
||||
p.width -= len(t.str)
|
||||
case '(':
|
||||
p.indents = append(p.indents, p.width)
|
||||
case ')':
|
||||
p.indents = p.indents[:len(p.indents)-1] // pop
|
||||
case ' ':
|
||||
if t.size > p.width {
|
||||
p.width = p.indents[len(p.indents)-1] - 1
|
||||
fmt.Fprintf(&p.Buffer, "\n%*s", margin-p.width, "")
|
||||
} else {
|
||||
p.WriteByte(' ')
|
||||
p.width--
|
||||
}
|
||||
}
|
||||
}
|
||||
func (p *printer) stringf(format string, args ...interface{}) {
|
||||
p.string(fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
func pretty(p *printer, v reflect.Value) error {
|
||||
switch v.Kind() {
|
||||
case reflect.Invalid:
|
||||
p.string("nil")
|
||||
|
||||
case reflect.Int, reflect.Int8, reflect.Int16,
|
||||
reflect.Int32, reflect.Int64:
|
||||
p.stringf("%d", v.Int())
|
||||
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16,
|
||||
reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
p.stringf("%d", v.Uint())
|
||||
|
||||
case reflect.String:
|
||||
p.stringf("%q", v.String())
|
||||
|
||||
case reflect.Array, reflect.Slice: // (value ...)
|
||||
p.begin()
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
if i > 0 {
|
||||
p.space()
|
||||
}
|
||||
if err := pretty(p, v.Index(i)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
p.end()
|
||||
|
||||
case reflect.Struct: // ((name value ...)
|
||||
p.begin()
|
||||
for i := 0; i < v.NumField(); i++ {
|
||||
if i > 0 {
|
||||
p.space()
|
||||
}
|
||||
p.begin()
|
||||
p.string(v.Type().Field(i).Name)
|
||||
p.space()
|
||||
if err := pretty(p, v.Field(i)); err != nil {
|
||||
return err
|
||||
}
|
||||
p.end()
|
||||
}
|
||||
p.end()
|
||||
|
||||
case reflect.Map: // ((key value ...)
|
||||
p.begin()
|
||||
for i, key := range v.MapKeys() {
|
||||
if i > 0 {
|
||||
p.space()
|
||||
}
|
||||
p.begin()
|
||||
if err := pretty(p, key); err != nil {
|
||||
return err
|
||||
}
|
||||
p.space()
|
||||
if err := pretty(p, v.MapIndex(key)); err != nil {
|
||||
return err
|
||||
}
|
||||
p.end()
|
||||
}
|
||||
p.end()
|
||||
|
||||
case reflect.Ptr:
|
||||
return pretty(p, v.Elem())
|
||||
|
||||
default: // float, complex, bool, chan, func, interface
|
||||
return fmt.Errorf("unsupported type: %s", v.Type())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
74
vendor/gopl.io/ch12/sexpr/sexpr_test.go
generated
vendored
Normal file
74
vendor/gopl.io/ch12/sexpr/sexpr_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 sexpr
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Test verifies that encoding and decoding a complex data value
|
||||
// produces an equal result.
|
||||
//
|
||||
// The test does not make direct assertions about the encoded output
|
||||
// because the output depends on map iteration order, which is
|
||||
// nondeterministic. The output of the t.Log statements can be
|
||||
// inspected by running the test with the -v flag:
|
||||
//
|
||||
// $ go test -v gopl.io/ch12/sexpr
|
||||
//
|
||||
func Test(t *testing.T) {
|
||||
type Movie struct {
|
||||
Title, Subtitle string
|
||||
Year int
|
||||
Actor map[string]string
|
||||
Oscars []string
|
||||
Sequel *string
|
||||
}
|
||||
strangelove := Movie{
|
||||
Title: "Dr. Strangelove",
|
||||
Subtitle: "How I Learned to Stop Worrying and Love the Bomb",
|
||||
Year: 1964,
|
||||
Actor: map[string]string{
|
||||
"Dr. Strangelove": "Peter Sellers",
|
||||
"Grp. Capt. Lionel Mandrake": "Peter Sellers",
|
||||
"Pres. Merkin Muffley": "Peter Sellers",
|
||||
"Gen. Buck Turgidson": "George C. Scott",
|
||||
"Brig. Gen. Jack D. Ripper": "Sterling Hayden",
|
||||
`Maj. T.J. "King" Kong`: "Slim Pickens",
|
||||
},
|
||||
Oscars: []string{
|
||||
"Best Actor (Nomin.)",
|
||||
"Best Adapted Screenplay (Nomin.)",
|
||||
"Best Director (Nomin.)",
|
||||
"Best Picture (Nomin.)",
|
||||
},
|
||||
}
|
||||
|
||||
// Encode it
|
||||
data, err := Marshal(strangelove)
|
||||
if err != nil {
|
||||
t.Fatalf("Marshal failed: %v", err)
|
||||
}
|
||||
t.Logf("Marshal() = %s\n", data)
|
||||
|
||||
// Decode it
|
||||
var movie Movie
|
||||
if err := Unmarshal(data, &movie); err != nil {
|
||||
t.Fatalf("Unmarshal failed: %v", err)
|
||||
}
|
||||
t.Logf("Unmarshal() = %+v\n", movie)
|
||||
|
||||
// Check equality.
|
||||
if !reflect.DeepEqual(movie, strangelove) {
|
||||
t.Fatal("not equal")
|
||||
}
|
||||
|
||||
// Pretty-print it:
|
||||
data, err = MarshalIndent(strangelove)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Logf("MarshalIdent() = %s\n", data)
|
||||
}
|
||||
28
vendor/gopl.io/ch13/bzip-print/bzip2.c
generated
vendored
Normal file
28
vendor/gopl.io/ch13/bzip-print/bzip2.c
generated
vendored
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
vendor/gopl.io/ch13/bzip-print/bzip2.go
generated
vendored
Normal file
96
vendor/gopl.io/ch13/bzip-print/bzip2.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 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
vendor/gopl.io/ch13/bzip-print/bzip2_test.go
generated
vendored
Normal file
40
vendor/gopl.io/ch13/bzip-print/bzip2_test.go
generated
vendored
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")
|
||||
}
|
||||
}
|
||||
32
vendor/gopl.io/ch13/bzip/bzip2.c
generated
vendored
Normal file
32
vendor/gopl.io/ch13/bzip/bzip2.c
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 362.
|
||||
//
|
||||
// The version of this program that appeared in the first and second
|
||||
// printings did not comply with the proposed rules for passing
|
||||
// pointers between Go and C, described here:
|
||||
// https://github.com/golang/proposal/blob/master/design/12416-cgo-pointers.md
|
||||
//
|
||||
// The version below, which appears in the third printing,
|
||||
// has been corrected. See bzip2.go for explanation.
|
||||
|
||||
//!+
|
||||
/* 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;
|
||||
s->next_in = s->next_out = NULL;
|
||||
return r;
|
||||
}
|
||||
|
||||
//!-
|
||||
111
vendor/gopl.io/ch13/bzip/bzip2.go
generated
vendored
Normal file
111
vendor/gopl.io/ch13/bzip/bzip2.go
generated
vendored
Normal file
@@ -0,0 +1,111 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 362.
|
||||
//
|
||||
// The version of this program that appeared in the first and second
|
||||
// printings did not comply with the proposed rules for passing
|
||||
// pointers between Go and C, described here:
|
||||
// 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.
|
||||
//
|
||||
// The version below, which appears in the third printing, has been
|
||||
// corrected. 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
|
||||
// pointers to Go variables.
|
||||
|
||||
//!+
|
||||
|
||||
// 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>
|
||||
#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) { free(s); }
|
||||
*/
|
||||
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
|
||||
const verbosity = 0
|
||||
const workFactor = 30
|
||||
w := &writer{w: out, stream: C.bz2alloc()}
|
||||
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)
|
||||
C.bz2free(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
vendor/gopl.io/ch13/bzip/bzip2_test.go
generated
vendored
Normal file
40
vendor/gopl.io/ch13/bzip/bzip2_test.go
generated
vendored
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")
|
||||
}
|
||||
}
|
||||
29
vendor/gopl.io/ch13/bzipper/main.go
generated
vendored
Normal file
29
vendor/gopl.io/ch13/bzipper/main.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 365.
|
||||
|
||||
//!+
|
||||
|
||||
// Bzipper reads input, bzip2-compresses it, and writes it out.
|
||||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"gopl.io/ch13/bzip"
|
||||
)
|
||||
|
||||
func main() {
|
||||
w := bzip.NewWriter(os.Stdout)
|
||||
if _, err := io.Copy(w, os.Stdin); err != nil {
|
||||
log.Fatalf("bzipper: %v\n", err)
|
||||
}
|
||||
if err := w.Close(); err != nil {
|
||||
log.Fatalf("bzipper: close: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
//!-
|
||||
127
vendor/gopl.io/ch13/equal/equal.go
generated
vendored
Normal file
127
vendor/gopl.io/ch13/equal/equal.go
generated
vendored
Normal file
@@ -0,0 +1,127 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 359.
|
||||
|
||||
// Package equal provides a deep equivalence relation for arbitrary values.
|
||||
package equal
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
//!+
|
||||
func equal(x, y reflect.Value, seen map[comparison]bool) bool {
|
||||
if !x.IsValid() || !y.IsValid() {
|
||||
return x.IsValid() == y.IsValid()
|
||||
}
|
||||
if x.Type() != y.Type() {
|
||||
return false
|
||||
}
|
||||
|
||||
// ...cycle check omitted (shown later)...
|
||||
|
||||
//!-
|
||||
//!+cyclecheck
|
||||
// cycle check
|
||||
if x.CanAddr() && y.CanAddr() {
|
||||
xptr := unsafe.Pointer(x.UnsafeAddr())
|
||||
yptr := unsafe.Pointer(y.UnsafeAddr())
|
||||
if xptr == yptr {
|
||||
return true // identical references
|
||||
}
|
||||
c := comparison{xptr, yptr, x.Type()}
|
||||
if seen[c] {
|
||||
return true // already seen
|
||||
}
|
||||
seen[c] = true
|
||||
}
|
||||
//!-cyclecheck
|
||||
//!+
|
||||
switch x.Kind() {
|
||||
case reflect.Bool:
|
||||
return x.Bool() == y.Bool()
|
||||
|
||||
case reflect.String:
|
||||
return x.String() == y.String()
|
||||
|
||||
// ...numeric cases omitted for brevity...
|
||||
|
||||
//!-
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,
|
||||
reflect.Int64:
|
||||
return x.Int() == y.Int()
|
||||
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,
|
||||
reflect.Uint64, reflect.Uintptr:
|
||||
return x.Uint() == y.Uint()
|
||||
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return x.Float() == y.Float()
|
||||
|
||||
case reflect.Complex64, reflect.Complex128:
|
||||
return x.Complex() == y.Complex()
|
||||
//!+
|
||||
case reflect.Chan, reflect.UnsafePointer, reflect.Func:
|
||||
return x.Pointer() == y.Pointer()
|
||||
|
||||
case reflect.Ptr, reflect.Interface:
|
||||
return equal(x.Elem(), y.Elem(), seen)
|
||||
|
||||
case reflect.Array, reflect.Slice:
|
||||
if x.Len() != y.Len() {
|
||||
return false
|
||||
}
|
||||
for i := 0; i < x.Len(); i++ {
|
||||
if !equal(x.Index(i), y.Index(i), seen) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
|
||||
// ...struct and map cases omitted for brevity...
|
||||
//!-
|
||||
case reflect.Struct:
|
||||
for i, n := 0, x.NumField(); i < n; i++ {
|
||||
if !equal(x.Field(i), y.Field(i), seen) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
|
||||
case reflect.Map:
|
||||
if x.Len() != y.Len() {
|
||||
return false
|
||||
}
|
||||
for _, k := range x.MapKeys() {
|
||||
if !equal(x.MapIndex(k), y.MapIndex(k), seen) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
//!+
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
//!-
|
||||
|
||||
//!+comparison
|
||||
// Equal reports whether x and y are deeply equal.
|
||||
//!-comparison
|
||||
//
|
||||
// Map keys are always compared with ==, not deeply.
|
||||
// (This matters for keys containing pointers or interfaces.)
|
||||
//!+comparison
|
||||
func Equal(x, y interface{}) bool {
|
||||
seen := make(map[comparison]bool)
|
||||
return equal(reflect.ValueOf(x), reflect.ValueOf(y), seen)
|
||||
}
|
||||
|
||||
type comparison struct {
|
||||
x, y unsafe.Pointer
|
||||
t reflect.Type
|
||||
}
|
||||
|
||||
//!-comparison
|
||||
133
vendor/gopl.io/ch13/equal/equal_test.go
generated
vendored
Normal file
133
vendor/gopl.io/ch13/equal/equal_test.go
generated
vendored
Normal file
@@ -0,0 +1,133 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
package equal
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestEqual(t *testing.T) {
|
||||
one, oneAgain, two := 1, 1, 2
|
||||
|
||||
type CyclePtr *CyclePtr
|
||||
var cyclePtr1, cyclePtr2 CyclePtr
|
||||
cyclePtr1 = &cyclePtr1
|
||||
cyclePtr2 = &cyclePtr2
|
||||
|
||||
type CycleSlice []CycleSlice
|
||||
var cycleSlice CycleSlice
|
||||
cycleSlice = append(cycleSlice, cycleSlice)
|
||||
|
||||
ch1, ch2 := make(chan int), make(chan int)
|
||||
var ch1ro <-chan int = ch1
|
||||
|
||||
type mystring string
|
||||
|
||||
var iface1, iface1Again, iface2 interface{} = &one, &oneAgain, &two
|
||||
|
||||
for _, test := range []struct {
|
||||
x, y interface{}
|
||||
want bool
|
||||
}{
|
||||
// basic types
|
||||
{1, 1, true},
|
||||
{1, 2, false}, // different values
|
||||
{1, 1.0, false}, // different types
|
||||
{"foo", "foo", true},
|
||||
{"foo", "bar", false},
|
||||
{mystring("foo"), "foo", false}, // different types
|
||||
// slices
|
||||
{[]string{"foo"}, []string{"foo"}, true},
|
||||
{[]string{"foo"}, []string{"bar"}, false},
|
||||
{[]string{}, []string(nil), true},
|
||||
// slice cycles
|
||||
{cycleSlice, cycleSlice, true},
|
||||
// maps
|
||||
{
|
||||
map[string][]int{"foo": {1, 2, 3}},
|
||||
map[string][]int{"foo": {1, 2, 3}},
|
||||
true,
|
||||
},
|
||||
{
|
||||
map[string][]int{"foo": {1, 2, 3}},
|
||||
map[string][]int{"foo": {1, 2, 3, 4}},
|
||||
false,
|
||||
},
|
||||
{
|
||||
map[string][]int{},
|
||||
map[string][]int(nil),
|
||||
true,
|
||||
},
|
||||
// pointers
|
||||
{&one, &one, true},
|
||||
{&one, &two, false},
|
||||
{&one, &oneAgain, true},
|
||||
{new(bytes.Buffer), new(bytes.Buffer), true},
|
||||
// pointer cycles
|
||||
{cyclePtr1, cyclePtr1, true},
|
||||
{cyclePtr2, cyclePtr2, true},
|
||||
{cyclePtr1, cyclePtr2, true}, // they're deeply equal
|
||||
// functions
|
||||
{(func())(nil), (func())(nil), true},
|
||||
{(func())(nil), func() {}, false},
|
||||
{func() {}, func() {}, false},
|
||||
// arrays
|
||||
{[...]int{1, 2, 3}, [...]int{1, 2, 3}, true},
|
||||
{[...]int{1, 2, 3}, [...]int{1, 2, 4}, false},
|
||||
// channels
|
||||
{ch1, ch1, true},
|
||||
{ch1, ch2, false},
|
||||
{ch1ro, ch1, false}, // NOTE: not equal
|
||||
// interfaces
|
||||
{&iface1, &iface1, true},
|
||||
{&iface1, &iface2, false},
|
||||
{&iface1Again, &iface1, true},
|
||||
} {
|
||||
if Equal(test.x, test.y) != test.want {
|
||||
t.Errorf("Equal(%v, %v) = %t",
|
||||
test.x, test.y, !test.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Example_equal() {
|
||||
//!+
|
||||
fmt.Println(Equal([]int{1, 2, 3}, []int{1, 2, 3})) // "true"
|
||||
fmt.Println(Equal([]string{"foo"}, []string{"bar"})) // "false"
|
||||
fmt.Println(Equal([]string(nil), []string{})) // "true"
|
||||
fmt.Println(Equal(map[string]int(nil), map[string]int{})) // "true"
|
||||
//!-
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// true
|
||||
// true
|
||||
}
|
||||
|
||||
func Example_equalCycle() {
|
||||
//!+cycle
|
||||
// Circular linked lists a -> b -> a and c -> c.
|
||||
type link struct {
|
||||
value string
|
||||
tail *link
|
||||
}
|
||||
a, b, c := &link{value: "a"}, &link{value: "b"}, &link{value: "c"}
|
||||
a.tail, b.tail, c.tail = b, a, c
|
||||
fmt.Println(Equal(a, a)) // "true"
|
||||
fmt.Println(Equal(b, b)) // "true"
|
||||
fmt.Println(Equal(c, c)) // "true"
|
||||
fmt.Println(Equal(a, b)) // "false"
|
||||
fmt.Println(Equal(a, c)) // "false"
|
||||
//!-cycle
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// true
|
||||
// true
|
||||
// false
|
||||
// false
|
||||
}
|
||||
38
vendor/gopl.io/ch13/unsafeptr/main.go
generated
vendored
Normal file
38
vendor/gopl.io/ch13/unsafeptr/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 357.
|
||||
|
||||
// Package unsafeptr demonstrates basic use of unsafe.Pointer.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func main() {
|
||||
//!+main
|
||||
var x struct {
|
||||
a bool
|
||||
b int16
|
||||
c []int
|
||||
}
|
||||
|
||||
// equivalent to pb := &x.b
|
||||
pb := (*int16)(unsafe.Pointer(
|
||||
uintptr(unsafe.Pointer(&x)) + unsafe.Offsetof(x.b)))
|
||||
*pb = 42
|
||||
|
||||
fmt.Println(x.b) // "42"
|
||||
//!-main
|
||||
}
|
||||
|
||||
/*
|
||||
//!+wrong
|
||||
// NOTE: subtly incorrect!
|
||||
tmp := uintptr(unsafe.Pointer(&x)) + unsafe.Offsetof(x.b)
|
||||
pb := (*int16)(unsafe.Pointer(tmp))
|
||||
*pb = 42
|
||||
//!-wrong
|
||||
*/
|
||||
22
vendor/gopl.io/ch2/boiling/main.go
generated
vendored
Normal file
22
vendor/gopl.io/ch2/boiling/main.go
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 29.
|
||||
//!+
|
||||
|
||||
// Boiling prints the boiling point of water.
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
const boilingF = 212.0
|
||||
|
||||
func main() {
|
||||
var f = boilingF
|
||||
var c = (f - 32) * 5 / 9
|
||||
fmt.Printf("boiling point = %g°F or %g°C\n", f, c)
|
||||
// Output:
|
||||
// boiling point = 212°F or 100°C
|
||||
}
|
||||
|
||||
//!-
|
||||
32
vendor/gopl.io/ch2/cf/main.go
generated
vendored
Normal file
32
vendor/gopl.io/ch2/cf/main.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 43.
|
||||
//!+
|
||||
|
||||
// Cf converts its numeric argument to Celsius and Fahrenheit.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"gopl.io/ch2/tempconv"
|
||||
)
|
||||
|
||||
func main() {
|
||||
for _, arg := range os.Args[1:] {
|
||||
t, err := strconv.ParseFloat(arg, 64)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "cf: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
f := tempconv.Fahrenheit(t)
|
||||
c := tempconv.Celsius(t)
|
||||
fmt.Printf("%s = %s, %s = %s\n",
|
||||
f, tempconv.FToC(f), c, tempconv.CToF(c))
|
||||
}
|
||||
}
|
||||
|
||||
//!-
|
||||
27
vendor/gopl.io/ch2/echo4/main.go
generated
vendored
Normal file
27
vendor/gopl.io/ch2/echo4/main.go
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 33.
|
||||
//!+
|
||||
|
||||
// Echo4 prints its command-line arguments.
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var n = flag.Bool("n", false, "omit trailing newline")
|
||||
var sep = flag.String("s", " ", "separator")
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
fmt.Print(strings.Join(flag.Args(), *sep))
|
||||
if !*n {
|
||||
fmt.Println()
|
||||
}
|
||||
}
|
||||
|
||||
//!-
|
||||
22
vendor/gopl.io/ch2/ftoc/main.go
generated
vendored
Normal file
22
vendor/gopl.io/ch2/ftoc/main.go
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 29.
|
||||
//!+
|
||||
|
||||
// Ftoc prints two Fahrenheit-to-Celsius conversions.
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
const freezingF, boilingF = 32.0, 212.0
|
||||
fmt.Printf("%g°F = %g°C\n", freezingF, fToC(freezingF)) // "32°F = 0°C"
|
||||
fmt.Printf("%g°F = %g°C\n", boilingF, fToC(boilingF)) // "212°F = 100°C"
|
||||
}
|
||||
|
||||
func fToC(f float64) float64 {
|
||||
return (f - 32) * 5 / 9
|
||||
}
|
||||
|
||||
//!-
|
||||
31
vendor/gopl.io/ch2/popcount/main.go
generated
vendored
Normal file
31
vendor/gopl.io/ch2/popcount/main.go
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 45.
|
||||
|
||||
// (Package doc comment intentionally malformed to demonstrate golint.)
|
||||
//!+
|
||||
package popcount
|
||||
|
||||
// pc[i] is the population count of i.
|
||||
var pc [256]byte
|
||||
|
||||
func init() {
|
||||
for i := range pc {
|
||||
pc[i] = pc[i/2] + byte(i&1)
|
||||
}
|
||||
}
|
||||
|
||||
// PopCount returns the population count (number of set bits) of x.
|
||||
func PopCount(x uint64) int {
|
||||
return int(pc[byte(x>>(0*8))] +
|
||||
pc[byte(x>>(1*8))] +
|
||||
pc[byte(x>>(2*8))] +
|
||||
pc[byte(x>>(3*8))] +
|
||||
pc[byte(x>>(4*8))] +
|
||||
pc[byte(x>>(5*8))] +
|
||||
pc[byte(x>>(6*8))] +
|
||||
pc[byte(x>>(7*8))])
|
||||
}
|
||||
|
||||
//!-
|
||||
83
vendor/gopl.io/ch2/popcount/popcount_test.go
generated
vendored
Normal file
83
vendor/gopl.io/ch2/popcount/popcount_test.go
generated
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
package popcount_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gopl.io/ch2/popcount"
|
||||
)
|
||||
|
||||
// -- Alternative implementations --
|
||||
|
||||
func BitCount(x uint64) int {
|
||||
// Hacker's Delight, Figure 5-2.
|
||||
x = x - ((x >> 1) & 0x5555555555555555)
|
||||
x = (x & 0x3333333333333333) + ((x >> 2) & 0x3333333333333333)
|
||||
x = (x + (x >> 4)) & 0x0f0f0f0f0f0f0f0f
|
||||
x = x + (x >> 8)
|
||||
x = x + (x >> 16)
|
||||
x = x + (x >> 32)
|
||||
return int(x & 0x7f)
|
||||
}
|
||||
|
||||
func PopCountByClearing(x uint64) int {
|
||||
n := 0
|
||||
for x != 0 {
|
||||
x = x & (x - 1) // clear rightmost non-zero bit
|
||||
n++
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func PopCountByShifting(x uint64) int {
|
||||
n := 0
|
||||
for i := uint(0); i < 64; i++ {
|
||||
if x&(1<<i) != 0 {
|
||||
n++
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// -- Benchmarks --
|
||||
|
||||
func BenchmarkPopCount(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
popcount.PopCount(0x1234567890ABCDEF)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkBitCount(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
BitCount(0x1234567890ABCDEF)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkPopCountByClearing(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
PopCountByClearing(0x1234567890ABCDEF)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkPopCountByShifting(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
PopCountByShifting(0x1234567890ABCDEF)
|
||||
}
|
||||
}
|
||||
|
||||
// 2.67GHz Xeon
|
||||
// $ go test -cpu=4 -bench=. gopl.io/ch2/popcount
|
||||
// BenchmarkPopCount-4 200000000 6.30 ns/op
|
||||
// BenchmarkBitCount-4 300000000 4.15 ns/op
|
||||
// BenchmarkPopCountByClearing-4 30000000 45.2 ns/op
|
||||
// BenchmarkPopCountByShifting-4 10000000 153 ns/op
|
||||
//
|
||||
// 2.5GHz Intel Core i5
|
||||
// $ go test -cpu=4 -bench=. gopl.io/ch2/popcount
|
||||
// testing: warning: no tests to run
|
||||
// BenchmarkPopCount-4 200000000 7.52 ns/op
|
||||
// BenchmarkBitCount-4 500000000 3.36 ns/op
|
||||
// BenchmarkPopCountByClearing-4 50000000 34.3 ns/op
|
||||
// BenchmarkPopCountByShifting-4 20000000 108 ns/op
|
||||
16
vendor/gopl.io/ch2/tempconv/conv.go
generated
vendored
Normal file
16
vendor/gopl.io/ch2/tempconv/conv.go
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 41.
|
||||
|
||||
//!+
|
||||
|
||||
package tempconv
|
||||
|
||||
// CToF converts a Celsius temperature to Fahrenheit.
|
||||
func CToF(c Celsius) Fahrenheit { return Fahrenheit(c*9/5 + 32) }
|
||||
|
||||
// FToC converts a Fahrenheit temperature to Celsius.
|
||||
func FToC(f Fahrenheit) Celsius { return Celsius((f - 32) * 5 / 9) }
|
||||
|
||||
//!-
|
||||
23
vendor/gopl.io/ch2/tempconv/tempconv.go
generated
vendored
Normal file
23
vendor/gopl.io/ch2/tempconv/tempconv.go
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
// 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 conversions.
|
||||
package tempconv
|
||||
|
||||
import "fmt"
|
||||
|
||||
type Celsius float64
|
||||
type Fahrenheit float64
|
||||
|
||||
const (
|
||||
AbsoluteZeroC Celsius = -273.15
|
||||
FreezingC Celsius = 0
|
||||
BoilingC Celsius = 100
|
||||
)
|
||||
|
||||
func (c Celsius) String() string { return fmt.Sprintf("%g°C", c) }
|
||||
func (f Fahrenheit) String() string { return fmt.Sprintf("%g°F", f) }
|
||||
|
||||
//!-
|
||||
27
vendor/gopl.io/ch2/tempconv0/celsius.go
generated
vendored
Normal file
27
vendor/gopl.io/ch2/tempconv0/celsius.go
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 39.
|
||||
//!+
|
||||
|
||||
// Package tempconv performs Celsius and Fahrenheit temperature computations.
|
||||
package tempconv
|
||||
|
||||
import "fmt"
|
||||
|
||||
type Celsius float64
|
||||
type Fahrenheit float64
|
||||
|
||||
const (
|
||||
AbsoluteZeroC Celsius = -273.15
|
||||
FreezingC Celsius = 0
|
||||
BoilingC Celsius = 100
|
||||
)
|
||||
|
||||
func CToF(c Celsius) Fahrenheit { return Fahrenheit(c*9/5 + 32) }
|
||||
|
||||
func FToC(f Fahrenheit) Celsius { return Celsius((f - 32) * 5 / 9) }
|
||||
|
||||
//!-
|
||||
|
||||
func (c Celsius) String() string { return fmt.Sprintf("%g°C", c) }
|
||||
45
vendor/gopl.io/ch2/tempconv0/tempconv_test.go
generated
vendored
Normal file
45
vendor/gopl.io/ch2/tempconv0/tempconv_test.go
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
package tempconv
|
||||
|
||||
import "fmt"
|
||||
|
||||
func Example_one() {
|
||||
{
|
||||
//!+arith
|
||||
fmt.Printf("%g\n", BoilingC-FreezingC) // "100" °C
|
||||
boilingF := CToF(BoilingC)
|
||||
fmt.Printf("%g\n", boilingF-CToF(FreezingC)) // "180" °F
|
||||
//!-arith
|
||||
}
|
||||
/*
|
||||
//!+arith
|
||||
fmt.Printf("%g\n", boilingF-FreezingC) // compile error: type mismatch
|
||||
//!-arith
|
||||
*/
|
||||
|
||||
// Output:
|
||||
// 100
|
||||
// 180
|
||||
}
|
||||
|
||||
func Example_two() {
|
||||
//!+printf
|
||||
c := FToC(212.0)
|
||||
fmt.Println(c.String()) // "100°C"
|
||||
fmt.Printf("%v\n", c) // "100°C"; no need to call String explicitly
|
||||
fmt.Printf("%s\n", c) // "100°C"
|
||||
fmt.Println(c) // "100°C"
|
||||
fmt.Printf("%g\n", c) // "100"; does not call String
|
||||
fmt.Println(float64(c)) // "100"; does not call String
|
||||
//!-printf
|
||||
|
||||
// Output:
|
||||
// 100°C
|
||||
// 100°C
|
||||
// 100°C
|
||||
// 100°C
|
||||
// 100
|
||||
// 100
|
||||
}
|
||||
44
vendor/gopl.io/ch3/basename1/main.go
generated
vendored
Normal file
44
vendor/gopl.io/ch3/basename1/main.go
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 72.
|
||||
|
||||
// Basename1 reads file names from stdin and prints the base name of each one.
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
input := bufio.NewScanner(os.Stdin)
|
||||
for input.Scan() {
|
||||
fmt.Println(basename(input.Text()))
|
||||
}
|
||||
// NOTE: ignoring potential errors from input.Err()
|
||||
}
|
||||
|
||||
//!+
|
||||
// basename removes directory components and a .suffix.
|
||||
// e.g., a => a, a.go => a, a/b/c.go => c, a/b.c.go => b.c
|
||||
func basename(s string) string {
|
||||
// Discard last '/' and everything before.
|
||||
for i := len(s) - 1; i >= 0; i-- {
|
||||
if s[i] == '/' {
|
||||
s = s[i+1:]
|
||||
break
|
||||
}
|
||||
}
|
||||
// Preserve everything before last '.'.
|
||||
for i := len(s) - 1; i >= 0; i-- {
|
||||
if s[i] == '.' {
|
||||
s = s[:i]
|
||||
break
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
//!-
|
||||
36
vendor/gopl.io/ch3/basename2/main.go
generated
vendored
Normal file
36
vendor/gopl.io/ch3/basename2/main.go
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 72.
|
||||
|
||||
// Basename2 reads file names from stdin and prints the base name of each one.
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func main() {
|
||||
input := bufio.NewScanner(os.Stdin)
|
||||
for input.Scan() {
|
||||
fmt.Println(basename(input.Text()))
|
||||
}
|
||||
// NOTE: ignoring potential errors from input.Err()
|
||||
}
|
||||
|
||||
// basename removes directory components and a trailing .suffix.
|
||||
// e.g., a => a, a.go => a, a/b/c.go => c, a/b.c.go => b.c
|
||||
//!+
|
||||
func basename(s string) string {
|
||||
slash := strings.LastIndex(s, "/") // -1 if "/" not found
|
||||
s = s[slash+1:]
|
||||
if dot := strings.LastIndex(s, "."); dot >= 0 {
|
||||
s = s[:dot]
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
//!-
|
||||
40
vendor/gopl.io/ch3/comma/main.go
generated
vendored
Normal file
40
vendor/gopl.io/ch3/comma/main.go
generated
vendored
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/
|
||||
|
||||
// See page 73.
|
||||
|
||||
// Comma prints its argument numbers with a comma at each power of 1000.
|
||||
//
|
||||
// Example:
|
||||
// $ go build gopl.io/ch3/comma
|
||||
// $ ./comma 1 12 123 1234 1234567890
|
||||
// 1
|
||||
// 12
|
||||
// 123
|
||||
// 1,234
|
||||
// 1,234,567,890
|
||||
//
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
for i := 1; i < len(os.Args); i++ {
|
||||
fmt.Printf(" %s\n", comma(os.Args[i]))
|
||||
}
|
||||
}
|
||||
|
||||
//!+
|
||||
// comma inserts commas in a non-negative decimal integer string.
|
||||
func comma(s string) string {
|
||||
n := len(s)
|
||||
if n <= 3 {
|
||||
return s
|
||||
}
|
||||
return comma(s[:n-3]) + "," + s[n-3:]
|
||||
}
|
||||
|
||||
//!-
|
||||
84
vendor/gopl.io/ch3/mandelbrot/main.go
generated
vendored
Normal file
84
vendor/gopl.io/ch3/mandelbrot/main.go
generated
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 61.
|
||||
//!+
|
||||
|
||||
// Mandelbrot emits a PNG image of the Mandelbrot fractal.
|
||||
package main
|
||||
|
||||
import (
|
||||
"image"
|
||||
"image/color"
|
||||
"image/png"
|
||||
"math/cmplx"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
const (
|
||||
xmin, ymin, xmax, ymax = -2, -2, +2, +2
|
||||
width, height = 1024, 1024
|
||||
)
|
||||
|
||||
img := image.NewRGBA(image.Rect(0, 0, width, height))
|
||||
for py := 0; py < height; py++ {
|
||||
y := float64(py)/height*(ymax-ymin) + ymin
|
||||
for px := 0; px < width; px++ {
|
||||
x := float64(px)/width*(xmax-xmin) + xmin
|
||||
z := complex(x, y)
|
||||
// Image point (px, py) represents complex value z.
|
||||
img.Set(px, py, mandelbrot(z))
|
||||
}
|
||||
}
|
||||
png.Encode(os.Stdout, img) // NOTE: ignoring errors
|
||||
}
|
||||
|
||||
func mandelbrot(z complex128) color.Color {
|
||||
const iterations = 200
|
||||
const contrast = 15
|
||||
|
||||
var v complex128
|
||||
for n := uint8(0); n < iterations; n++ {
|
||||
v = v*v + z
|
||||
if cmplx.Abs(v) > 2 {
|
||||
return color.Gray{255 - contrast*n}
|
||||
}
|
||||
}
|
||||
return color.Black
|
||||
}
|
||||
|
||||
//!-
|
||||
|
||||
// Some other interesting functions:
|
||||
|
||||
func acos(z complex128) color.Color {
|
||||
v := cmplx.Acos(z)
|
||||
blue := uint8(real(v)*128) + 127
|
||||
red := uint8(imag(v)*128) + 127
|
||||
return color.YCbCr{192, blue, red}
|
||||
}
|
||||
|
||||
func sqrt(z complex128) color.Color {
|
||||
v := cmplx.Sqrt(z)
|
||||
blue := uint8(real(v)*128) + 127
|
||||
red := uint8(imag(v)*128) + 127
|
||||
return color.YCbCr{128, blue, red}
|
||||
}
|
||||
|
||||
// f(x) = x^4 - 1
|
||||
//
|
||||
// z' = z - f(z)/f'(z)
|
||||
// = z - (z^4 - 1) / (4 * z^3)
|
||||
// = z - (z - 1/z^3) / 4
|
||||
func newton(z complex128) color.Color {
|
||||
const iterations = 37
|
||||
const contrast = 7
|
||||
for i := uint8(0); i < iterations; i++ {
|
||||
z -= (z - 1/(z*z*z)) / 4
|
||||
if cmplx.Abs(z*z*z*z-1) < 1e-6 {
|
||||
return color.Gray{255 - contrast*i}
|
||||
}
|
||||
}
|
||||
return color.Black
|
||||
}
|
||||
30
vendor/gopl.io/ch3/netflag/netflag.go
generated
vendored
Normal file
30
vendor/gopl.io/ch3/netflag/netflag.go
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 77.
|
||||
|
||||
// Netflag demonstrates an integer type used as a bit field.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
. "net"
|
||||
)
|
||||
|
||||
//!+
|
||||
func IsUp(v Flags) bool { return v&FlagUp == FlagUp }
|
||||
func TurnDown(v *Flags) { *v &^= FlagUp }
|
||||
func SetBroadcast(v *Flags) { *v |= FlagBroadcast }
|
||||
func IsCast(v Flags) bool { return v&(FlagBroadcast|FlagMulticast) != 0 }
|
||||
|
||||
func main() {
|
||||
var v Flags = FlagMulticast | FlagUp
|
||||
fmt.Printf("%b %t\n", v, IsUp(v)) // "10001 true"
|
||||
TurnDown(&v)
|
||||
fmt.Printf("%b %t\n", v, IsUp(v)) // "10000 false"
|
||||
SetBroadcast(&v)
|
||||
fmt.Printf("%b %t\n", v, IsUp(v)) // "10010 false"
|
||||
fmt.Printf("%b %t\n", v, IsCast(v)) // "10010 true"
|
||||
}
|
||||
|
||||
//!-
|
||||
33
vendor/gopl.io/ch3/printints/main.go
generated
vendored
Normal file
33
vendor/gopl.io/ch3/printints/main.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 74.
|
||||
|
||||
// Printints demonstrates the use of bytes.Buffer to format a string.
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
//!+
|
||||
// intsToString is like fmt.Sprint(values) but adds commas.
|
||||
func intsToString(values []int) string {
|
||||
var buf bytes.Buffer
|
||||
buf.WriteByte('[')
|
||||
for i, v := range values {
|
||||
if i > 0 {
|
||||
buf.WriteString(", ")
|
||||
}
|
||||
fmt.Fprintf(&buf, "%d", v)
|
||||
}
|
||||
buf.WriteByte(']')
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func main() {
|
||||
fmt.Println(intsToString([]int{1, 2, 3})) // "[1, 2, 3]"
|
||||
}
|
||||
|
||||
//!-
|
||||
62
vendor/gopl.io/ch3/surface/main.go
generated
vendored
Normal file
62
vendor/gopl.io/ch3/surface/main.go
generated
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 58.
|
||||
//!+
|
||||
|
||||
// Surface computes an SVG rendering of a 3-D surface function.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
)
|
||||
|
||||
const (
|
||||
width, height = 600, 320 // canvas size in pixels
|
||||
cells = 100 // number of grid cells
|
||||
xyrange = 30.0 // axis ranges (-xyrange..+xyrange)
|
||||
xyscale = width / 2 / xyrange // pixels per x or y unit
|
||||
zscale = height * 0.4 // pixels per z unit
|
||||
angle = math.Pi / 6 // angle of x, y axes (=30°)
|
||||
)
|
||||
|
||||
var sin30, cos30 = math.Sin(angle), math.Cos(angle) // sin(30°), cos(30°)
|
||||
|
||||
func main() {
|
||||
fmt.Printf("<svg xmlns='http://www.w3.org/2000/svg' "+
|
||||
"style='stroke: grey; fill: white; stroke-width: 0.7' "+
|
||||
"width='%d' height='%d'>", width, height)
|
||||
for i := 0; i < cells; i++ {
|
||||
for j := 0; j < cells; j++ {
|
||||
ax, ay := corner(i+1, j)
|
||||
bx, by := corner(i, j)
|
||||
cx, cy := corner(i, j+1)
|
||||
dx, dy := corner(i+1, j+1)
|
||||
fmt.Printf("<polygon points='%g,%g %g,%g %g,%g %g,%g'/>\n",
|
||||
ax, ay, bx, by, cx, cy, dx, dy)
|
||||
}
|
||||
}
|
||||
fmt.Println("</svg>")
|
||||
}
|
||||
|
||||
func corner(i, j int) (float64, float64) {
|
||||
// Find point (x,y) at corner of cell (i,j).
|
||||
x := xyrange * (float64(i)/cells - 0.5)
|
||||
y := xyrange * (float64(j)/cells - 0.5)
|
||||
|
||||
// Compute surface height z.
|
||||
z := f(x, y)
|
||||
|
||||
// Project (x,y,z) isometrically onto 2-D SVG canvas (sx,sy).
|
||||
sx := width/2 + (x-y)*cos30*xyscale
|
||||
sy := height/2 + (x+y)*sin30*xyscale - z*zscale
|
||||
return sx, sy
|
||||
}
|
||||
|
||||
func f(x, y float64) float64 {
|
||||
r := math.Hypot(x, y) // distance from (0,0)
|
||||
return math.Sin(r) / r
|
||||
}
|
||||
|
||||
//!-
|
||||
79
vendor/gopl.io/ch4/append/main.go
generated
vendored
Normal file
79
vendor/gopl.io/ch4/append/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 88.
|
||||
|
||||
// Append illustrates the behavior of the built-in append function.
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func appendslice(x []int, y ...int) []int {
|
||||
var z []int
|
||||
zlen := len(x) + len(y)
|
||||
if zlen <= cap(x) {
|
||||
// There is room to expand the slice.
|
||||
z = x[:zlen]
|
||||
} else {
|
||||
// There is insufficient space.
|
||||
// Grow by doubling, for amortized linear complexity.
|
||||
zcap := zlen
|
||||
if zcap < 2*len(x) {
|
||||
zcap = 2 * len(x)
|
||||
}
|
||||
z = make([]int, zlen, zcap)
|
||||
copy(z, x)
|
||||
}
|
||||
copy(z[len(x):], y)
|
||||
return z
|
||||
}
|
||||
|
||||
//!+append
|
||||
func appendInt(x []int, y int) []int {
|
||||
var z []int
|
||||
zlen := len(x) + 1
|
||||
if zlen <= cap(x) {
|
||||
// There is room to grow. Extend the slice.
|
||||
z = x[:zlen]
|
||||
} else {
|
||||
// There is insufficient space. Allocate a new array.
|
||||
// Grow by doubling, for amortized linear complexity.
|
||||
zcap := zlen
|
||||
if zcap < 2*len(x) {
|
||||
zcap = 2 * len(x)
|
||||
}
|
||||
z = make([]int, zlen, zcap)
|
||||
copy(z, x) // a built-in function; see text
|
||||
}
|
||||
z[len(x)] = y
|
||||
return z
|
||||
}
|
||||
|
||||
//!-append
|
||||
|
||||
//!+growth
|
||||
func main() {
|
||||
var x, y []int
|
||||
for i := 0; i < 10; i++ {
|
||||
y = appendInt(x, i)
|
||||
fmt.Printf("%d cap=%d\t%v\n", i, cap(y), y)
|
||||
x = y
|
||||
}
|
||||
}
|
||||
|
||||
//!-growth
|
||||
|
||||
/*
|
||||
//!+output
|
||||
0 cap=1 [0]
|
||||
1 cap=2 [0 1]
|
||||
2 cap=4 [0 1 2]
|
||||
3 cap=4 [0 1 2 3]
|
||||
4 cap=8 [0 1 2 3 4]
|
||||
5 cap=8 [0 1 2 3 4 5]
|
||||
6 cap=8 [0 1 2 3 4 5 6]
|
||||
7 cap=8 [0 1 2 3 4 5 6 7]
|
||||
8 cap=16 [0 1 2 3 4 5 6 7 8]
|
||||
9 cap=16 [0 1 2 3 4 5 6 7 8 9]
|
||||
//!-output
|
||||
*/
|
||||
30
vendor/gopl.io/ch4/autoescape/main.go
generated
vendored
Normal file
30
vendor/gopl.io/ch4/autoescape/main.go
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 117.
|
||||
|
||||
// Autoescape demonstrates automatic HTML escaping in html/template.
|
||||
package main
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
//!+
|
||||
func main() {
|
||||
const templ = `<p>A: {{.A}}</p><p>B: {{.B}}</p>`
|
||||
t := template.Must(template.New("escape").Parse(templ))
|
||||
var data struct {
|
||||
A string // untrusted plain text
|
||||
B template.HTML // trusted HTML
|
||||
}
|
||||
data.A = "<b>Hello!</b>"
|
||||
data.B = "<b>Hello!</b>"
|
||||
if err := t.Execute(os.Stdout, data); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
//!-
|
||||
56
vendor/gopl.io/ch4/charcount/main.go
generated
vendored
Normal file
56
vendor/gopl.io/ch4/charcount/main.go
generated
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 97.
|
||||
//!+
|
||||
|
||||
// Charcount computes counts of Unicode characters.
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
func main() {
|
||||
counts := make(map[rune]int) // counts of Unicode characters
|
||||
var utflen [utf8.UTFMax + 1]int // count of lengths of UTF-8 encodings
|
||||
invalid := 0 // count of invalid UTF-8 characters
|
||||
|
||||
in := bufio.NewReader(os.Stdin)
|
||||
for {
|
||||
r, n, err := in.ReadRune() // returns rune, nbytes, error
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "charcount: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if r == unicode.ReplacementChar && n == 1 {
|
||||
invalid++
|
||||
continue
|
||||
}
|
||||
counts[r]++
|
||||
utflen[n]++
|
||||
}
|
||||
fmt.Printf("rune\tcount\n")
|
||||
for c, n := range counts {
|
||||
fmt.Printf("%q\t%d\n", c, n)
|
||||
}
|
||||
fmt.Print("\nlen\tcount\n")
|
||||
for i, n := range utflen {
|
||||
if i > 0 {
|
||||
fmt.Printf("%d\t%d\n", i, n)
|
||||
}
|
||||
}
|
||||
if invalid > 0 {
|
||||
fmt.Printf("\n%d invalid UTF-8 characters\n", invalid)
|
||||
}
|
||||
}
|
||||
|
||||
//!-
|
||||
33
vendor/gopl.io/ch4/dedup/main.go
generated
vendored
Normal file
33
vendor/gopl.io/ch4/dedup/main.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 96.
|
||||
|
||||
// Dedup prints only one instance of each line; duplicates are removed.
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
//!+
|
||||
func main() {
|
||||
seen := make(map[string]bool) // a set of strings
|
||||
input := bufio.NewScanner(os.Stdin)
|
||||
for input.Scan() {
|
||||
line := input.Text()
|
||||
if !seen[line] {
|
||||
seen[line] = true
|
||||
fmt.Println(line)
|
||||
}
|
||||
}
|
||||
|
||||
if err := input.Err(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "dedup: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
//!-
|
||||
46
vendor/gopl.io/ch4/embed/main.go
generated
vendored
Normal file
46
vendor/gopl.io/ch4/embed/main.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 106.
|
||||
|
||||
// Embed demonstrates basic struct embedding.
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type Point struct{ X, Y int }
|
||||
|
||||
type Circle struct {
|
||||
Point
|
||||
Radius int
|
||||
}
|
||||
|
||||
type Wheel struct {
|
||||
Circle
|
||||
Spokes int
|
||||
}
|
||||
|
||||
func main() {
|
||||
var w Wheel
|
||||
//!+
|
||||
w = Wheel{Circle{Point{8, 8}, 5}, 20}
|
||||
|
||||
w = Wheel{
|
||||
Circle: Circle{
|
||||
Point: Point{X: 8, Y: 8},
|
||||
Radius: 5,
|
||||
},
|
||||
Spokes: 20, // NOTE: trailing comma necessary here (and at Radius)
|
||||
}
|
||||
|
||||
fmt.Printf("%#v\n", w)
|
||||
// Output:
|
||||
// Wheel{Circle:Circle{Point:Point{X:8, Y:8}, Radius:5}, Spokes:20}
|
||||
|
||||
w.X = 42
|
||||
|
||||
fmt.Printf("%#v\n", w)
|
||||
// Output:
|
||||
// Wheel{Circle:Circle{Point:Point{X:42, Y:8}, Radius:5}, Spokes:20}
|
||||
//!-
|
||||
}
|
||||
35
vendor/gopl.io/ch4/github/github.go
generated
vendored
Normal file
35
vendor/gopl.io/ch4/github/github.go
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 110.
|
||||
//!+
|
||||
|
||||
// Package github provides a Go API for the GitHub issue tracker.
|
||||
// See https://developer.github.com/v3/search/#search-issues.
|
||||
package github
|
||||
|
||||
import "time"
|
||||
|
||||
const IssuesURL = "https://api.github.com/search/issues"
|
||||
|
||||
type IssuesSearchResult struct {
|
||||
TotalCount int `json:"total_count"`
|
||||
Items []*Issue
|
||||
}
|
||||
|
||||
type Issue struct {
|
||||
Number int
|
||||
HTMLURL string `json:"html_url"`
|
||||
Title string
|
||||
State string
|
||||
User *User
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
Body string // in Markdown format
|
||||
}
|
||||
|
||||
type User struct {
|
||||
Login string
|
||||
HTMLURL string `json:"html_url"`
|
||||
}
|
||||
|
||||
//!-
|
||||
53
vendor/gopl.io/ch4/github/search.go
generated
vendored
Normal file
53
vendor/gopl.io/ch4/github/search.go
generated
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
//!+
|
||||
|
||||
package github
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// SearchIssues queries the GitHub issue tracker.
|
||||
func SearchIssues(terms []string) (*IssuesSearchResult, error) {
|
||||
q := url.QueryEscape(strings.Join(terms, " "))
|
||||
resp, err := http.Get(IssuesURL + "?q=" + q)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
//!-
|
||||
// For long-term stability, instead of http.Get, use the
|
||||
// variant below which adds an HTTP request header indicating
|
||||
// that only version 3 of the GitHub API is acceptable.
|
||||
//
|
||||
// req, err := http.NewRequest("GET", IssuesURL+"?q="+q, nil)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// req.Header.Set(
|
||||
// "Accept", "application/vnd.github.v3.text-match+json")
|
||||
// resp, err := http.DefaultClient.Do(req)
|
||||
//!+
|
||||
|
||||
// We must close resp.Body on all execution paths.
|
||||
// (Chapter 5 presents 'defer', which makes this simpler.)
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
resp.Body.Close()
|
||||
return nil, fmt.Errorf("search query failed: %s", resp.Status)
|
||||
}
|
||||
|
||||
var result IssuesSearchResult
|
||||
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
|
||||
resp.Body.Close()
|
||||
return nil, err
|
||||
}
|
||||
resp.Body.Close()
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
//!-
|
||||
43
vendor/gopl.io/ch4/graph/main.go
generated
vendored
Normal file
43
vendor/gopl.io/ch4/graph/main.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 99.
|
||||
|
||||
// Graph shows how to use a map of maps to represent a directed graph.
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
//!+
|
||||
var graph = make(map[string]map[string]bool)
|
||||
|
||||
func addEdge(from, to string) {
|
||||
edges := graph[from]
|
||||
if edges == nil {
|
||||
edges = make(map[string]bool)
|
||||
graph[from] = edges
|
||||
}
|
||||
edges[to] = true
|
||||
}
|
||||
|
||||
func hasEdge(from, to string) bool {
|
||||
return graph[from][to]
|
||||
}
|
||||
|
||||
//!-
|
||||
|
||||
func main() {
|
||||
addEdge("a", "b")
|
||||
addEdge("c", "d")
|
||||
addEdge("a", "d")
|
||||
addEdge("d", "a")
|
||||
fmt.Println(hasEdge("a", "b"))
|
||||
fmt.Println(hasEdge("c", "d"))
|
||||
fmt.Println(hasEdge("a", "d"))
|
||||
fmt.Println(hasEdge("d", "a"))
|
||||
fmt.Println(hasEdge("x", "b"))
|
||||
fmt.Println(hasEdge("c", "d"))
|
||||
fmt.Println(hasEdge("x", "d"))
|
||||
fmt.Println(hasEdge("d", "x"))
|
||||
|
||||
}
|
||||
52
vendor/gopl.io/ch4/issues/main.go
generated
vendored
Normal file
52
vendor/gopl.io/ch4/issues/main.go
generated
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 112.
|
||||
//!+
|
||||
|
||||
// Issues prints a table of GitHub issues matching the search terms.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"gopl.io/ch4/github"
|
||||
)
|
||||
|
||||
//!+
|
||||
func main() {
|
||||
result, err := github.SearchIssues(os.Args[1:])
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Printf("%d issues:\n", result.TotalCount)
|
||||
for _, item := range result.Items {
|
||||
fmt.Printf("#%-5d %9.9s %.55s\n",
|
||||
item.Number, item.User.Login, item.Title)
|
||||
}
|
||||
}
|
||||
|
||||
//!-
|
||||
|
||||
/*
|
||||
//!+textoutput
|
||||
$ go build gopl.io/ch4/issues
|
||||
$ ./issues repo:golang/go is:open json decoder
|
||||
13 issues:
|
||||
#5680 eaigner encoding/json: set key converter on en/decoder
|
||||
#6050 gopherbot encoding/json: provide tokenizer
|
||||
#8658 gopherbot encoding/json: use bufio
|
||||
#8462 kortschak encoding/json: UnmarshalText confuses json.Unmarshal
|
||||
#5901 rsc encoding/json: allow override type marshaling
|
||||
#9812 klauspost encoding/json: string tag not symmetric
|
||||
#7872 extempora encoding/json: Encoder internally buffers full output
|
||||
#9650 cespare encoding/json: Decoding gives errPhase when unmarshalin
|
||||
#6716 gopherbot encoding/json: include field name in unmarshal error me
|
||||
#6901 lukescott encoding/json, encoding/xml: option to treat unknown fi
|
||||
#6384 joeshaw encoding/json: encode precise floating point integers u
|
||||
#6647 btracey x/tools/cmd/godoc: display type kind of each named type
|
||||
#4237 gjemiller encoding/base64: URLEncoding padding is optional
|
||||
//!-textoutput
|
||||
*/
|
||||
52
vendor/gopl.io/ch4/issueshtml/main.go
generated
vendored
Normal file
52
vendor/gopl.io/ch4/issueshtml/main.go
generated
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 115.
|
||||
|
||||
// Issueshtml prints an HTML table of issues matching the search terms.
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"gopl.io/ch4/github"
|
||||
)
|
||||
|
||||
//!+template
|
||||
import "html/template"
|
||||
|
||||
var issueList = template.Must(template.New("issuelist").Parse(`
|
||||
<h1>{{.TotalCount}} issues</h1>
|
||||
<table>
|
||||
<tr style='text-align: left'>
|
||||
<th>#</th>
|
||||
<th>State</th>
|
||||
<th>User</th>
|
||||
<th>Title</th>
|
||||
</tr>
|
||||
{{range .Items}}
|
||||
<tr>
|
||||
<td><a href='{{.HTMLURL}}'>{{.Number}}</a></td>
|
||||
<td>{{.State}}</td>
|
||||
<td><a href='{{.User.HTMLURL}}'>{{.User.Login}}</a></td>
|
||||
<td><a href='{{.HTMLURL}}'>{{.Title}}</a></td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</table>
|
||||
`))
|
||||
|
||||
//!-template
|
||||
|
||||
//!+
|
||||
func main() {
|
||||
result, err := github.SearchIssues(os.Args[1:])
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if err := issueList.Execute(os.Stdout, result); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
//!-
|
||||
89
vendor/gopl.io/ch4/issuesreport/main.go
generated
vendored
Normal file
89
vendor/gopl.io/ch4/issuesreport/main.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 113.
|
||||
|
||||
// Issuesreport prints a report of issues matching the search terms.
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"gopl.io/ch4/github"
|
||||
)
|
||||
|
||||
//!+template
|
||||
const templ = `{{.TotalCount}} issues:
|
||||
{{range .Items}}----------------------------------------
|
||||
Number: {{.Number}}
|
||||
User: {{.User.Login}}
|
||||
Title: {{.Title | printf "%.64s"}}
|
||||
Age: {{.CreatedAt | daysAgo}} days
|
||||
{{end}}`
|
||||
|
||||
//!-template
|
||||
|
||||
//!+daysAgo
|
||||
func daysAgo(t time.Time) int {
|
||||
return int(time.Since(t).Hours() / 24)
|
||||
}
|
||||
|
||||
//!-daysAgo
|
||||
|
||||
//!+exec
|
||||
var report = template.Must(template.New("issuelist").
|
||||
Funcs(template.FuncMap{"daysAgo": daysAgo}).
|
||||
Parse(templ))
|
||||
|
||||
func main() {
|
||||
result, err := github.SearchIssues(os.Args[1:])
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if err := report.Execute(os.Stdout, result); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
//!-exec
|
||||
|
||||
func noMust() {
|
||||
//!+parse
|
||||
report, err := template.New("report").
|
||||
Funcs(template.FuncMap{"daysAgo": daysAgo}).
|
||||
Parse(templ)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
//!-parse
|
||||
result, err := github.SearchIssues(os.Args[1:])
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if err := report.Execute(os.Stdout, result); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
//!+output
|
||||
$ go build gopl.io/ch4/issuesreport
|
||||
$ ./issuesreport repo:golang/go is:open json decoder
|
||||
13 issues:
|
||||
----------------------------------------
|
||||
Number: 5680
|
||||
User: eaigner
|
||||
Title: encoding/json: set key converter on en/decoder
|
||||
Age: 750 days
|
||||
----------------------------------------
|
||||
Number: 6050
|
||||
User: gopherbot
|
||||
Title: encoding/json: provide tokenizer
|
||||
Age: 695 days
|
||||
----------------------------------------
|
||||
...
|
||||
//!-output
|
||||
*/
|
||||
104
vendor/gopl.io/ch4/movie/main.go
generated
vendored
Normal file
104
vendor/gopl.io/ch4/movie/main.go
generated
vendored
Normal file
@@ -0,0 +1,104 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 108.
|
||||
|
||||
// Movie prints Movies as JSON.
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
)
|
||||
|
||||
//!+
|
||||
type Movie struct {
|
||||
Title string
|
||||
Year int `json:"released"`
|
||||
Color bool `json:"color,omitempty"`
|
||||
Actors []string
|
||||
}
|
||||
|
||||
var movies = []Movie{
|
||||
{Title: "Casablanca", Year: 1942, Color: false,
|
||||
Actors: []string{"Humphrey Bogart", "Ingrid Bergman"}},
|
||||
{Title: "Cool Hand Luke", Year: 1967, Color: true,
|
||||
Actors: []string{"Paul Newman"}},
|
||||
{Title: "Bullitt", Year: 1968, Color: true,
|
||||
Actors: []string{"Steve McQueen", "Jacqueline Bisset"}},
|
||||
// ...
|
||||
}
|
||||
|
||||
//!-
|
||||
|
||||
func main() {
|
||||
{
|
||||
//!+Marshal
|
||||
data, err := json.Marshal(movies)
|
||||
if err != nil {
|
||||
log.Fatalf("JSON marshaling failed: %s", err)
|
||||
}
|
||||
fmt.Printf("%s\n", data)
|
||||
//!-Marshal
|
||||
}
|
||||
|
||||
{
|
||||
//!+MarshalIndent
|
||||
data, err := json.MarshalIndent(movies, "", " ")
|
||||
if err != nil {
|
||||
log.Fatalf("JSON marshaling failed: %s", err)
|
||||
}
|
||||
fmt.Printf("%s\n", data)
|
||||
//!-MarshalIndent
|
||||
|
||||
//!+Unmarshal
|
||||
var titles []struct{ Title string }
|
||||
if err := json.Unmarshal(data, &titles); err != nil {
|
||||
log.Fatalf("JSON unmarshaling failed: %s", err)
|
||||
}
|
||||
fmt.Println(titles) // "[{Casablanca} {Cool Hand Luke} {Bullitt}]"
|
||||
//!-Unmarshal
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
//!+output
|
||||
[{"Title":"Casablanca","released":1942,"Actors":["Humphrey Bogart","Ingr
|
||||
id Bergman"]},{"Title":"Cool Hand Luke","released":1967,"color":true,"Ac
|
||||
tors":["Paul Newman"]},{"Title":"Bullitt","released":1968,"color":true,"
|
||||
Actors":["Steve McQueen","Jacqueline Bisset"]}]
|
||||
//!-output
|
||||
*/
|
||||
|
||||
/*
|
||||
//!+indented
|
||||
[
|
||||
{
|
||||
"Title": "Casablanca",
|
||||
"released": 1942,
|
||||
"Actors": [
|
||||
"Humphrey Bogart",
|
||||
"Ingrid Bergman"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Title": "Cool Hand Luke",
|
||||
"released": 1967,
|
||||
"color": true,
|
||||
"Actors": [
|
||||
"Paul Newman"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Title": "Bullitt",
|
||||
"released": 1968,
|
||||
"color": true,
|
||||
"Actors": [
|
||||
"Steve McQueen",
|
||||
"Jacqueline Bisset"
|
||||
]
|
||||
}
|
||||
]
|
||||
//!-indented
|
||||
*/
|
||||
47
vendor/gopl.io/ch4/nonempty/main.go
generated
vendored
Normal file
47
vendor/gopl.io/ch4/nonempty/main.go
generated
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 91.
|
||||
|
||||
//!+nonempty
|
||||
|
||||
// Nonempty is an example of an in-place slice algorithm.
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
// nonempty returns a slice holding only the non-empty strings.
|
||||
// The underlying array is modified during the call.
|
||||
func nonempty(strings []string) []string {
|
||||
i := 0
|
||||
for _, s := range strings {
|
||||
if s != "" {
|
||||
strings[i] = s
|
||||
i++
|
||||
}
|
||||
}
|
||||
return strings[:i]
|
||||
}
|
||||
|
||||
//!-nonempty
|
||||
|
||||
func main() {
|
||||
//!+main
|
||||
data := []string{"one", "", "three"}
|
||||
fmt.Printf("%q\n", nonempty(data)) // `["one" "three"]`
|
||||
fmt.Printf("%q\n", data) // `["one" "three" "three"]`
|
||||
//!-main
|
||||
}
|
||||
|
||||
//!+alt
|
||||
func nonempty2(strings []string) []string {
|
||||
out := strings[:0] // zero-length slice of original
|
||||
for _, s := range strings {
|
||||
if s != "" {
|
||||
out = append(out, s)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
//!-alt
|
||||
60
vendor/gopl.io/ch4/rev/main.go
generated
vendored
Normal file
60
vendor/gopl.io/ch4/rev/main.go
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 86.
|
||||
|
||||
// Rev reverses a slice.
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func main() {
|
||||
//!+array
|
||||
a := [...]int{0, 1, 2, 3, 4, 5}
|
||||
reverse(a[:])
|
||||
fmt.Println(a) // "[5 4 3 2 1 0]"
|
||||
//!-array
|
||||
|
||||
//!+slice
|
||||
s := []int{0, 1, 2, 3, 4, 5}
|
||||
// Rotate s left by two positions.
|
||||
reverse(s[:2])
|
||||
reverse(s[2:])
|
||||
reverse(s)
|
||||
fmt.Println(s) // "[2 3 4 5 0 1]"
|
||||
//!-slice
|
||||
|
||||
// Interactive test of reverse.
|
||||
input := bufio.NewScanner(os.Stdin)
|
||||
outer:
|
||||
for input.Scan() {
|
||||
var ints []int
|
||||
for _, s := range strings.Fields(input.Text()) {
|
||||
x, err := strconv.ParseInt(s, 10, 64)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
continue outer
|
||||
}
|
||||
ints = append(ints, int(x))
|
||||
}
|
||||
reverse(ints)
|
||||
fmt.Printf("%v\n", ints)
|
||||
}
|
||||
// NOTE: ignoring potential errors from input.Err()
|
||||
}
|
||||
|
||||
//!+rev
|
||||
// reverse reverses a slice of ints in place.
|
||||
func reverse(s []int) {
|
||||
for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
|
||||
s[i], s[j] = s[j], s[i]
|
||||
}
|
||||
}
|
||||
|
||||
//!-rev
|
||||
25
vendor/gopl.io/ch4/sha256/main.go
generated
vendored
Normal file
25
vendor/gopl.io/ch4/sha256/main.go
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 83.
|
||||
|
||||
// The sha256 command computes the SHA256 hash (an array) of a string.
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
//!+
|
||||
import "crypto/sha256"
|
||||
|
||||
func main() {
|
||||
c1 := sha256.Sum256([]byte("x"))
|
||||
c2 := sha256.Sum256([]byte("X"))
|
||||
fmt.Printf("%x\n%x\n%t\n%T\n", c1, c2, c1 == c2, c1)
|
||||
// Output:
|
||||
// 2d711642b726b04401627ca9fbac32f5c8530fb1903cc4db02258717921a4881
|
||||
// 4b68ab3847feda7d6c62c1fbcbeebfa35eab7351ed5e78f4ddadea5df64b8015
|
||||
// false
|
||||
// [32]uint8
|
||||
}
|
||||
|
||||
//!-
|
||||
50
vendor/gopl.io/ch4/treesort/sort.go
generated
vendored
Normal file
50
vendor/gopl.io/ch4/treesort/sort.go
generated
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 101.
|
||||
|
||||
// Package treesort provides insertion sort using an unbalanced binary tree.
|
||||
package treesort
|
||||
|
||||
//!+
|
||||
type tree struct {
|
||||
value int
|
||||
left, right *tree
|
||||
}
|
||||
|
||||
// Sort sorts values in place.
|
||||
func Sort(values []int) {
|
||||
var root *tree
|
||||
for _, v := range values {
|
||||
root = add(root, v)
|
||||
}
|
||||
appendValues(values[:0], root)
|
||||
}
|
||||
|
||||
// appendValues appends the elements of t to values in order
|
||||
// and returns the resulting slice.
|
||||
func appendValues(values []int, t *tree) []int {
|
||||
if t != nil {
|
||||
values = appendValues(values, t.left)
|
||||
values = append(values, t.value)
|
||||
values = appendValues(values, t.right)
|
||||
}
|
||||
return values
|
||||
}
|
||||
|
||||
func add(t *tree, value int) *tree {
|
||||
if t == nil {
|
||||
// Equivalent to return &tree{value: value}.
|
||||
t = new(tree)
|
||||
t.value = value
|
||||
return t
|
||||
}
|
||||
if value < t.value {
|
||||
t.left = add(t.left, value)
|
||||
} else {
|
||||
t.right = add(t.right, value)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
//!-
|
||||
23
vendor/gopl.io/ch4/treesort/sort_test.go
generated
vendored
Normal file
23
vendor/gopl.io/ch4/treesort/sort_test.go
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
package treesort_test
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"gopl.io/ch4/treesort"
|
||||
)
|
||||
|
||||
func TestSort(t *testing.T) {
|
||||
data := make([]int, 50)
|
||||
for i := range data {
|
||||
data[i] = rand.Int() % 50
|
||||
}
|
||||
treesort.Sort(data)
|
||||
if !sort.IntsAreSorted(data) {
|
||||
t.Errorf("not sorted: %v", data)
|
||||
}
|
||||
}
|
||||
48
vendor/gopl.io/ch5/defer1/defer.go
generated
vendored
Normal file
48
vendor/gopl.io/ch5/defer1/defer.go
generated
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 150.
|
||||
|
||||
// Defer1 demonstrates a deferred call being invoked during a panic.
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
//!+f
|
||||
func main() {
|
||||
f(3)
|
||||
}
|
||||
|
||||
func f(x int) {
|
||||
fmt.Printf("f(%d)\n", x+0/x) // panics if x == 0
|
||||
defer fmt.Printf("defer %d\n", x)
|
||||
f(x - 1)
|
||||
}
|
||||
|
||||
//!-f
|
||||
|
||||
/*
|
||||
//!+stdout
|
||||
f(3)
|
||||
f(2)
|
||||
f(1)
|
||||
defer 1
|
||||
defer 2
|
||||
defer 3
|
||||
//!-stdout
|
||||
|
||||
//!+stderr
|
||||
panic: runtime error: integer divide by zero
|
||||
main.f(0)
|
||||
src/gopl.io/ch5/defer1/defer.go:14
|
||||
main.f(1)
|
||||
src/gopl.io/ch5/defer1/defer.go:16
|
||||
main.f(2)
|
||||
src/gopl.io/ch5/defer1/defer.go:16
|
||||
|
||||
main.f(3)
|
||||
src/gopl.io/ch5/defer1/defer.go:16
|
||||
main.main()
|
||||
src/gopl.io/ch5/defer1/defer.go:10
|
||||
//!-stderr
|
||||
*/
|
||||
51
vendor/gopl.io/ch5/defer2/defer.go
generated
vendored
Normal file
51
vendor/gopl.io/ch5/defer2/defer.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 151.
|
||||
|
||||
// Defer2 demonstrates a deferred call to runtime.Stack during a panic.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
//!+
|
||||
func main() {
|
||||
defer printStack()
|
||||
f(3)
|
||||
}
|
||||
|
||||
func printStack() {
|
||||
var buf [4096]byte
|
||||
n := runtime.Stack(buf[:], false)
|
||||
os.Stdout.Write(buf[:n])
|
||||
}
|
||||
|
||||
//!-
|
||||
|
||||
func f(x int) {
|
||||
fmt.Printf("f(%d)\n", x+0/x) // panics if x == 0
|
||||
defer fmt.Printf("defer %d\n", x)
|
||||
f(x - 1)
|
||||
}
|
||||
|
||||
/*
|
||||
//!+printstack
|
||||
goroutine 1 [running]:
|
||||
main.printStack()
|
||||
src/gopl.io/ch5/defer2/defer.go:20
|
||||
main.f(0)
|
||||
src/gopl.io/ch5/defer2/defer.go:27
|
||||
main.f(1)
|
||||
src/gopl.io/ch5/defer2/defer.go:29
|
||||
main.f(2)
|
||||
src/gopl.io/ch5/defer2/defer.go:29
|
||||
main.f(3)
|
||||
src/gopl.io/ch5/defer2/defer.go:29
|
||||
main.main()
|
||||
src/gopl.io/ch5/defer2/defer.go:15
|
||||
//!-printstack
|
||||
*/
|
||||
54
vendor/gopl.io/ch5/fetch/main.go
generated
vendored
Normal file
54
vendor/gopl.io/ch5/fetch/main.go
generated
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 148.
|
||||
|
||||
// Fetch saves the contents of a URL into a local file.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
)
|
||||
|
||||
//!+
|
||||
// Fetch downloads the URL and returns the
|
||||
// name and length of the local file.
|
||||
func fetch(url string) (filename string, n int64, err error) {
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
local := path.Base(resp.Request.URL.Path)
|
||||
if local == "/" {
|
||||
local = "index.html"
|
||||
}
|
||||
f, err := os.Create(local)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
n, err = io.Copy(f, resp.Body)
|
||||
// Close file, but prefer error from Copy, if any.
|
||||
if closeErr := f.Close(); err == nil {
|
||||
err = closeErr
|
||||
}
|
||||
return local, n, err
|
||||
}
|
||||
|
||||
//!-
|
||||
|
||||
func main() {
|
||||
for _, url := range os.Args[1:] {
|
||||
local, n, err := fetch(url)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "fetch %s: %v\n", url, err)
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "%s => %s (%d bytes).\n", url, local, n)
|
||||
}
|
||||
}
|
||||
76
vendor/gopl.io/ch5/findlinks1/main.go
generated
vendored
Normal file
76
vendor/gopl.io/ch5/findlinks1/main.go
generated
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 122.
|
||||
//!+main
|
||||
|
||||
// Findlinks1 prints the links in an HTML document read from standard input.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"golang.org/x/net/html"
|
||||
)
|
||||
|
||||
func main() {
|
||||
doc, err := html.Parse(os.Stdin)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "findlinks1: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
for _, link := range visit(nil, doc) {
|
||||
fmt.Println(link)
|
||||
}
|
||||
}
|
||||
|
||||
//!-main
|
||||
|
||||
//!+visit
|
||||
// visit appends to links each link found in n and returns the result.
|
||||
func visit(links []string, n *html.Node) []string {
|
||||
if n.Type == html.ElementNode && n.Data == "a" {
|
||||
for _, a := range n.Attr {
|
||||
if a.Key == "href" {
|
||||
links = append(links, a.Val)
|
||||
}
|
||||
}
|
||||
}
|
||||
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||
links = visit(links, c)
|
||||
}
|
||||
return links
|
||||
}
|
||||
|
||||
//!-visit
|
||||
|
||||
/*
|
||||
//!+html
|
||||
package html
|
||||
|
||||
type Node struct {
|
||||
Type NodeType
|
||||
Data string
|
||||
Attr []Attribute
|
||||
FirstChild, NextSibling *Node
|
||||
}
|
||||
|
||||
type NodeType int32
|
||||
|
||||
const (
|
||||
ErrorNode NodeType = iota
|
||||
TextNode
|
||||
DocumentNode
|
||||
ElementNode
|
||||
CommentNode
|
||||
DoctypeNode
|
||||
)
|
||||
|
||||
type Attribute struct {
|
||||
Key, Val string
|
||||
}
|
||||
|
||||
func Parse(r io.Reader) (*Node, error)
|
||||
//!-html
|
||||
*/
|
||||
69
vendor/gopl.io/ch5/findlinks2/main.go
generated
vendored
Normal file
69
vendor/gopl.io/ch5/findlinks2/main.go
generated
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 125.
|
||||
|
||||
// Findlinks2 does an HTTP GET on each URL, parses the
|
||||
// result as HTML, and prints the links within it.
|
||||
//
|
||||
// Usage:
|
||||
// findlinks url ...
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"golang.org/x/net/html"
|
||||
)
|
||||
|
||||
// visit appends to links each link found in n, and returns the result.
|
||||
func visit(links []string, n *html.Node) []string {
|
||||
if n.Type == html.ElementNode && n.Data == "a" {
|
||||
for _, a := range n.Attr {
|
||||
if a.Key == "href" {
|
||||
links = append(links, a.Val)
|
||||
}
|
||||
}
|
||||
}
|
||||
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||
links = visit(links, c)
|
||||
}
|
||||
return links
|
||||
}
|
||||
|
||||
//!+
|
||||
func main() {
|
||||
for _, url := range os.Args[1:] {
|
||||
links, err := findLinks(url)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "findlinks2: %v\n", err)
|
||||
continue
|
||||
}
|
||||
for _, link := range links {
|
||||
fmt.Println(link)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// findLinks performs an HTTP GET request for url, parses the
|
||||
// response as HTML, and extracts and returns the links.
|
||||
func findLinks(url string) ([]string, error) {
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
resp.Body.Close()
|
||||
return nil, fmt.Errorf("getting %s: %s", url, resp.Status)
|
||||
}
|
||||
doc, err := html.Parse(resp.Body)
|
||||
resp.Body.Close()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing %s as HTML: %v", url, err)
|
||||
}
|
||||
return visit(nil, doc), nil
|
||||
}
|
||||
|
||||
//!-
|
||||
56
vendor/gopl.io/ch5/findlinks3/findlinks.go
generated
vendored
Normal file
56
vendor/gopl.io/ch5/findlinks3/findlinks.go
generated
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 139.
|
||||
|
||||
// Findlinks3 crawls the web, starting with the URLs on the command line.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"gopl.io/ch5/links"
|
||||
)
|
||||
|
||||
//!+breadthFirst
|
||||
// breadthFirst calls f for each item in the worklist.
|
||||
// Any items returned by f are added to the worklist.
|
||||
// f is called at most once for each item.
|
||||
func breadthFirst(f func(item string) []string, worklist []string) {
|
||||
seen := make(map[string]bool)
|
||||
for len(worklist) > 0 {
|
||||
items := worklist
|
||||
worklist = nil
|
||||
for _, item := range items {
|
||||
if !seen[item] {
|
||||
seen[item] = true
|
||||
worklist = append(worklist, f(item)...)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//!-breadthFirst
|
||||
|
||||
//!+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() {
|
||||
// Crawl the web breadth-first,
|
||||
// starting from the command-line arguments.
|
||||
breadthFirst(crawl, os.Args[1:])
|
||||
}
|
||||
|
||||
//!-main
|
||||
67
vendor/gopl.io/ch5/links/links.go
generated
vendored
Normal file
67
vendor/gopl.io/ch5/links/links.go
generated
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 138.
|
||||
//!+Extract
|
||||
|
||||
// Package links provides a link-extraction function.
|
||||
package links
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"golang.org/x/net/html"
|
||||
)
|
||||
|
||||
// Extract makes an HTTP GET request to the specified URL, parses
|
||||
// the response as HTML, and returns the links in the HTML document.
|
||||
func Extract(url string) ([]string, error) {
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
resp.Body.Close()
|
||||
return nil, fmt.Errorf("getting %s: %s", url, resp.Status)
|
||||
}
|
||||
|
||||
doc, err := html.Parse(resp.Body)
|
||||
resp.Body.Close()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing %s as HTML: %v", url, err)
|
||||
}
|
||||
|
||||
var links []string
|
||||
visitNode := func(n *html.Node) {
|
||||
if n.Type == html.ElementNode && n.Data == "a" {
|
||||
for _, a := range n.Attr {
|
||||
if a.Key != "href" {
|
||||
continue
|
||||
}
|
||||
link, err := resp.Request.URL.Parse(a.Val)
|
||||
if err != nil {
|
||||
continue // ignore bad URLs
|
||||
}
|
||||
links = append(links, link.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
forEachNode(doc, visitNode, nil)
|
||||
return links, nil
|
||||
}
|
||||
|
||||
//!-Extract
|
||||
|
||||
// Copied from gopl.io/ch5/outline2.
|
||||
func forEachNode(n *html.Node, pre, post func(n *html.Node)) {
|
||||
if pre != nil {
|
||||
pre(n)
|
||||
}
|
||||
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||
forEachNode(c, pre, post)
|
||||
}
|
||||
if post != nil {
|
||||
post(n)
|
||||
}
|
||||
}
|
||||
36
vendor/gopl.io/ch5/outline/main.go
generated
vendored
Normal file
36
vendor/gopl.io/ch5/outline/main.go
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 123.
|
||||
|
||||
// Outline prints the outline of an HTML document tree.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"golang.org/x/net/html"
|
||||
)
|
||||
|
||||
//!+
|
||||
func main() {
|
||||
doc, err := html.Parse(os.Stdin)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "outline: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
outline(nil, doc)
|
||||
}
|
||||
|
||||
func outline(stack []string, n *html.Node) {
|
||||
if n.Type == html.ElementNode {
|
||||
stack = append(stack, n.Data) // push tag
|
||||
fmt.Println(stack)
|
||||
}
|
||||
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||
outline(stack, c)
|
||||
}
|
||||
}
|
||||
|
||||
//!-
|
||||
80
vendor/gopl.io/ch5/outline2/outline.go
generated
vendored
Normal file
80
vendor/gopl.io/ch5/outline2/outline.go
generated
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 133.
|
||||
|
||||
// Outline prints the outline of an HTML document tree.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"golang.org/x/net/html"
|
||||
)
|
||||
|
||||
func main() {
|
||||
for _, url := range os.Args[1:] {
|
||||
outline(url)
|
||||
}
|
||||
}
|
||||
|
||||
func outline(url string) error {
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
doc, err := html.Parse(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
//!+call
|
||||
forEachNode(doc, startElement, endElement)
|
||||
//!-call
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
//!+forEachNode
|
||||
// forEachNode calls the functions pre(x) and post(x) for each node
|
||||
// x in the tree rooted at n. Both functions are optional.
|
||||
// pre is called before the children are visited (preorder) and
|
||||
// post is called after (postorder).
|
||||
func forEachNode(n *html.Node, pre, post func(n *html.Node)) {
|
||||
if pre != nil {
|
||||
pre(n)
|
||||
}
|
||||
|
||||
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||
forEachNode(c, pre, post)
|
||||
}
|
||||
|
||||
if post != nil {
|
||||
post(n)
|
||||
}
|
||||
}
|
||||
|
||||
//!-forEachNode
|
||||
|
||||
//!+startend
|
||||
var depth int
|
||||
|
||||
func startElement(n *html.Node) {
|
||||
if n.Type == html.ElementNode {
|
||||
fmt.Printf("%*s<%s>\n", depth*2, "", n.Data)
|
||||
depth++
|
||||
}
|
||||
}
|
||||
|
||||
func endElement(n *html.Node) {
|
||||
if n.Type == html.ElementNode {
|
||||
depth--
|
||||
fmt.Printf("%*s</%s>\n", depth*2, "", n.Data)
|
||||
}
|
||||
}
|
||||
|
||||
//!-startend
|
||||
30
vendor/gopl.io/ch5/squares/main.go
generated
vendored
Normal file
30
vendor/gopl.io/ch5/squares/main.go
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
|
||||
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
// See page 135.
|
||||
|
||||
// The squares program demonstrates a function value with state.
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
//!+
|
||||
// squares returns a function that returns
|
||||
// the next square number each time it is called.
|
||||
func squares() func() int {
|
||||
var x int
|
||||
return func() int {
|
||||
x++
|
||||
return x * x
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
f := squares()
|
||||
fmt.Println(f()) // "1"
|
||||
fmt.Println(f()) // "4"
|
||||
fmt.Println(f()) // "9"
|
||||
fmt.Println(f()) // "16"
|
||||
}
|
||||
|
||||
//!-
|
||||
33
vendor/gopl.io/ch5/sum/main.go
generated
vendored
Normal file
33
vendor/gopl.io/ch5/sum/main.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 142.
|
||||
|
||||
// The sum program demonstrates a variadic function.
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
//!+
|
||||
func sum(vals ...int) int {
|
||||
total := 0
|
||||
for _, val := range vals {
|
||||
total += val
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
//!-
|
||||
|
||||
func main() {
|
||||
//!+main
|
||||
fmt.Println(sum()) // "0"
|
||||
fmt.Println(sum(3)) // "3"
|
||||
fmt.Println(sum(1, 2, 3, 4)) // "10"
|
||||
//!-main
|
||||
|
||||
//!+slice
|
||||
values := []int{1, 2, 3, 4}
|
||||
fmt.Println(sum(values...)) // "10"
|
||||
//!-slice
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user