// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// Test that the Go environment variables are present and accessible through
+// package os and package runtime.
+
package main
import (
)
func main() {
- ga, e0 := os.Getenverror("GOARCH")
- if e0 != nil {
- print("$GOARCH: ", e0.Error(), "\n")
- os.Exit(1)
- }
+ ga := os.Getenv("GOARCH")
if ga != runtime.GOARCH {
print("$GOARCH=", ga, "!= runtime.GOARCH=", runtime.GOARCH, "\n")
os.Exit(1)
}
- xxx, e1 := os.Getenverror("DOES_NOT_EXIST")
- if e1 != os.ENOENV {
- print("$DOES_NOT_EXIST=", xxx, "; err = ", e1.Error(), "\n")
+ xxx := os.Getenv("DOES_NOT_EXIST")
+ if xxx != "" {
+ print("$DOES_NOT_EXIST=", xxx, "\n")
os.Exit(1)
}
}
package main
import (
- "os"
+ "errors"
"strconv"
)
}
mm := make(map[string]error)
trace = ""
- mm["abc"] = os.EINVAL
+ mm["abc"] = errors.New("invalid")
*i(), mm[f()] = strconv.Atoi(h())
if mm["abc"] != nil || trace != "ifh" {
println("BUG1", mm["abc"], trace)
+++ /dev/null
-// $G $D/$F.go && $L $F.$A && ./$A.out
-
-// Copyright 2009 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.
-
-// trivial malloc test
-
-package main
-
-import (
- "flag"
- "fmt"
- "runtime"
-)
-
-var chatty = flag.Bool("v", false, "chatty")
-
-func main() {
- memstats := new(runtime.MemStats)
- runtime.Free(runtime.Alloc(1))
- runtime.ReadMemStats(memstats)
- if *chatty {
- fmt.Printf("%+v %v\n", memstats, uint64(0))
- }
-}
+++ /dev/null
-// $G $D/$F.go && $L $F.$A && ./$A.out
-
-// Copyright 2009 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.
-
-// Random malloc test.
-
-package main
-
-import (
- "flag"
- "math/rand"
- "runtime"
- "unsafe"
-)
-
-var chatty = flag.Bool("v", false, "chatty")
-
-var footprint uint64
-var allocated uint64
-
-func bigger() {
- memstats := new(runtime.MemStats)
- runtime.ReadMemStats(memstats)
- if f := memstats.Sys; footprint < f {
- footprint = f
- if *chatty {
- println("Footprint", footprint, " for ", allocated)
- }
- if footprint > 1e9 {
- println("too big")
- panic("fail")
- }
- }
-}
-
-// Prime the data structures by allocating one of
-// each block in order. After this, there should be
-// little reason to ask for more memory from the OS.
-func prime() {
- for i := 0; i < 16; i++ {
- b := runtime.Alloc(1 << uint(i))
- runtime.Free(b)
- }
- for i := uintptr(0); i < 256; i++ {
- b := runtime.Alloc(i << 12)
- runtime.Free(b)
- }
-}
-
-func memset(b *byte, c byte, n uintptr) {
- np := uintptr(n)
- for i := uintptr(0); i < np; i++ {
- *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(b)) + i)) = c
- }
-}
-
-func main() {
- flag.Parse()
- // prime()
- var blocks [1]struct {
- base *byte
- siz uintptr
- }
- for i := 0; i < 1<<10; i++ {
- if i%(1<<10) == 0 && *chatty {
- println(i)
- }
- b := rand.Int() % len(blocks)
- if blocks[b].base != nil {
- // println("Free", blocks[b].siz, blocks[b].base)
- runtime.Free(blocks[b].base)
- blocks[b].base = nil
- allocated -= uint64(blocks[b].siz)
- continue
- }
- siz := uintptr(rand.Int() >> (11 + rand.Uint32()%20))
- base := runtime.Alloc(siz)
- // ptr := uintptr(syscall.BytePtr(base))+uintptr(siz/2)
- // obj, size, ref, ok := allocator.find(ptr)
- // if obj != base || *ref != 0 || !ok {
- // println("find", siz, obj, ref, ok)
- // panic("fail")
- // }
- blocks[b].base = base
- blocks[b].siz = siz
- allocated += uint64(siz)
- // println("Alloc", siz, base)
- memset(base, 0xbb, siz)
- bigger()
- }
-}
+++ /dev/null
-// $G $D/$F.go && $L $F.$A && ./$A.out
-
-// Copyright 2009 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.
-
-// Repeated malloc test.
-
-package main
-
-import (
- "flag"
- "runtime"
-)
-
-var chatty = flag.Bool("v", false, "chatty")
-
-var oldsys uint64
-var memstats runtime.MemStats
-
-func bigger() {
- st := &memstats
- runtime.ReadMemStats(st)
- if oldsys < st.Sys {
- oldsys = st.Sys
- if *chatty {
- println(st.Sys, " system bytes for ", st.Alloc, " Go bytes")
- }
- if st.Sys > 1e9 {
- println("too big")
- panic("fail")
- }
- }
-}
-
-func main() {
- runtime.GC() // clean up garbage from init
- runtime.ReadMemStats(&memstats) // first call can do some allocations
- runtime.MemProfileRate = 0 // disable profiler
- stacks := memstats.Alloc // ignore stacks
- flag.Parse()
- for i := 0; i < 1<<7; i++ {
- for j := 1; j <= 1<<22; j <<= 1 {
- if i == 0 && *chatty {
- println("First alloc:", j)
- }
- if a := memstats.Alloc - stacks; a != 0 {
- println("no allocations but stats report", a, "bytes allocated")
- panic("fail")
- }
- b := runtime.Alloc(uintptr(j))
- runtime.ReadMemStats(&memstats)
- during := memstats.Alloc - stacks
- runtime.Free(b)
- runtime.ReadMemStats(&memstats)
- if a := memstats.Alloc - stacks; a != 0 {
- println("allocated ", j, ": wrong stats: during=", during, " after=", a, " (want 0)")
- panic("fail")
- }
- bigger()
- }
- if i%(1<<10) == 0 && *chatty {
- println(i)
- }
- if i == 0 {
- if *chatty {
- println("Primed", i)
- }
- // runtime.frozen = true
- }
- }
-}
+++ /dev/null
-// $G $D/$F.go && $L $F.$A && ./$A.out
-
-// Copyright 2009 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.
-
-// Repeated malloc test.
-
-package main
-
-import (
- "flag"
- "fmt"
- "runtime"
- "strconv"
-)
-
-var chatty = flag.Bool("v", false, "chatty")
-var reverse = flag.Bool("r", false, "reverse")
-var longtest = flag.Bool("l", false, "long test")
-
-var b []*byte
-var stats = new(runtime.MemStats)
-
-func OkAmount(size, n uintptr) bool {
- if n < size {
- return false
- }
- if size < 16*8 {
- if n > size+16 {
- return false
- }
- } else {
- if n > size*9/8 {
- return false
- }
- }
- return true
-}
-
-func AllocAndFree(size, count int) {
- if *chatty {
- fmt.Printf("size=%d count=%d ...\n", size, count)
- }
- runtime.ReadMemStats(stats)
- n1 := stats.Alloc
- for i := 0; i < count; i++ {
- b[i] = runtime.Alloc(uintptr(size))
- base, n := runtime.Lookup(b[i])
- if base != b[i] || !OkAmount(uintptr(size), n) {
- println("lookup failed: got", base, n, "for", b[i])
- panic("fail")
- }
- runtime.ReadMemStats(stats)
- if stats.Sys > 1e9 {
- println("too much memory allocated")
- panic("fail")
- }
- }
- runtime.ReadMemStats(stats)
- n2 := stats.Alloc
- if *chatty {
- fmt.Printf("size=%d count=%d stats=%+v\n", size, count, *stats)
- }
- n3 := stats.Alloc
- for j := 0; j < count; j++ {
- i := j
- if *reverse {
- i = count - 1 - j
- }
- alloc := uintptr(stats.Alloc)
- base, n := runtime.Lookup(b[i])
- if base != b[i] || !OkAmount(uintptr(size), n) {
- println("lookup failed: got", base, n, "for", b[i])
- panic("fail")
- }
- runtime.Free(b[i])
- runtime.ReadMemStats(stats)
- if stats.Alloc != uint64(alloc-n) {
- println("free alloc got", stats.Alloc, "expected", alloc-n, "after free of", n)
- panic("fail")
- }
- if stats.Sys > 1e9 {
- println("too much memory allocated")
- panic("fail")
- }
- }
- runtime.ReadMemStats(stats)
- n4 := stats.Alloc
-
- if *chatty {
- fmt.Printf("size=%d count=%d stats=%+v\n", size, count, *stats)
- }
- if n2-n1 != n3-n4 {
- println("wrong alloc count: ", n2-n1, n3-n4)
- panic("fail")
- }
-}
-
-func atoi(s string) int {
- i, _ := strconv.Atoi(s)
- return i
-}
-
-func main() {
- runtime.MemProfileRate = 0 // disable profiler
- flag.Parse()
- b = make([]*byte, 10000)
- if flag.NArg() > 0 {
- AllocAndFree(atoi(flag.Arg(0)), atoi(flag.Arg(1)))
- return
- }
- maxb := 1 << 22
- if !*longtest {
- maxb = 1 << 19
- }
- for j := 1; j <= maxb; j <<= 1 {
- n := len(b)
- max := uintptr(1 << 28)
- if !*longtest {
- max = uintptr(maxb)
- }
- if uintptr(j)*uintptr(n) > max {
- n = int(max / uintptr(j))
- }
- if n < 10 {
- n = 10
- }
- for m := 1; m <= n; {
- AllocAndFree(j, m)
- if m == n {
- break
- }
- m = 5 * m / 4
- if m < 4 {
- m++
- }
- if m > n {
- m = n
- }
- }
- }
-}
-43cf9b39b647
+96bd78e7d35e
The first line of this file holds the Mercurial revision number of the
last merge done from the master library sources.
mv -f $@.tmp $@
sema.c: $(srcdir)/runtime/sema.goc goc2c
- ./goc2c --gcc --go-prefix libgo_runtime $< > $@.tmp
+ ./goc2c --gcc --go-prefix libgo_sync $< > $@.tmp
mv -f $@.tmp $@
sigqueue.c: $(srcdir)/runtime/sigqueue.goc goc2c
go/sync/cond.go \
go/sync/mutex.go \
go/sync/once.go \
+ go/sync/runtime.go \
go/sync/rwmutex.go \
go/sync/waitgroup.go
go/time/tick.go \
go/time/time.go \
go/time/zoneinfo.go \
+ go/time/zoneinfo_read.go \
go/time/zoneinfo_unix.go
go_unicode_files = \
go/exp/norm/composition.go \
go/exp/norm/forminfo.go \
go/exp/norm/input.go \
+ go/exp/norm/iter.go \
go/exp/norm/normalize.go \
go/exp/norm/readwriter.go \
go/exp/norm/tables.go \
go/go/doc/example.go \
go/go/doc/exports.go \
go/go/doc/filter.go \
- go/go/doc/reader.go
+ go/go/doc/reader.go \
+ go/go/doc/synopsis.go
go_go_parser_files = \
go/go/parser/interface.go \
go/go/parser/parser.go
go_html_template_files = \
go/html/template/attr.go \
- go/html/template/clone.go \
go/html/template/content.go \
go/html/template/context.go \
go/html/template/css.go \
go/sync/cond.go \
go/sync/mutex.go \
go/sync/once.go \
+ go/sync/runtime.go \
go/sync/rwmutex.go \
go/sync/waitgroup.go
go/time/tick.go \
go/time/time.go \
go/time/zoneinfo.go \
+ go/time/zoneinfo_read.go \
go/time/zoneinfo_unix.go
go_unicode_files = \
go/exp/norm/composition.go \
go/exp/norm/forminfo.go \
go/exp/norm/input.go \
+ go/exp/norm/iter.go \
go/exp/norm/normalize.go \
go/exp/norm/readwriter.go \
go/exp/norm/tables.go \
go/go/doc/example.go \
go/go/doc/exports.go \
go/go/doc/filter.go \
- go/go/doc/reader.go
+ go/go/doc/reader.go \
+ go/go/doc/synopsis.go
go_go_parser_files = \
go/go/parser/interface.go \
go_html_template_files = \
go/html/template/attr.go \
- go/html/template/clone.go \
go/html/template/content.go \
go/html/template/context.go \
go/html/template/css.go \
mv -f $@.tmp $@
sema.c: $(srcdir)/runtime/sema.goc goc2c
- ./goc2c --gcc --go-prefix libgo_runtime $< > $@.tmp
+ ./goc2c --gcc --go-prefix libgo_sync $< > $@.tmp
mv -f $@.tmp $@
sigqueue.c: $(srcdir)/runtime/sigqueue.goc goc2c
if m > n {
m = n
}
- err := b.readErr()
- if m < n && err == nil {
- err = ErrBufferFull
+ var err error
+ if m < n {
+ err = b.readErr()
+ if err == nil {
+ err = ErrBufferFull
+ }
}
return b.buf[b.r : b.r+m], err
}
if _, err := buf.Peek(1); err != io.EOF {
t.Fatalf("want EOF got %v", err)
}
+
+ // Test for issue 3022, not exposing a reader's error on a successful Peek.
+ buf = NewReaderSize(dataAndEOFReader("abcd"), 32)
+ if s, err := buf.Peek(2); string(s) != "ab" || err != nil {
+ t.Errorf(`Peek(2) on "abcd", EOF = %q, %v; want "ab", nil`, string(s), err)
+ }
+ if s, err := buf.Peek(4); string(s) != "abcd" || err != nil {
+ t.Errorf(`Peek(4) on "abcd", EOF = %q, %v; want "abcd", nil`, string(s), err)
+ }
+ if n, err := buf.Read(p[0:5]); string(p[0:n]) != "abcd" || err != nil {
+ t.Fatalf("Read after peek = %q, %v; want abcd, EOF", p[0:n], err)
+ }
+ if n, err := buf.Read(p[0:1]); string(p[0:n]) != "" || err != io.EOF {
+ t.Fatalf(`second Read after peek = %q, %v; want "", EOF`, p[0:n], err)
+ }
+}
+
+type dataAndEOFReader string
+
+func (r dataAndEOFReader) Read(p []byte) (int, error) {
+ return copy(p, r), io.EOF
}
func TestPeekThenUnreadRune(t *testing.T) {
// Compare returns an integer comparing the two byte arrays lexicographically.
// The result will be 0 if a==b, -1 if a < b, and +1 if a > b
+// A nil argument is equivalent to an empty slice.
func Compare(a, b []byte) int {
m := len(a)
if m > len(b) {
}
// Equal returns a boolean reporting whether a == b.
+// A nil argument is equivalent to an empty slice.
func Equal(a, b []byte) bool
func equalPortable(a, b []byte) bool {
i int
}
-var comparetests = []BinOpTest{
- {"", "", 0},
- {"a", "", 1},
- {"", "a", -1},
- {"abc", "abc", 0},
- {"ab", "abc", -1},
- {"abc", "ab", 1},
- {"x", "ab", 1},
- {"ab", "x", -1},
- {"x", "a", 1},
- {"b", "x", -1},
+var compareTests = []struct {
+ a, b []byte
+ i int
+}{
+ {[]byte(""), []byte(""), 0},
+ {[]byte("a"), []byte(""), 1},
+ {[]byte(""), []byte("a"), -1},
+ {[]byte("abc"), []byte("abc"), 0},
+ {[]byte("ab"), []byte("abc"), -1},
+ {[]byte("abc"), []byte("ab"), 1},
+ {[]byte("x"), []byte("ab"), 1},
+ {[]byte("ab"), []byte("x"), -1},
+ {[]byte("x"), []byte("a"), 1},
+ {[]byte("b"), []byte("x"), -1},
+ // nil tests
+ {nil, nil, 0},
+ {[]byte(""), nil, 0},
+ {nil, []byte(""), 0},
+ {[]byte("a"), nil, 1},
+ {nil, []byte("a"), -1},
}
func TestCompare(t *testing.T) {
- for _, tt := range comparetests {
- a := []byte(tt.a)
- b := []byte(tt.b)
- cmp := Compare(a, b)
+ for _, tt := range compareTests {
+ cmp := Compare(tt.a, tt.b)
if cmp != tt.i {
t.Errorf(`Compare(%q, %q) = %v`, tt.a, tt.b, cmp)
}
- eql := Equal(a, b)
+ eql := Equal(tt.a, tt.b)
if eql != (tt.i == 0) {
t.Errorf(`Equal(%q, %q) = %v`, tt.a, tt.b, eql)
}
- eql = EqualPortable(a, b)
+ eql = EqualPortable(tt.a, tt.b)
if eql != (tt.i == 0) {
t.Errorf(`EqualPortable(%q, %q) = %v`, tt.a, tt.b, eql)
}
"os"
)
-// Hello world!
func ExampleBuffer() {
var b Buffer // A Buffer needs no initialization.
b.Write([]byte("Hello "))
b.Write([]byte("world!"))
b.WriteTo(os.Stdout)
+ // Output: Hello world!
}
-// Gophers rule!
func ExampleBuffer_reader() {
// A Buffer can turn a string or a []byte into an io.Reader.
buf := NewBufferString("R29waGVycyBydWxlIQ==")
dec := base64.NewDecoder(base64.StdEncoding, buf)
io.Copy(os.Stdout, dec)
+ // Output: Gophers rule!
}
t.Error(err)
}
testToFromWithLimit(t, gold, test.label, test.limit)
+ if testing.Short() {
+ break
+ }
}
}
// See http://code.google.com/p/go/issues/detail?id=2508
func TestRegression2508(t *testing.T) {
+ if testing.Short() {
+ t.Logf("test disabled with -short")
+ return
+ }
w, err := NewWriter(ioutil.Discard, 1)
if err != nil {
t.Fatalf("NewWriter: %v", err)
return item
}
-// 99:seven 88:five 77:zero 66:nine 55:three 44:two 33:six 22:one 11:four 00:eight
-func ExampleInterface() {
- // The full code of this example, including the methods that implement
- // heap.Interface, is in the file src/pkg/container/heap/example_test.go.
+// update is not used by the example but shows how to take the top item from
+// the queue, update its priority and value, and put it back.
+func (pq *PriorityQueue) update(value string, priority int) {
+ item := heap.Pop(pq).(*Item)
+ item.value = value
+ item.priority = priority
+ heap.Push(pq, item)
+}
+// changePriority is not used by the example but shows how to change the
+// priority of an arbitrary item.
+func (pq *PriorityQueue) changePriority(item *Item, priority int) {
+ heap.Remove(pq, item.index)
+ item.priority = priority
+ heap.Push(pq, item)
+}
+
+// This example pushes 10 items into a PriorityQueue and takes them out in
+// order of priority.
+func Example() {
const nItem = 10
// Random priorities for the items (a permutation of 0..9, times 11)).
priorities := [nItem]int{
item := heap.Pop(&pq).(*Item)
fmt.Printf("%.2d:%s ", item.priority, item.value)
}
-}
-
-// update is not used by the example but shows how to take the top item from the queue,
-// update its priority and value, and put it back.
-func (pq *PriorityQueue) update(value string, priority int) {
- item := heap.Pop(pq).(*Item)
- item.value = value
- item.priority = priority
- heap.Push(pq, item)
-}
-
-// changePriority is not used by the example but shows how to change the priority of an arbitrary
-// item.
-func (pq *PriorityQueue) changePriority(item *Item, priority int) {
- heap.Remove(pq, item.index)
- item.priority = priority
- heap.Push(pq, item)
+ // Output:
+ // 99:seven 88:five 77:zero 66:nine 55:three 44:two 33:six 22:one 11:four 00:eight
}
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Package dsa implements the Digital Signature Algorithm, as defined in FIPS 186-3
+// Package dsa implements the Digital Signature Algorithm, as defined in FIPS 186-3.
package dsa
import (
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package md5
+package md5_test
import (
+ "crypto/md5"
"fmt"
"io"
"testing"
func TestGolden(t *testing.T) {
for i := 0; i < len(golden); i++ {
g := golden[i]
- c := New()
+ c := md5.New()
for j := 0; j < 3; j++ {
if j < 2 {
io.WriteString(c, g.in)
}
}
}
+
+func ExampleNew() {
+ h := md5.New()
+ io.WriteString(h, "The fog is getting thicker!")
+ io.WriteString(h, "And Leon's getting laaarger!")
+ fmt.Printf("%x", h.Sum(nil))
+ // Output: e2c569be17396eca2a2e3c11578123ed
+}
// SHA1 hash algorithm. See RFC 3174.
-package sha1
+package sha1_test
import (
+ "crypto/sha1"
"fmt"
"io"
"testing"
func TestGolden(t *testing.T) {
for i := 0; i < len(golden); i++ {
g := golden[i]
- c := New()
+ c := sha1.New()
for j := 0; j < 3; j++ {
if j < 2 {
io.WriteString(c, g.in)
}
}
}
+
+func ExampleNew() {
+ h := sha1.New()
+ io.WriteString(h, "His money is twice tainted: 'taint yours and 'taint mine.")
+ fmt.Printf("% x", h.Sum(nil))
+ // Output: 59 7f 6a 54 00 10 f9 4c 15 d7 18 06 a9 9a 2c 87 10 e7 47 bd
+}
masterSecret, clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV :=
keysFromPreMasterSecret(c.vers, preMasterSecret, hello.random, serverHello.random, suite.macLen, suite.keyLen, suite.ivLen)
- clientCipher := suite.cipher(clientKey, clientIV, false /* not for reading */ )
+ clientCipher := suite.cipher(clientKey, clientIV, false /* not for reading */)
clientHash := suite.mac(c.vers, clientMAC)
c.out.prepareCipherSpec(c.vers, clientCipher, clientHash)
c.writeRecord(recordTypeChangeCipherSpec, []byte{1})
finishedHash.Write(finished.marshal())
c.writeRecord(recordTypeHandshake, finished.marshal())
- serverCipher := suite.cipher(serverKey, serverIV, true /* for reading */ )
+ serverCipher := suite.cipher(serverKey, serverIV, true /* for reading */)
serverHash := suite.mac(c.vers, serverMAC)
c.in.prepareCipherSpec(c.vers, serverCipher, serverHash)
c.readRecord(recordTypeChangeCipherSpec)
masterSecret, clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV :=
keysFromPreMasterSecret(c.vers, preMasterSecret, clientHello.random, hello.random, suite.macLen, suite.keyLen, suite.ivLen)
- clientCipher := suite.cipher(clientKey, clientIV, true /* for reading */ )
+ clientCipher := suite.cipher(clientKey, clientIV, true /* for reading */)
clientHash := suite.mac(c.vers, clientMAC)
c.in.prepareCipherSpec(c.vers, clientCipher, clientHash)
c.readRecord(recordTypeChangeCipherSpec)
finishedHash.Write(clientFinished.marshal())
- serverCipher := suite.cipher(serverKey, serverIV, false /* not for reading */ )
+ serverCipher := suite.cipher(serverKey, serverIV, false /* not for reading */)
serverHash := suite.mac(c.vers, serverMAC)
c.out.prepareCipherSpec(c.vers, serverCipher, serverHash)
c.writeRecord(recordTypeChangeCipherSpec, []byte{1})
// subsetTypeArgs takes a slice of arguments from callers of the sql
// package and converts them into a slice of the driver package's
// "subset types".
-func subsetTypeArgs(args []interface{}) ([]interface{}, error) {
- out := make([]interface{}, len(args))
+func subsetTypeArgs(args []interface{}) ([]driver.Value, error) {
+ out := make([]driver.Value, len(args))
for n, arg := range args {
var err error
out[n], err = driver.DefaultParameterConverter.ConvertValue(arg)
// drivers as used by package sql.
//
// Most code should use package sql.
-//
-// Drivers only need to be aware of a subset of Go's types. The sql package
-// will convert all types into one of the following:
+package driver
+
+import "errors"
+
+// A driver Value is a value that drivers must be able to handle.
+// A Value is either nil or an instance of one of these types:
//
// int64
// float64
// bool
-// nil
// []byte
// string [*] everywhere except from Rows.Next.
// time.Time
-//
-package driver
-
-import "errors"
+type Value interface{}
// Driver is the interface that must be implemented by a database
// driver.
// first prepare a query, execute the statement, and then close the
// statement.
//
-// All arguments are of a subset type as defined in the package docs.
-//
// Exec may return ErrSkip.
type Execer interface {
- Exec(query string, args []interface{}) (Result, error)
+ Exec(query string, args []Value) (Result, error)
}
// Conn is a connection to a database. It is not used concurrently
NumInput() int
// Exec executes a query that doesn't return rows, such
- // as an INSERT or UPDATE. The args are all of a subset
- // type as defined above.
- Exec(args []interface{}) (Result, error)
+ // as an INSERT or UPDATE.
+ Exec(args []Value) (Result, error)
// Exec executes a query that may return rows, such as a
- // SELECT. The args of all of a subset type as defined above.
- Query(args []interface{}) (Rows, error)
+ // SELECT.
+ Query(args []Value) (Rows, error)
}
// ColumnConverter may be optionally implemented by Stmt if the
// the statement is aware of its own columns' types and can
-// convert from any type to a driver subset type.
+// convert from any type to a driver Value.
type ColumnConverter interface {
// ColumnConverter returns a ValueConverter for the provided
// column index. If the type of a specific column isn't known
// the provided slice. The provided slice will be the same
// size as the Columns() are wide.
//
- // The dest slice may be populated with only with values
- // of subset types defined above, but excluding string.
+ // The dest slice may be populated only with
+ // a driver Value type, but excluding string.
// All string values must be converted to []byte.
//
// Next should return io.EOF when there are no more rows.
- Next(dest []interface{}) error
+ Next(dest []Value) error
}
// Tx is a transaction.
return int64(v), nil
}
-// DDLSuccess is a pre-defined Result for drivers to return when a DDL
-// command succeeds.
-var DDLSuccess ddlSuccess
+// ResultNoRows is a pre-defined Result for drivers to return when a DDL
+// command (such as a CREATE TABLE) succeeds. It returns an error for both
+// LastInsertId and RowsAffected.
+var ResultNoRows noRows
-type ddlSuccess struct{}
+type noRows struct{}
-var _ Result = ddlSuccess{}
+var _ Result = noRows{}
-func (ddlSuccess) LastInsertId() (int64, error) {
+func (noRows) LastInsertId() (int64, error) {
return 0, errors.New("no LastInsertId available after DDL statement")
}
-func (ddlSuccess) RowsAffected() (int64, error) {
+func (noRows) RowsAffected() (int64, error) {
return 0, errors.New("no RowsAffected available after DDL statement")
}
// driver package to provide consistent implementations of conversions
// between drivers. The ValueConverters have several uses:
//
-// * converting from the subset types as provided by the sql package
+// * converting from the Value types as provided by the sql package
// into a database table's specific column type and making sure it
// fits, such as making sure a particular int64 fits in a
// table's uint16 column.
//
// * converting a value as given from the database into one of the
-// subset types.
+// driver Value types.
//
-// * by the sql package, for converting from a driver's subset type
+// * by the sql package, for converting from a driver's Value type
// to a user's type in a scan.
type ValueConverter interface {
- // ConvertValue converts a value to a restricted subset type.
- ConvertValue(v interface{}) (interface{}, error)
+ // ConvertValue converts a value to a driver Value.
+ ConvertValue(v interface{}) (Value, error)
}
-// SubsetValuer is the interface providing the SubsetValue method.
+// Valuer is the interface providing the Value method.
//
-// Types implementing SubsetValuer interface are able to convert
-// themselves to one of the driver's allowed subset values.
-type SubsetValuer interface {
- // SubsetValue returns a driver parameter subset value.
- SubsetValue() (interface{}, error)
+// Types implementing Valuer interface are able to convert
+// themselves to a driver Value.
+type Valuer interface {
+ // Value returns a driver Value.
+ Value() (Value, error)
}
// Bool is a ValueConverter that converts input values to bools.
func (boolType) String() string { return "Bool" }
-func (boolType) ConvertValue(src interface{}) (interface{}, error) {
+func (boolType) ConvertValue(src interface{}) (Value, error) {
switch s := src.(type) {
case bool:
return s, nil
var _ ValueConverter = int32Type{}
-func (int32Type) ConvertValue(v interface{}) (interface{}, error) {
+func (int32Type) ConvertValue(v interface{}) (Value, error) {
rv := reflect.ValueOf(v)
switch rv.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
type stringType struct{}
-func (stringType) ConvertValue(v interface{}) (interface{}, error) {
+func (stringType) ConvertValue(v interface{}) (Value, error) {
switch v.(type) {
case string, []byte:
return v, nil
Converter ValueConverter
}
-func (n Null) ConvertValue(v interface{}) (interface{}, error) {
+func (n Null) ConvertValue(v interface{}) (Value, error) {
if v == nil {
return nil, nil
}
Converter ValueConverter
}
-func (n NotNull) ConvertValue(v interface{}) (interface{}, error) {
+func (n NotNull) ConvertValue(v interface{}) (Value, error) {
if v == nil {
return nil, fmt.Errorf("nil value not allowed")
}
return n.Converter.ConvertValue(v)
}
-// IsParameterSubsetType reports whether v is of a valid type for a
-// parameter. These types are:
-//
-// int64
-// float64
-// bool
-// nil
-// []byte
-// time.Time
-// string
-//
-// This is the same list as IsScanSubsetType, with the addition of
-// string.
-func IsParameterSubsetType(v interface{}) bool {
- if IsScanSubsetType(v) {
+// IsValue reports whether v is a valid Value parameter type.
+// Unlike IsScanValue, IsValue permits the string type.
+func IsValue(v interface{}) bool {
+ if IsScanValue(v) {
return true
}
if _, ok := v.(string); ok {
return false
}
-// IsScanSubsetType reports whether v is of a valid type for a
-// value populated by Rows.Next. These types are:
-//
-// int64
-// float64
-// bool
-// nil
-// []byte
-// time.Time
-//
-// This is the same list as IsParameterSubsetType, without string.
-func IsScanSubsetType(v interface{}) bool {
+// IsScanValue reports whether v is a valid Value scan type.
+// Unlike IsValue, IsScanValue does not permit the string type.
+func IsScanValue(v interface{}) bool {
if v == nil {
return true
}
// ColumnConverter.
//
// DefaultParameterConverter returns the given value directly if
-// IsSubsetType(value). Otherwise integer type are converted to
+// IsValue(value). Otherwise integer type are converted to
// int64, floats to float64, and strings to []byte. Other types are
// an error.
var DefaultParameterConverter defaultConverter
var _ ValueConverter = defaultConverter{}
-func (defaultConverter) ConvertValue(v interface{}) (interface{}, error) {
- if IsParameterSubsetType(v) {
+func (defaultConverter) ConvertValue(v interface{}) (Value, error) {
+ if IsValue(v) {
return v, nil
}
- if svi, ok := v.(SubsetValuer); ok {
- sv, err := svi.SubsetValue()
+ if svi, ok := v.(Valuer); ok {
+ sv, err := svi.Value()
if err != nil {
return nil, err
}
- if !IsParameterSubsetType(sv) {
- return nil, fmt.Errorf("non-subset type %T returned from SubsetValue", sv)
+ if !IsValue(sv) {
+ return nil, fmt.Errorf("non-Value type %T returned from Value", sv)
}
return sv, nil
}
return nil
}
-func checkSubsetTypes(args []interface{}) error {
+func checkSubsetTypes(args []driver.Value) error {
for n, arg := range args {
switch arg.(type) {
case int64, float64, bool, nil, []byte, string, time.Time:
return nil
}
-func (c *fakeConn) Exec(query string, args []interface{}) (driver.Result, error) {
+func (c *fakeConn) Exec(query string, args []driver.Value) (driver.Result, error) {
// This is an optional interface, but it's implemented here
// just to check that all the args of of the proper types.
// ErrSkip is returned so the caller acts as if we didn't
var errClosed = errors.New("fakedb: statement has been closed")
-func (s *fakeStmt) Exec(args []interface{}) (driver.Result, error) {
+func (s *fakeStmt) Exec(args []driver.Value) (driver.Result, error) {
if s.closed {
return nil, errClosed
}
switch s.cmd {
case "WIPE":
db.wipe()
- return driver.DDLSuccess, nil
+ return driver.ResultNoRows, nil
case "CREATE":
if err := db.createTable(s.table, s.colName, s.colType); err != nil {
return nil, err
}
- return driver.DDLSuccess, nil
+ return driver.ResultNoRows, nil
case "INSERT":
return s.execInsert(args)
}
return nil, fmt.Errorf("unimplemented statement Exec command type of %q", s.cmd)
}
-func (s *fakeStmt) execInsert(args []interface{}) (driver.Result, error) {
+func (s *fakeStmt) execInsert(args []driver.Value) (driver.Result, error) {
db := s.c.db
if len(args) != s.placeholders {
panic("error in pkg db; should only get here if size is correct")
return driver.RowsAffected(1), nil
}
-func (s *fakeStmt) Query(args []interface{}) (driver.Rows, error) {
+func (s *fakeStmt) Query(args []driver.Value) (driver.Rows, error) {
if s.closed {
return nil, errClosed
}
return rc.cols
}
-func (rc *rowsCursor) Next(dest []interface{}) error {
+func (rc *rowsCursor) Next(dest []driver.Value) error {
if rc.closed {
return errors.New("fakedb: cursor is closed")
}
return convertAssign(&ns.String, value)
}
-// SubsetValue implements the driver SubsetValuer interface.
-func (ns NullString) SubsetValue() (interface{}, error) {
+// Value implements the driver Valuer interface.
+func (ns NullString) Value() (driver.Value, error) {
if !ns.Valid {
return nil, nil
}
return convertAssign(&n.Int64, value)
}
-// SubsetValue implements the driver SubsetValuer interface.
-func (n NullInt64) SubsetValue() (interface{}, error) {
+// Value implements the driver Valuer interface.
+func (n NullInt64) Value() (driver.Value, error) {
if !n.Valid {
return nil, nil
}
return convertAssign(&n.Float64, value)
}
-// SubsetValue implements the driver SubsetValuer interface.
-func (n NullFloat64) SubsetValue() (interface{}, error) {
+// Value implements the driver Valuer interface.
+func (n NullFloat64) Value() (driver.Value, error) {
if !n.Valid {
return nil, nil
}
return convertAssign(&n.Bool, value)
}
-// SubsetValue implements the driver SubsetValuer interface.
-func (n NullBool) SubsetValue() (interface{}, error) {
+// Value implements the driver Valuer interface.
+func (n NullBool) Value() (driver.Value, error) {
if !n.Valid {
return nil, nil
}
}
defer tx.releaseConn()
+ sargs, err := subsetTypeArgs(args)
+ if err != nil {
+ return nil, err
+ }
+
if execer, ok := ci.(driver.Execer); ok {
- resi, err := execer.Exec(query, args)
+ resi, err := execer.Exec(query, sargs)
if err == nil {
return result{resi}, nil
}
}
defer sti.Close()
- sargs, err := subsetTypeArgs(args)
- if err != nil {
- return nil, err
- }
-
resi, err := sti.Exec(sargs)
if err != nil {
return nil, err
return nil, fmt.Errorf("sql: expected %d arguments, got %d", want, len(args))
}
+ sargs := make([]driver.Value, len(args))
+
// Convert args to subset types.
if cc, ok := si.(driver.ColumnConverter); ok {
for n, arg := range args {
// First, see if the value itself knows how to convert
// itself to a driver type. For example, a NullString
// struct changing into a string or nil.
- if svi, ok := arg.(driver.SubsetValuer); ok {
- sv, err := svi.SubsetValue()
+ if svi, ok := arg.(driver.Valuer); ok {
+ sv, err := svi.Value()
if err != nil {
- return nil, fmt.Errorf("sql: argument index %d from SubsetValue: %v", n, err)
+ return nil, fmt.Errorf("sql: argument index %d from Value: %v", n, err)
}
- if !driver.IsParameterSubsetType(sv) {
- return nil, fmt.Errorf("sql: argument index %d: non-subset type %T returned from SubsetValue", n, sv)
+ if !driver.IsValue(sv) {
+ return nil, fmt.Errorf("sql: argument index %d: non-subset type %T returned from Value", n, sv)
}
arg = sv
}
// truncated), or that a nil can't go into a NOT NULL
// column before going across the network to get the
// same error.
- args[n], err = cc.ColumnConverter(n).ConvertValue(arg)
+ sargs[n], err = cc.ColumnConverter(n).ConvertValue(arg)
if err != nil {
return nil, fmt.Errorf("sql: converting Exec argument #%d's type: %v", n, err)
}
- if !driver.IsParameterSubsetType(args[n]) {
+ if !driver.IsValue(sargs[n]) {
return nil, fmt.Errorf("sql: driver ColumnConverter error converted %T to unsupported type %T",
- arg, args[n])
+ arg, sargs[n])
}
}
} else {
for n, arg := range args {
- args[n], err = driver.DefaultParameterConverter.ConvertValue(arg)
+ sargs[n], err = driver.DefaultParameterConverter.ConvertValue(arg)
if err != nil {
return nil, fmt.Errorf("sql: converting Exec argument #%d's type: %v", n, err)
}
}
}
- resi, err := si.Exec(args)
+ resi, err := si.Exec(sargs)
if err != nil {
return nil, err
}
rowsi driver.Rows
closed bool
- lastcols []interface{}
+ lastcols []driver.Value
lasterr error
closeStmt *Stmt // if non-nil, statement to Close on close
}
return false
}
if rs.lastcols == nil {
- rs.lastcols = make([]interface{}, len(rs.rowsi.Columns()))
+ rs.lastcols = make([]driver.Value, len(rs.rowsi.Columns()))
}
rs.lasterr = rs.rowsi.Next(rs.lastcols)
if rs.lasterr == io.EOF {
}
// New returns a new Data object initialized from the given parameters.
-// Clients should typically use [TODO(rsc): method to be named later] instead of calling
-// New directly.
+// Rather than calling this function directly, clients should typically use
+// the DWARF method of the File type of the appropriate package debug/elf,
+// debug/macho, or debug/pe.
//
// The []byte arguments are the data from the corresponding debug section
// in the object file; for example, for an ELF object, abbrev is the contents of
volatile int vi;
char x : 1;
int y : 4;
+ int z[0];
long long array[40];
+ int zz[0];
} t_my_struct;
+typedef struct my_struct1 {
+ int zz [1];
+} t_my_struct1;
typedef union my_union {
volatile int vi;
char x : 1;
t_func_void_of_void *a10;
t_func_void_of_ptr_char_dots *a11;
t_my_struct *a12;
-t_my_union *a12a;
+t_my_struct1 *a12a;
+t_my_union *a12b;
t_my_enum *a13;
t_my_list *a14;
t_my_tree *a15;
t.StructName, _ = e.Val(AttrName).(string)
t.Incomplete = e.Val(AttrDeclaration) != nil
t.Field = make([]*StructField, 0, 8)
+ var lastFieldType Type
+ var lastFieldBitOffset int64
for kid := next(); kid != nil; kid = next() {
if kid.Tag == TagMember {
f := new(StructField)
goto Error
}
}
+
+ haveBitOffset := false
f.Name, _ = kid.Val(AttrName).(string)
f.ByteSize, _ = kid.Val(AttrByteSize).(int64)
- f.BitOffset, _ = kid.Val(AttrBitOffset).(int64)
+ f.BitOffset, haveBitOffset = kid.Val(AttrBitOffset).(int64)
f.BitSize, _ = kid.Val(AttrBitSize).(int64)
t.Field = append(t.Field, f)
+
+ bito := f.BitOffset
+ if !haveBitOffset {
+ bito = f.ByteOffset * 8
+ }
+ if bito == lastFieldBitOffset && t.Kind != "union" {
+ // Last field was zero width. Fix array length.
+ // (DWARF writes out 0-length arrays as if they were 1-length arrays.)
+ zeroArray(lastFieldType)
+ }
+ lastFieldType = f.Type
+ lastFieldBitOffset = bito
+ }
+ }
+ if t.Kind != "union" {
+ b, ok := e.Val(AttrByteSize).(int64)
+ if ok && b*8 == lastFieldBitOffset {
+ // Final field must be zero width. Fix array length.
+ zeroArray(lastFieldType)
}
}
delete(d.typeCache, off)
return nil, err
}
+
+func zeroArray(t Type) {
+ for {
+ at, ok := t.(*ArrayType)
+ if !ok {
+ break
+ }
+ at.Count = 0
+ t = at.Type
+ }
+}
"t_func_void_of_char": "func(char) void",
"t_func_void_of_void": "func() void",
"t_func_void_of_ptr_char_dots": "func(*char, ...) void",
- "t_my_struct": "struct my_struct {vi volatile int@0; x char@4 : 1@7; y int@4 : 4@27; array [40]long long int@8}",
+ "t_my_struct": "struct my_struct {vi volatile int@0; x char@4 : 1@7; y int@4 : 4@27; z [0]int@8; array [40]long long int@8; zz [0]int@328}",
+ "t_my_struct1": "struct my_struct1 {zz [1]int@0}",
"t_my_union": "union my_union {vi volatile int@0; x char@0 : 1@7; y int@0 : 4@28; array [40]long long int@0}",
"t_my_enum": "enum my_enum {e1=1; e2=2; e3=-5; e4=1000000000000000}",
"t_my_list": "struct list {val short int@0; next *t_my_list@8}",
"t_my_tree": "struct tree {left *struct tree@0; right *struct tree@8; val long long unsigned int@16}",
}
+// As Apple converts gcc to a clang-based front end
+// they keep breaking the DWARF output. This map lists the
+// conversion from real answer to Apple answer.
+var machoBug = map[string]string{
+ "func(*char, ...) void": "func(*char) void",
+ "enum my_enum {e1=1; e2=2; e3=-5; e4=1000000000000000}": "enum my_enum {e1=1; e2=2; e3=-5; e4=-1530494976}",
+}
+
func elfData(t *testing.T, name string) *Data {
f, err := elf.Open(name)
if err != nil {
return d
}
-func TestTypedefsELF(t *testing.T) { testTypedefs(t, elfData(t, "testdata/typedef.elf")) }
+func TestTypedefsELF(t *testing.T) { testTypedefs(t, elfData(t, "testdata/typedef.elf"), "elf") }
func TestTypedefsMachO(t *testing.T) {
- testTypedefs(t, machoData(t, "testdata/typedef.macho"))
+ testTypedefs(t, machoData(t, "testdata/typedef.macho"), "macho")
}
-func testTypedefs(t *testing.T, d *Data) {
+func testTypedefs(t *testing.T, d *Data, kind string) {
r := d.Reader()
seen := make(map[string]bool)
for {
t.Errorf("multiple definitions for %s", t1.Name)
}
seen[t1.Name] = true
- if typstr != want {
+ if typstr != want && (kind != "macho" || typstr != machoBug[want]) {
t.Errorf("%s:\n\thave %s\n\twant %s", t1.Name, typstr, want)
}
}
import (
"debug/elf"
+ "fmt"
"os"
+ "os/exec"
"runtime"
+ "strings"
"testing"
)
+var pclinetestBinary string
+
func dotest() bool {
// For now, only works on ELF platforms.
- // TODO: convert to work with new go tool
- return false && runtime.GOOS == "linux" && runtime.GOARCH == "amd64"
+ if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
+ return false
+ }
+ if pclinetestBinary != "" {
+ return true
+ }
+ // This command builds pclinetest from pclinetest.asm;
+ // the resulting binary looks like it was built from pclinetest.s,
+ // but we have renamed it to keep it away from the go tool.
+ pclinetestBinary = os.TempDir() + "/pclinetest"
+ command := fmt.Sprintf("go tool 6a -o %s.6 pclinetest.asm && go tool 6l -E main -o %s %s.6",
+ pclinetestBinary, pclinetestBinary, pclinetestBinary)
+ cmd := exec.Command("sh", "-c", command)
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ if err := cmd.Run(); err != nil {
+ panic(err)
+ }
+ return true
}
func getTable(t *testing.T) *Table {
return
}
- f, tab := crack("_test/pclinetest", t)
+ f, tab := crack(pclinetestBinary, t)
text := f.Section(".text")
textdat, err := text.Data()
if err != nil {
file, line, fn := tab.PCToLine(pc)
off := pc - text.Addr // TODO(rsc): should not need off; bug in 8g
wantLine += int(textdat[off])
+ t.Logf("off is %d", off)
if fn == nil {
t.Errorf("failed to get line of PC %#x", pc)
- } else if len(file) < 12 || file[len(file)-12:] != "pclinetest.s" || line != wantLine || fn != sym {
- t.Errorf("expected %s:%d (%s) at PC %#x, got %s:%d (%s)", "pclinetest.s", wantLine, sym.Name, pc, file, line, fn.Name)
+ } else if !strings.HasSuffix(file, "pclinetest.asm") {
+ t.Errorf("expected %s (%s) at PC %#x, got %s (%s)", "pclinetest.asm", sym.Name, pc, file, fn.Name)
+ } else if line != wantLine || fn != sym {
+ t.Errorf("expected :%d (%s) at PC %#x, got :%d (%s)", wantLine, sym.Name, pc, line, fn.Name)
}
}
// decodeSingle decodes a top-level value that is not a struct and stores it through p.
// Such values are preceded by a zero, making them have the memory layout of a
// struct field (although with an illegal field number).
-func (dec *Decoder) decodeSingle(engine *decEngine, ut *userTypeInfo, basep uintptr) (err error) {
+func (dec *Decoder) decodeSingle(engine *decEngine, ut *userTypeInfo, basep uintptr) {
state := dec.newDecoderState(&dec.buf)
state.fieldnum = singletonField
delta := int(state.decodeUint())
}
instr := &engine.instr[singletonField]
if instr.indir != ut.indir {
- return errors.New("gob: internal error: inconsistent indirection")
+ errorf("internal error: inconsistent indirection instr %d ut %d", instr.indir, ut.indir)
}
ptr := unsafe.Pointer(basep) // offset will be zero
if instr.indir > 1 {
}
instr.op(instr, state, ptr)
dec.freeDecoderState(state)
- return nil
}
-// decodeSingle decodes a top-level struct and stores it through p.
+// decodeStruct decodes a top-level struct and stores it through p.
// Indir is for the value, not the type. At the time of the call it may
// differ from ut.indir, which was computed when the engine was built.
// This state cannot arise for decodeSingle, which is called directly
}
case reflect.Map:
- name = "element of " + name
keyId := dec.wireType[wireId].MapT.Key
elemId := dec.wireType[wireId].MapT.Elem
- keyOp, keyIndir := dec.decOpFor(keyId, t.Key(), name, inProgress)
- elemOp, elemIndir := dec.decOpFor(elemId, t.Elem(), name, inProgress)
+ keyOp, keyIndir := dec.decOpFor(keyId, t.Key(), "key of "+name, inProgress)
+ elemOp, elemIndir := dec.decOpFor(elemId, t.Elem(), "element of "+name, inProgress)
ovfl := overflow(name)
op = func(i *decInstr, state *decoderState, p unsafe.Pointer) {
up := unsafe.Pointer(p)
// getDecEnginePtr returns the engine for the specified type.
func (dec *Decoder) getDecEnginePtr(remoteId typeId, ut *userTypeInfo) (enginePtr **decEngine, err error) {
- rt := ut.base
+ rt := ut.user
decoderMap, ok := dec.decoderCache[rt]
if !ok {
decoderMap = make(map[typeId]**decEngine)
t.Error("expected compatibility error")
}
}
+
+// Mutually recursive slices of structs caused problems.
+type Bug3 struct {
+ Num int
+ Children []*Bug3
+}
+
+func TestGobPtrSlices(t *testing.T) {
+ in := []*Bug3{
+ &Bug3{1, nil},
+ &Bug3{2, nil},
+ }
+ b := new(bytes.Buffer)
+ err := NewEncoder(b).Encode(&in)
+ if err != nil {
+ t.Fatal("encode:", err)
+ }
+
+ var out []*Bug3
+ err = NewDecoder(b).Decode(&out)
+ if err != nil {
+ t.Fatal("decode:", err)
+ }
+ if !reflect.DeepEqual(in, out) {
+ t.Fatal("got %v; wanted %v", out, in)
+ }
+}
+
+// getDecEnginePtr cached engine for ut.base instead of ut.user so we passed
+// a *map and then tried to reuse its engine to decode the inner map.
+func TestPtrToMapOfMap(t *testing.T) {
+ Register(make(map[string]interface{}))
+ subdata := make(map[string]interface{})
+ subdata["bar"] = "baz"
+ data := make(map[string]interface{})
+ data["foo"] = subdata
+
+ b := new(bytes.Buffer)
+ err := NewEncoder(b).Encode(data)
+ if err != nil {
+ t.Fatal("encode:", err)
+ }
+ var newData map[string]interface{}
+ err = NewDecoder(b).Decode(&newData)
+ if err != nil {
+ t.Fatal("decode:", err)
+ }
+ if !reflect.DeepEqual(data, newData) {
+ t.Fatalf("expected %v got %v", data, newData)
+ }
+}
var builtinIdToType map[typeId]gobType // set in init() after builtins are established
func setTypeId(typ gobType) {
+ // When building recursive types, someone may get there before us.
+ if typ.id() != 0 {
+ return
+ }
nextId++
typ.setId(nextId)
idToType[nextId] = typ
func (s *sliceType) init(elem gobType) {
// Set our type id before evaluating the element's, in case it's our own.
setTypeId(s)
+ // See the comments about ids in newTypeObject. Only slices and
+ // structs have mutual recursion.
+ if elem.id() == 0 {
+ setTypeId(elem)
+ }
s.Elem = elem.id()
}
if err != nil {
return nil, err
}
+ // Some mutually recursive types can cause us to be here while
+ // still defining the element. Fix the element type id here.
+ // We could do this more neatly by setting the id at the start of
+ // building every type, but that would break binary compatibility.
+ if gt.id() == 0 {
+ setTypeId(gt)
+ }
st.Field = append(st.Field, &fieldType{f.Name, gt.id()})
}
return st, nil
// Pretend this field doesn't exist.
continue
}
+ if sf.Anonymous {
+ // Pretend this field doesn't exist,
+ // so that we can do a good job with
+ // these in a later version.
+ continue
+ }
// First, tag match
tagName, _ := parseTag(tag)
if tagName == key {
}
return b[0:w], true
}
+
+// The following is issue 3069.
+
+// BUG(rsc): This package ignores anonymous (embedded) struct fields
+// during encoding and decoding. A future version may assign meaning
+// to them. To force an anonymous field to be ignored in all future
+// versions of this package, use an explicit `json:"-"` tag in the struct
+// definition.
t.Errorf("got %+v, want %+v", got, want)
}
}
+
+// Test that anonymous fields are ignored.
+// We may assign meaning to them later.
+func TestAnonymous(t *testing.T) {
+ type S struct {
+ T
+ N int
+ }
+
+ data, err := Marshal(new(S))
+ if err != nil {
+ t.Fatalf("Marshal: %v", err)
+ }
+ want := `{"N":0}`
+ if string(data) != want {
+ t.Fatalf("Marshal = %#q, want %#q", string(data), want)
+ }
+
+ var s S
+ if err := Unmarshal([]byte(`{"T": 1, "T": {"Y": 1}, "N": 2}`), &s); err != nil {
+ t.Fatalf("Unmarshal: %v", err)
+ }
+ if s.N != 2 {
+ t.Fatal("Unmarshal: did not set N")
+ }
+ if s.T.Y != 0 {
+ t.Fatal("Unmarshal: did set T.Y")
+ }
+}
if f.PkgPath != "" {
continue
}
+ if f.Anonymous {
+ // We want to do a better job with these later,
+ // so for now pretend they don't exist.
+ continue
+ }
var ef encodeField
ef.i = i
ef.tag = f.Name
// if the field value is empty. The empty values are false, 0, any
// nil pointer or interface value, and any array, slice, map, or
// string of length zero.
+// - a non-pointer anonymous struct field is handled as if the
+// fields of its value were part of the outer struct.
//
// If a field uses a tag "a>b>c", then the element c will be nested inside
// parent elements a and b. Fields that appear next to each other that name
-// the same parent will be enclosed in one XML element. For example:
+// the same parent will be enclosed in one XML element.
//
-// type Result struct {
-// XMLName xml.Name `xml:"result"`
-// Id int `xml:"id,attr"`
-// FirstName string `xml:"person>name>first"`
-// LastName string `xml:"person>name>last"`
-// Age int `xml:"person>age"`
-// Height float `xml:"person>height,omitempty"`
-// Married bool `xml:"person>married"`
-// }
-//
-// xml.Marshal(&Result{Id: 13, FirstName: "John", LastName: "Doe", Age: 42})
-//
-// would be marshalled as:
-//
-// <result>
-// <person id="13">
-// <name>
-// <first>John</first>
-// <last>Doe</last>
-// </name>
-// <age>42</age>
-// <married>false</married>
-// </person>
-// </result>
+// See MarshalIndent for an example.
//
// Marshal will return an error if asked to marshal a channel, function, or map.
func Marshal(v interface{}) ([]byte, error) {
return b.Bytes(), nil
}
+// MarshalIndent works like Marshal, but each XML element begins on a new
+// indented line that starts with prefix and is followed by one or more
+// copies of indent according to the nesting depth.
+func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) {
+ var b bytes.Buffer
+ enc := NewEncoder(&b)
+ enc.prefix = prefix
+ enc.indent = indent
+ err := enc.marshalValue(reflect.ValueOf(v), nil)
+ enc.Flush()
+ if err != nil {
+ return nil, err
+ }
+ return b.Bytes(), nil
+}
+
// An Encoder writes XML data to an output stream.
type Encoder struct {
printer
// NewEncoder returns a new encoder that writes to w.
func NewEncoder(w io.Writer) *Encoder {
- return &Encoder{printer{bufio.NewWriter(w)}}
+ return &Encoder{printer{Writer: bufio.NewWriter(w)}}
}
// Encode writes the XML encoding of v to the stream.
type printer struct {
*bufio.Writer
+ indent string
+ prefix string
+ depth int
+ indentedIn bool
}
+// marshalValue writes one or more XML elements representing val.
+// If val was obtained from a struct field, finfo must have its details.
func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo) error {
if !val.IsValid() {
return nil
}
}
+ p.writeIndent(1)
p.WriteByte('<')
p.WriteString(name)
return err
}
+ p.writeIndent(-1)
p.WriteByte('<')
p.WriteByte('/')
p.WriteString(name)
if vf.Len() == 0 {
continue
}
+ p.writeIndent(0)
p.WriteString("<!--")
dashDash := false
dashLast := false
return nil
}
+func (p *printer) writeIndent(depthDelta int) {
+ if len(p.prefix) == 0 && len(p.indent) == 0 {
+ return
+ }
+ if depthDelta < 0 {
+ p.depth--
+ if p.indentedIn {
+ p.indentedIn = false
+ return
+ }
+ p.indentedIn = false
+ }
+ p.WriteByte('\n')
+ if len(p.prefix) > 0 {
+ p.WriteString(p.prefix)
+ }
+ if len(p.indent) > 0 {
+ for i := 0; i < p.depth; i++ {
+ p.WriteString(p.indent)
+ }
+ }
+ if depthDelta > 0 {
+ p.depth++
+ p.indentedIn = true
+ }
+}
+
type parentStack struct {
*printer
stack []string
break
}
}
-
for i := len(s.stack) - 1; i >= split; i-- {
+ s.writeIndent(-1)
s.WriteString("</")
s.WriteString(s.stack[i])
s.WriteByte('>')
}
-
s.stack = parents[:split]
}
// push adds parent elements to the stack and writes open tags.
func (s *parentStack) push(parents []string) {
for i := 0; i < len(parents); i++ {
- s.WriteString("<")
+ s.writeIndent(1)
+ s.WriteByte('<')
s.WriteString(parents[i])
s.WriteByte('>')
}
// slice, or string. Well-formed data that does not fit into v is
// discarded.
//
-// For example, given these definitions:
-//
-// type Email struct {
-// Where string `xml:",attr"`
-// Addr string
-// }
-//
-// type Result struct {
-// XMLName xml.Name `xml:"result"`
-// Name string
-// Phone string
-// Email []Email
-// Groups []string `xml:"group>value"`
-// }
-//
-// result := Result{Name: "name", Phone: "phone", Email: nil}
-//
-// unmarshalling the XML input
-//
-// <result>
-// <email where="home">
-// <addr>gre@example.com</addr>
-// </email>
-// <email where='work'>
-// <addr>gre@work.com</addr>
-// </email>
-// <name>Grace R. Emlin</name>
-// <group>
-// <value>Friends</value>
-// <value>Squash</value>
-// </group>
-// <address>123 Main Street</address>
-// </result>
-//
-// via Unmarshal(data, &result) is equivalent to assigning
-//
-// r = Result{
-// xml.Name{Local: "result"},
-// "Grace R. Emlin", // name
-// "phone", // no phone given
-// []Email{
-// Email{"home", "gre@example.com"},
-// Email{"work", "gre@work.com"},
-// },
-// []string{"Friends", "Squash"},
-// }
-//
-// Note that the field r.Phone has not been modified and
-// that the XML <address> element was discarded. Also, the field
-// Groups was assigned considering the element path provided in the
-// field tag.
-//
// Because Unmarshal uses the reflect package, it can only assign
// to exported (upper case) fields. Unmarshal uses a case-sensitive
// comparison to match XML element names to tag values and struct
// of the above rules and the struct has a field with tag ",any",
// unmarshal maps the sub-element to that struct field.
//
+// * A non-pointer anonymous struct field is handled as if the
+// fields of its value were part of the outer struct.
+//
// * A struct field with tag "-" is never unmarshalled into.
//
// Unmarshal maps an XML element to a string or []byte by saving the
package errors_test
import (
- . "errors"
+ "errors"
+ "fmt"
"testing"
)
func TestNewEqual(t *testing.T) {
// Different allocations should not be equal.
- if New("abc") == New("abc") {
+ if errors.New("abc") == errors.New("abc") {
t.Errorf(`New("abc") == New("abc")`)
}
- if New("abc") == New("xyz") {
+ if errors.New("abc") == errors.New("xyz") {
t.Errorf(`New("abc") == New("xyz")`)
}
// Same allocation should be equal to itself (not crash).
- err := New("jkl")
+ err := errors.New("jkl")
if err != err {
t.Errorf(`err != err`)
}
}
func TestErrorMethod(t *testing.T) {
- err := New("abc")
+ err := errors.New("abc")
if err.Error() != "abc" {
t.Errorf(`New("abc").Error() = %q, want %q`, err.Error(), "abc")
}
}
+
+func ExampleNew() {
+ err := errors.New("emit macho dwarf: elf header corrupted")
+ if err != nil {
+ fmt.Print(err)
+ }
+ // Output: emit macho dwarf: elf header corrupted
+}
+
+// The fmt package's Errorf function lets us use the package's formatting
+// features to create descriptive error messages.
+func ExampleNew_errorf() {
+ const name, id = "bimmler", 17
+ err := fmt.Errorf("user %q (id %d) not found", name, id)
+ if err != nil {
+ fmt.Print(err)
+ }
+ // Output: user "bimmler" (id 17) not found
+}
package inotify
import (
+ "io/ioutil"
"os"
"testing"
"time"
// Create an inotify watcher instance and initialize it
watcher, err := NewWatcher()
if err != nil {
- t.Fatalf("NewWatcher() failed: %s", err)
+ t.Fatalf("NewWatcher failed: %s", err)
}
- t.Logf("NEEDS TO BE CONVERTED TO NEW GO TOOL") // TODO
- return
+ dir, err := ioutil.TempDir("", "inotify")
+ if err != nil {
+ t.Fatalf("TempDir failed: %s", err)
+ }
+ defer os.RemoveAll(dir)
// Add a watch for "_test"
- err = watcher.Watch("_test")
+ err = watcher.Watch(dir)
if err != nil {
- t.Fatalf("Watcher.Watch() failed: %s", err)
+ t.Fatalf("Watch failed: %s", err)
}
// Receive errors on the error channel on a separate goroutine
}
}()
- const testFile string = "_test/TestInotifyEvents.testfile"
+ testFile := dir + "/TestInotifyEvents.testfile"
// Receive events on the event channel on a separate goroutine
eventstream := watcher.Event
// This should add at least one event to the inotify event queue
_, err = os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
- t.Fatalf("creating test file failed: %s", err)
+ t.Fatalf("creating test file: %s", err)
}
// We expect this event to be received almost immediately, but let's wait 1 s to be sure
t.Fatal("double Close() test failed: second Close() call didn't return")
}
- err := watcher.Watch("_test")
+ err := watcher.Watch(os.TempDir())
if err == nil {
t.Fatal("expected error on Watch() after Close(), got nil")
}
return out
}
+// flushCopy copies the normalized segment to buf and resets rb.
+// It returns the number of bytes written to buf.
+func (rb *reorderBuffer) flushCopy(buf []byte) int {
+ p := 0
+ for i := 0; i < rb.nrune; i++ {
+ runep := rb.rune[i]
+ p += copy(buf[p:], rb.byte[runep.pos:runep.pos+runep.size])
+ }
+ rb.reset()
+ return p
+}
+
// insertOrdered inserts a rune in the buffer, ordered by Canonical Combining Class.
// It returns false if the buffer is not large enough to hold the rune.
// It is used internally by insert and insertString only.
// insert inserts the given rune in the buffer ordered by CCC.
// It returns true if the buffer was large enough to hold the decomposed rune.
func (rb *reorderBuffer) insert(src input, i int, info runeInfo) bool {
- if info.size == 3 {
- if rune := src.hangul(i); rune != 0 {
- return rb.decomposeHangul(rune)
- }
+ if rune := src.hangul(i); rune != 0 {
+ return rb.decomposeHangul(rune)
}
if info.hasDecomposition() {
- dcomp := info.decomposition()
- rb.tmpBytes = inputBytes(dcomp)
- for i := 0; i < len(dcomp); {
- info = rb.f.info(&rb.tmpBytes, i)
- pos := rb.nbyte
- if !rb.insertOrdered(info) {
- return false
- }
- end := i + int(info.size)
- copy(rb.byte[pos:], dcomp[i:end])
- i = end
- }
- } else {
- // insertOrder changes nbyte
+ return rb.insertDecomposed(info.decomposition())
+ }
+ return rb.insertSingle(src, i, info)
+}
+
+// insertDecomposed inserts an entry in to the reorderBuffer for each rune
+// in dcomp. dcomp must be a sequence of decomposed UTF-8-encoded runes.
+func (rb *reorderBuffer) insertDecomposed(dcomp []byte) bool {
+ saveNrune, saveNbyte := rb.nrune, rb.nbyte
+ rb.tmpBytes = inputBytes(dcomp)
+ for i := 0; i < len(dcomp); {
+ info := rb.f.info(&rb.tmpBytes, i)
pos := rb.nbyte
if !rb.insertOrdered(info) {
+ rb.nrune, rb.nbyte = saveNrune, saveNbyte
return false
}
- src.copySlice(rb.byte[pos:], i, i+int(info.size))
+ i += copy(rb.byte[pos:], dcomp[i:i+int(info.size)])
+ }
+ return true
+}
+
+// insertSingle inserts an entry in the reorderBuffer for the rune at
+// position i. info is the runeInfo for the rune at position i.
+func (rb *reorderBuffer) insertSingle(src input, i int, info runeInfo) bool {
+ // insertOrder changes nbyte
+ pos := rb.nbyte
+ if !rb.insertOrdered(info) {
+ return false
}
+ src.copySlice(rb.byte[pos:], i, i+int(info.size))
return true
}
jamoLVTCount = 19 * 21 * 28
)
-// Caller must verify that len(b) >= 3.
+const hangulUTF8Size = 3
+
func isHangul(b []byte) bool {
+ if len(b) < hangulUTF8Size {
+ return false
+ }
b0 := b[0]
if b0 < hangulBase0 {
return false
return b1 == hangulEnd1 && b[2] < hangulEnd2
}
-// Caller must verify that len(b) >= 3.
func isHangulString(b string) bool {
+ if len(b) < hangulUTF8Size {
+ return false
+ }
b0 := b[0]
if b0 < hangulBase0 {
return false
return c < jamoLVTCount && c%jamoTCount == 0
}
+// decomposeHangul writes the decomposed Hangul to buf and returns the number
+// of bytes written. len(buf) should be at least 9.
+func decomposeHangul(buf []byte, r rune) int {
+ const JamoUTF8Len = 3
+ r -= hangulBase
+ x := r % jamoTCount
+ r /= jamoTCount
+ utf8.EncodeRune(buf, jamoLBase+r/jamoVCount)
+ utf8.EncodeRune(buf[JamoUTF8Len:], jamoVBase+r%jamoVCount)
+ if x != 0 {
+ utf8.EncodeRune(buf[2*JamoUTF8Len:], jamoTBase+x)
+ return 3 * JamoUTF8Len
+ }
+ return 2 * JamoUTF8Len
+}
+
// decomposeHangul algorithmically decomposes a Hangul rune into
// its Jamo components.
// See http://unicode.org/reports/tr15/#Hangul for details on decomposing Hangul.
}
}
-func TestFlush(t *testing.T) {
+type flushFunc func(rb *reorderBuffer) []byte
+
+func testFlush(t *testing.T, name string, fn flushFunc) {
rb := reorderBuffer{}
rb.init(NFC, nil)
- out := make([]byte, 0)
-
- out = rb.flush(out)
+ out := fn(&rb)
if len(out) != 0 {
- t.Errorf("wrote bytes on flush of empty buffer. (len(out) = %d)", len(out))
+ t.Errorf("%s: wrote bytes on flush of empty buffer. (len(out) = %d)", name, len(out))
}
for _, r := range []rune("world!") {
out = rb.flush(out)
want := "Hello world!"
if string(out) != want {
- t.Errorf(`output after flush was "%s"; want "%s"`, string(out), want)
+ t.Errorf(`%s: output after flush was "%s"; want "%s"`, name, string(out), want)
}
if rb.nrune != 0 {
- t.Errorf("flush: non-null size of info buffer (rb.nrune == %d)", rb.nrune)
+ t.Errorf("%s: non-null size of info buffer (rb.nrune == %d)", name, rb.nrune)
}
if rb.nbyte != 0 {
- t.Errorf("flush: non-null size of byte buffer (rb.nbyte == %d)", rb.nbyte)
+ t.Errorf("%s: non-null size of byte buffer (rb.nbyte == %d)", name, rb.nbyte)
}
}
+func flushF(rb *reorderBuffer) []byte {
+ out := make([]byte, 0)
+ return rb.flush(out)
+}
+
+func flushCopyF(rb *reorderBuffer) []byte {
+ out := make([]byte, MaxSegmentSize)
+ n := rb.flushCopy(out)
+ return out[:n]
+}
+
+func TestFlush(t *testing.T) {
+ testFlush(t, "flush", flushF)
+ testFlush(t, "flushCopy", flushCopyF)
+}
+
var insertTests = []TestCase{
{[]rune{'a'}, []rune{'a'}},
{[]rune{0x300}, []rune{0x300}},
import "unicode/utf8"
type input interface {
- skipASCII(p int) int
+ skipASCII(p, max int) int
skipNonStarter(p int) int
appendSlice(buf []byte, s, e int) []byte
copySlice(buf []byte, s, e int)
type inputString string
-func (s inputString) skipASCII(p int) int {
- for ; p < len(s) && s[p] < utf8.RuneSelf; p++ {
+func (s inputString) skipASCII(p, max int) int {
+ for ; p < max && s[p] < utf8.RuneSelf; p++ {
}
return p
}
type inputBytes []byte
-func (s inputBytes) skipASCII(p int) int {
- for ; p < len(s) && s[p] < utf8.RuneSelf; p++ {
+func (s inputBytes) skipASCII(p, max int) int {
+ for ; p < max && s[p] < utf8.RuneSelf; p++ {
}
return p
}
--- /dev/null
+// 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 norm
+
+const MaxSegmentSize = maxByteBufferSize
+
+// An Iter iterates over a string or byte slice, while normalizing it
+// to a given Form.
+type Iter struct {
+ rb reorderBuffer
+ info runeInfo // first character saved from previous iteration
+ next iterFunc // implementation of next depends on form
+
+ p int // current position in input source
+ outStart int // start of current segment in output buffer
+ inStart int // start of current segment in input source
+ maxp int // position in output buffer after which not to start a new segment
+ maxseg int // for tracking an excess of combining characters
+
+ tccc uint8
+ done bool
+}
+
+type iterFunc func(*Iter, []byte) int
+
+// SetInput initializes i to iterate over src after normalizing it to Form f.
+func (i *Iter) SetInput(f Form, src []byte) {
+ i.rb.init(f, src)
+ if i.rb.f.composing {
+ i.next = nextComposed
+ } else {
+ i.next = nextDecomposed
+ }
+ i.p = 0
+ if i.done = len(src) == 0; !i.done {
+ i.info = i.rb.f.info(i.rb.src, i.p)
+ }
+}
+
+// SetInputString initializes i to iterate over src after normalizing it to Form f.
+func (i *Iter) SetInputString(f Form, src string) {
+ i.rb.initString(f, src)
+ if i.rb.f.composing {
+ i.next = nextComposed
+ } else {
+ i.next = nextDecomposed
+ }
+ i.p = 0
+ if i.done = len(src) == 0; !i.done {
+ i.info = i.rb.f.info(i.rb.src, i.p)
+ }
+}
+
+// Pos returns the byte position at which the next call to Next will commence processing.
+func (i *Iter) Pos() int {
+ return i.p
+}
+
+// Done returns true if there is no more input to process.
+func (i *Iter) Done() bool {
+ return i.done
+}
+
+// Next writes f(i.input[i.Pos():n]...) to buffer buf, where n is the
+// largest boundary of i.input such that the result fits in buf.
+// It returns the number of bytes written to buf.
+// len(buf) should be at least MaxSegmentSize.
+// Done must be false before calling Next.
+func (i *Iter) Next(buf []byte) int {
+ return i.next(i, buf)
+}
+
+func (i *Iter) initNext(outn, inStart int) {
+ i.outStart = 0
+ i.inStart = inStart
+ i.maxp = outn - MaxSegmentSize
+ i.maxseg = MaxSegmentSize
+}
+
+// setStart resets the start of the new segment to the given position.
+// It returns true if there is not enough room for the new segment.
+func (i *Iter) setStart(outp, inp int) bool {
+ if outp > i.maxp {
+ return true
+ }
+ i.outStart = outp
+ i.inStart = inp
+ i.maxseg = outp + MaxSegmentSize
+ return false
+}
+
+func min(a, b int) int {
+ if a < b {
+ return a
+ }
+ return b
+}
+
+// nextDecomposed is the implementation of Next for forms NFD and NFKD.
+func nextDecomposed(i *Iter, out []byte) int {
+ var outp int
+ i.initNext(len(out), i.p)
+doFast:
+ inCopyStart, outCopyStart := i.p, outp // invariant xCopyStart <= i.xStart
+ for {
+ if sz := int(i.info.size); sz <= 1 {
+ // ASCII or illegal byte. Either way, advance by 1.
+ i.p++
+ outp++
+ max := min(i.rb.nsrc, len(out)-outp+i.p)
+ if np := i.rb.src.skipASCII(i.p, max); np > i.p {
+ outp += np - i.p
+ i.p = np
+ if i.p >= i.rb.nsrc {
+ break
+ }
+ // ASCII may combine with consecutive runes.
+ if i.setStart(outp-1, i.p-1) {
+ i.p--
+ outp--
+ i.info.size = 1
+ break
+ }
+ }
+ } else if d := i.info.decomposition(); d != nil {
+ i.rb.src.copySlice(out[outCopyStart:], inCopyStart, i.p)
+ p := outp + len(d)
+ if p > i.maxseg && i.setStart(outp, i.p) {
+ return outp
+ }
+ copy(out[outp:], d)
+ outp = p
+ i.p += sz
+ inCopyStart, outCopyStart = i.p, outp
+ } else if r := i.rb.src.hangul(i.p); r != 0 {
+ i.rb.src.copySlice(out[outCopyStart:], inCopyStart, i.p)
+ for {
+ outp += decomposeHangul(out[outp:], r)
+ i.p += hangulUTF8Size
+ if r = i.rb.src.hangul(i.p); r == 0 {
+ break
+ }
+ if i.setStart(outp, i.p) {
+ return outp
+ }
+ }
+ inCopyStart, outCopyStart = i.p, outp
+ } else {
+ p := outp + sz
+ if p > i.maxseg && i.setStart(outp, i.p) {
+ break
+ }
+ outp = p
+ i.p += sz
+ }
+ if i.p >= i.rb.nsrc {
+ break
+ }
+ prevCC := i.info.tccc
+ i.info = i.rb.f.info(i.rb.src, i.p)
+ if cc := i.info.ccc; cc == 0 {
+ if i.setStart(outp, i.p) {
+ break
+ }
+ } else if cc < prevCC {
+ goto doNorm
+ }
+ }
+ if inCopyStart != i.p {
+ i.rb.src.copySlice(out[outCopyStart:], inCopyStart, i.p)
+ }
+ i.done = i.p >= i.rb.nsrc
+ return outp
+doNorm:
+ // Insert what we have decomposed so far in the reorderBuffer.
+ // As we will only reorder, there will always be enough room.
+ i.rb.src.copySlice(out[outCopyStart:], inCopyStart, i.p)
+ if !i.rb.insertDecomposed(out[i.outStart:outp]) {
+ // Start over to prevent decompositions from crossing segment boundaries.
+ // This is a rare occurance.
+ i.p = i.inStart
+ i.info = i.rb.f.info(i.rb.src, i.p)
+ }
+ outp = i.outStart
+ for {
+ if !i.rb.insert(i.rb.src, i.p, i.info) {
+ break
+ }
+ if i.p += int(i.info.size); i.p >= i.rb.nsrc {
+ outp += i.rb.flushCopy(out[outp:])
+ i.done = true
+ return outp
+ }
+ i.info = i.rb.f.info(i.rb.src, i.p)
+ if i.info.ccc == 0 {
+ break
+ }
+ }
+ // new segment or too many combining characters: exit normalization
+ if outp += i.rb.flushCopy(out[outp:]); i.setStart(outp, i.p) {
+ return outp
+ }
+ goto doFast
+}
+
+// nextComposed is the implementation of Next for forms NFC and NFKC.
+func nextComposed(i *Iter, out []byte) int {
+ var outp int
+ i.initNext(len(out), i.p)
+doFast:
+ inCopyStart, outCopyStart := i.p, outp // invariant xCopyStart <= i.xStart
+ var prevCC uint8
+ for {
+ if !i.info.isYesC() {
+ goto doNorm
+ }
+ if cc := i.info.ccc; cc == 0 {
+ if i.setStart(outp, i.p) {
+ break
+ }
+ } else if cc < prevCC {
+ goto doNorm
+ }
+ prevCC = i.info.tccc
+ sz := int(i.info.size)
+ if sz == 0 {
+ sz = 1 // illegal rune: copy byte-by-byte
+ }
+ p := outp + sz
+ if p > i.maxseg && i.setStart(outp, i.p) {
+ break
+ }
+ outp = p
+ i.p += sz
+ max := min(i.rb.nsrc, len(out)-outp+i.p)
+ if np := i.rb.src.skipASCII(i.p, max); np > i.p {
+ outp += np - i.p
+ i.p = np
+ if i.p >= i.rb.nsrc {
+ break
+ }
+ // ASCII may combine with consecutive runes.
+ if i.setStart(outp-1, i.p-1) {
+ i.p--
+ outp--
+ i.info = runeInfo{size: 1}
+ break
+ }
+ }
+ if i.p >= i.rb.nsrc {
+ break
+ }
+ i.info = i.rb.f.info(i.rb.src, i.p)
+ }
+ if inCopyStart != i.p {
+ i.rb.src.copySlice(out[outCopyStart:], inCopyStart, i.p)
+ }
+ i.done = i.p >= i.rb.nsrc
+ return outp
+doNorm:
+ i.rb.src.copySlice(out[outCopyStart:], inCopyStart, i.inStart)
+ outp, i.p = i.outStart, i.inStart
+ i.info = i.rb.f.info(i.rb.src, i.p)
+ for {
+ if !i.rb.insert(i.rb.src, i.p, i.info) {
+ break
+ }
+ if i.p += int(i.info.size); i.p >= i.rb.nsrc {
+ i.rb.compose()
+ outp += i.rb.flushCopy(out[outp:])
+ i.done = true
+ return outp
+ }
+ i.info = i.rb.f.info(i.rb.src, i.p)
+ if i.info.boundaryBefore() {
+ break
+ }
+ }
+ i.rb.compose()
+ if outp += i.rb.flushCopy(out[outp:]); i.setStart(outp, i.p) {
+ return outp
+ }
+ goto doFast
+}
--- /dev/null
+// 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 norm
+
+import (
+ "strings"
+ "testing"
+)
+
+var iterBufSizes = []int{
+ MaxSegmentSize,
+ 1.5 * MaxSegmentSize,
+ 2 * MaxSegmentSize,
+ 3 * MaxSegmentSize,
+ 100 * MaxSegmentSize,
+}
+
+func doIterNorm(f Form, buf []byte, s string) []byte {
+ acc := []byte{}
+ i := Iter{}
+ i.SetInputString(f, s)
+ for !i.Done() {
+ n := i.Next(buf)
+ acc = append(acc, buf[:n]...)
+ }
+ return acc
+}
+
+func runIterTests(t *testing.T, name string, f Form, tests []AppendTest, norm bool) {
+ for i, test := range tests {
+ in := test.left + test.right
+ gold := test.out
+ if norm {
+ gold = string(f.AppendString(nil, test.out))
+ }
+ for _, sz := range iterBufSizes {
+ buf := make([]byte, sz)
+ out := string(doIterNorm(f, buf, in))
+ if len(out) != len(gold) {
+ const msg = "%s:%d:%d: length is %d; want %d"
+ t.Errorf(msg, name, i, sz, len(out), len(gold))
+ }
+ if out != gold {
+ // Find first rune that differs and show context.
+ ir := []rune(out)
+ ig := []rune(gold)
+ for j := 0; j < len(ir) && j < len(ig); j++ {
+ if ir[j] == ig[j] {
+ continue
+ }
+ if j -= 3; j < 0 {
+ j = 0
+ }
+ for e := j + 7; j < e && j < len(ir) && j < len(ig); j++ {
+ const msg = "%s:%d:%d: runeAt(%d) = %U; want %U"
+ t.Errorf(msg, name, i, sz, j, ir[j], ig[j])
+ }
+ break
+ }
+ }
+ }
+ }
+}
+
+func rep(r rune, n int) string {
+ return strings.Repeat(string(r), n)
+}
+
+var iterTests = []AppendTest{
+ {"", ascii, ascii},
+ {"", txt_all, txt_all},
+ {"", "a" + rep(0x0300, MaxSegmentSize/2), "a" + rep(0x0300, MaxSegmentSize/2)},
+}
+
+var iterTestsD = []AppendTest{
+ { // segment overflow on unchanged character
+ "",
+ "a" + rep(0x0300, MaxSegmentSize/2) + "\u0316",
+ "a" + rep(0x0300, MaxSegmentSize/2-1) + "\u0316\u0300",
+ },
+ { // segment overflow on unchanged character + start value
+ "",
+ "a" + rep(0x0300, MaxSegmentSize/2+maxCombiningChars+4) + "\u0316",
+ "a" + rep(0x0300, MaxSegmentSize/2+maxCombiningChars) + "\u0316" + rep(0x300, 4),
+ },
+ { // segment overflow on decomposition
+ "",
+ "a" + rep(0x0300, MaxSegmentSize/2-1) + "\u0340",
+ "a" + rep(0x0300, MaxSegmentSize/2),
+ },
+ { // segment overflow on decomposition + start value
+ "",
+ "a" + rep(0x0300, MaxSegmentSize/2-1) + "\u0340" + rep(0x300, maxCombiningChars+4) + "\u0320",
+ "a" + rep(0x0300, MaxSegmentSize/2-1) + rep(0x300, maxCombiningChars+1) + "\u0320" + rep(0x300, 4),
+ },
+ { // start value after ASCII overflow
+ "",
+ rep('a', MaxSegmentSize) + rep(0x300, maxCombiningChars+2) + "\u0320",
+ rep('a', MaxSegmentSize) + rep(0x300, maxCombiningChars) + "\u0320\u0300\u0300",
+ },
+ { // start value after Hangul overflow
+ "",
+ rep(0xAC00, MaxSegmentSize/6) + rep(0x300, maxCombiningChars+2) + "\u0320",
+ strings.Repeat("\u1100\u1161", MaxSegmentSize/6) + rep(0x300, maxCombiningChars-1) + "\u0320" + rep(0x300, 3),
+ },
+ { // start value after cc=0
+ "",
+ "您您" + rep(0x300, maxCombiningChars+4) + "\u0320",
+ "您您" + rep(0x300, maxCombiningChars) + "\u0320" + rep(0x300, 4),
+ },
+ { // start value after normalization
+ "",
+ "\u0300\u0320a" + rep(0x300, maxCombiningChars+4) + "\u0320",
+ "\u0320\u0300a" + rep(0x300, maxCombiningChars) + "\u0320" + rep(0x300, 4),
+ },
+}
+
+var iterTestsC = []AppendTest{
+ { // ordering of non-composing combining characters
+ "",
+ "\u0305\u0316",
+ "\u0316\u0305",
+ },
+ { // segment overflow
+ "",
+ "a" + rep(0x0305, MaxSegmentSize/2+4) + "\u0316",
+ "a" + rep(0x0305, MaxSegmentSize/2-1) + "\u0316" + rep(0x305, 5),
+ },
+}
+
+func TestIterNextD(t *testing.T) {
+ runIterTests(t, "IterNextD1", NFKD, appendTests, true)
+ runIterTests(t, "IterNextD2", NFKD, iterTests, true)
+ runIterTests(t, "IterNextD3", NFKD, iterTestsD, false)
+}
+
+func TestIterNextC(t *testing.T) {
+ runIterTests(t, "IterNextC1", NFKC, appendTests, true)
+ runIterTests(t, "IterNextC2", NFKC, iterTests, true)
+ runIterTests(t, "IterNextC3", NFKC, iterTestsC, false)
+}
+
+type SegmentTest struct {
+ in string
+ out []string
+}
+
+var segmentTests = []SegmentTest{
+ {rep('a', MaxSegmentSize), []string{rep('a', MaxSegmentSize), ""}},
+ {rep('a', MaxSegmentSize+2), []string{rep('a', MaxSegmentSize-1), "aaa", ""}},
+ {rep('a', MaxSegmentSize) + "\u0300aa", []string{rep('a', MaxSegmentSize-1), "a\u0300", "aa", ""}},
+}
+
+// Note that, by design, segmentation is equal for composing and decomposing forms.
+func TestIterSegmentation(t *testing.T) {
+ segmentTest(t, "SegmentTestD", NFD, segmentTests)
+ segmentTest(t, "SegmentTestC", NFC, segmentTests)
+}
+
+func segmentTest(t *testing.T, name string, f Form, tests []SegmentTest) {
+ iter := Iter{}
+ for i, tt := range segmentTests {
+ buf := make([]byte, MaxSegmentSize)
+ iter.SetInputString(f, tt.in)
+ for j, seg := range tt.out {
+ if seg == "" {
+ if !iter.Done() {
+ n := iter.Next(buf)
+ res := string(buf[:n])
+ t.Errorf(`%s:%d:%d: expected Done()==true, found segment "%s"`, name, i, j, res)
+ }
+ continue
+ }
+ if iter.Done() {
+ t.Errorf("%s:%d:%d: Done()==true, want false", name, i, j)
+ }
+ n := iter.Next(buf)
+ seg = f.String(seg)
+ if res := string(buf[:n]); res != seg {
+ t.Errorf(`%s:%d:%d" segment was "%s" (%d); want "%s" (%d)`, name, i, j, res, len(res), seg, len(seg))
+ }
+ }
+ }
+}
lastSegStart := i
src, n := rb.src, rb.nsrc
for i < n {
- if j := src.skipASCII(i); i != j {
+ if j := src.skipASCII(i, n); i != j {
i = j
lastSegStart = i - 1
lastCC = 0
}
// Check that decomposition doesn't result in overflow.
if info.hasDecomposition() {
- dcomp := info.decomposition()
- for i := 0; i < len(dcomp); {
- inf := rb.f.info(inputBytes(dcomp), i)
- i += int(inf.size)
+ if isHangul(buf) {
+ i += int(info.size)
n++
+ } else {
+ dcomp := info.decomposition()
+ for i := 0; i < len(dcomp); {
+ inf := rb.f.info(inputBytes(dcomp), i)
+ i += int(inf.size)
+ n++
+ }
}
} else {
n++
package norm
import (
+ "bytes"
"strings"
"testing"
)
runAppendTests(t, "TestString", NFKC, stringF, appendTests)
}
+func appendBench(f Form, in []byte) func() {
+ buf := make([]byte, 0, 4*len(in))
+ return func() {
+ f.Append(buf, in...)
+ }
+}
+
+func iterBench(f Form, in []byte) func() {
+ buf := make([]byte, 4*len(in))
+ iter := Iter{}
+ return func() {
+ iter.SetInput(f, in)
+ for !iter.Done() {
+ iter.Next(buf)
+ }
+ }
+}
+
+func appendBenchmarks(bm []func(), f Form, in []byte) []func() {
+ //bm = append(bm, appendBench(f, in))
+ bm = append(bm, iterBench(f, in))
+ return bm
+}
+
func doFormBenchmark(b *testing.B, inf, f Form, s string) {
b.StopTimer()
in := inf.Bytes([]byte(s))
- buf := make([]byte, 2*len(in))
- b.SetBytes(int64(len(in)))
+ bm := appendBenchmarks(nil, f, in)
+ b.SetBytes(int64(len(in) * len(bm)))
b.StartTimer()
for i := 0; i < b.N; i++ {
- buf = f.Append(buf[0:0], in...)
- buf = buf[0:0]
+ for _, fn := range bm {
+ fn()
+ }
}
}
doFormBenchmark(b, NFD, NFD, txt_kr)
}
+var forms = []Form{NFC, NFD, NFKC, NFKD}
+
func doTextBenchmark(b *testing.B, s string) {
b.StopTimer()
- b.SetBytes(int64(len(s)) * 4)
in := []byte(s)
- var buf = make([]byte, 0, 2*len(in))
+ bm := []func(){}
+ for _, f := range forms {
+ bm = appendBenchmarks(bm, f, in)
+ }
+ b.SetBytes(int64(len(s) * len(bm)))
b.StartTimer()
for i := 0; i < b.N; i++ {
- NFC.Append(buf, in...)
- NFD.Append(buf, in...)
- NFKC.Append(buf, in...)
- NFKD.Append(buf, in...)
+ for _, f := range bm {
+ f()
+ }
}
}
func BenchmarkChinese(b *testing.B) {
doTextBenchmark(b, txt_cn)
}
+func BenchmarkOverflow(b *testing.B) {
+ doTextBenchmark(b, overflow)
+}
+
+var overflow = string(bytes.Repeat([]byte("\u035D"), 4096)) + "\u035B"
// Tests sampled from the Canonical ordering tests (Part 2) of
// http://unicode.org/Public/UNIDATA/NormalizationTest.txt
func doTest(t *Test, f norm.Form, gold, test string) {
result := f.Bytes([]byte(test))
cmpResult(t, "Bytes", f, gold, test, string(result))
+ sresult := f.String(test)
+ cmpResult(t, "String", f, gold, test, sresult)
+ buf := make([]byte, norm.MaxSegmentSize)
+ acc := []byte{}
+ i := norm.Iter{}
+ i.SetInputString(f, test)
+ for !i.Done() {
+ n := i.Next(buf)
+ acc = append(acc, buf[:n]...)
+ }
+ cmpResult(t, "Iter.Next", f, gold, test, string(acc))
for i := range test {
out := f.Append(f.Bytes([]byte(test[:i])), []byte(test[i:])...)
cmpResult(t, fmt.Sprintf(":Append:%d", i), f, gold, test, string(out))
buf = append(buf, socks5Version)
if len(s.user) > 0 && len(s.user) < 256 && len(s.password) < 256 {
- buf = append(buf, 2, /* num auth methods */ socks5AuthNone, socks5AuthPassword)
+ buf = append(buf, 2 /* num auth methods */, socks5AuthNone, socks5AuthPassword)
} else {
- buf = append(buf, 1, /* num auth methods */ socks5AuthNone)
+ buf = append(buf, 1 /* num auth methods */, socks5AuthNone)
}
if _, err = conn.Write(buf); err != nil {
}
buf = buf[:0]
- buf = append(buf, socks5Version, socks5Connect, 0 /* reserved */ )
+ buf = append(buf, socks5Version, socks5Connect, 0 /* reserved */)
if ip := net.ParseIP(host); ip != nil {
if len(ip) == 4 {
// We have a prompt and possibly user input on the screen. We
// have to clear it first.
- t.move(0, /* up */ 0, /* down */ t.cursorX, /* left */ 0 /* right */ )
+ t.move(0 /* up */, 0 /* down */, t.cursorX /* left */, 0 /* right */)
t.cursorX = 0
t.clearLineToRight()
for t.cursorY > 0 {
- t.move(1, /* up */ 0, 0, 0)
+ t.move(1 /* up */, 0, 0, 0)
t.cursorY--
t.clearLineToRight()
}
package winfsnotify
import (
+ "io/ioutil"
"os"
"testing"
"time"
t.Fatal("double Close() test failed: second Close() call didn't return")
}
- err := watcher.Watch("_test")
+ dir, err := ioutil.TempDir("", "wininotify")
+ if err != nil {
+ t.Fatalf("TempDir failed: %s", err)
+ }
+ defer os.RemoveAll(dir)
+
+ err = watcher.Watch(dir)
if err == nil {
t.Fatal("expected error on Watch() after Close(), got nil")
}
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Extract example functions from package ASTs.
+// Extract example functions from file ASTs.
package doc
import (
"go/ast"
- "go/printer"
"go/token"
+ "regexp"
+ "sort"
"strings"
"unicode"
"unicode/utf8"
)
type Example struct {
- Name string // name of the item being demonstrated
- Body *printer.CommentedNode // code
- Output string // expected output
+ Name string // name of the item being exemplified
+ Doc string // example function doc string
+ Code ast.Node
+ Comments []*ast.CommentGroup
+ Output string // expected output
}
-func Examples(pkg *ast.Package) []*Example {
+func Examples(files ...*ast.File) []*Example {
var list []*Example
- for _, file := range pkg.Files {
+ for _, file := range files {
hasTests := false // file contains tests or benchmarks
numDecl := 0 // number of non-import declarations in the file
var flist []*Example
if !isTest(name, "Example") {
continue
}
+ var doc string
+ if f.Doc != nil {
+ doc = f.Doc.Text()
+ }
flist = append(flist, &Example{
- Name: name[len("Example"):],
- Body: &printer.CommentedNode{
- Node: f.Body,
- Comments: file.Comments,
- },
- Output: f.Doc.Text(),
+ Name: name[len("Example"):],
+ Doc: doc,
+ Code: f.Body,
+ Comments: file.Comments,
+ Output: exampleOutput(f, file.Comments),
})
}
if !hasTests && numDecl > 1 && len(flist) == 1 {
// If this file only has one example function, some
// other top-level declarations, and no tests or
// benchmarks, use the whole file as the example.
- flist[0].Body.Node = file
+ flist[0].Code = file
}
list = append(list, flist...)
}
+ sort.Sort(exampleByName(list))
return list
}
+var outputPrefix = regexp.MustCompile(`(?i)^[[:space:]]*output:`)
+
+func exampleOutput(fun *ast.FuncDecl, comments []*ast.CommentGroup) string {
+ // find the last comment in the function
+ var last *ast.CommentGroup
+ for _, cg := range comments {
+ if cg.Pos() < fun.Pos() {
+ continue
+ }
+ if cg.End() > fun.End() {
+ break
+ }
+ last = cg
+ }
+ if last != nil {
+ // test that it begins with the correct prefix
+ text := last.Text()
+ if loc := outputPrefix.FindStringIndex(text); loc != nil {
+ return strings.TrimSpace(text[loc[1]:])
+ }
+ }
+ return "" // no suitable comment found
+}
+
// isTest tells whether name looks like a test, example, or benchmark.
// It is a Test (say) if there is a character after Test that is not a
// lower-case letter. (We don't want Testiness.)
rune, _ := utf8.DecodeRuneInString(name[len(prefix):])
return !unicode.IsLower(rune)
}
+
+type exampleByName []*Example
+
+func (s exampleByName) Len() int { return len(s) }
+func (s exampleByName) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+func (s exampleByName) Less(i, j int) bool { return s[i].Name < s[j].Name }
// gets to (re-)use the declaration documentation
// if there's none associated with the spec itself
fake := &ast.GenDecl{
- d.Doc, d.Pos(), token.TYPE, token.NoPos,
- []ast.Spec{s}, token.NoPos,
+ Doc: d.Doc,
+ TokPos: d.Pos(),
+ Tok: token.TYPE,
+ Specs: []ast.Spec{s},
}
r.readType(fake, s)
}
// non-empty BUG comment; collect comment without BUG prefix
list := append([]*ast.Comment(nil), c.List...) // make a copy
list[0].Text = text[m[1]:]
- r.bugs = append(r.bugs, (&ast.CommentGroup{list}).Text())
+ r.bugs = append(r.bugs, (&ast.CommentGroup{List: list}).Text())
}
}
}
_, origRecvIsPtr := newField.Type.(*ast.StarExpr)
var typ ast.Expr = ast.NewIdent(recvTypeName)
if !embeddedIsPtr && origRecvIsPtr {
- typ = &ast.StarExpr{token.NoPos, typ}
+ typ = &ast.StarExpr{X: typ}
}
newField.Type = typ
--- /dev/null
+// 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.
+
+package doc
+
+import "unicode"
+
+// firstSentenceLen returns the length of the first sentence in s.
+// The sentence ends after the first period followed by space and
+// not preceded by exactly one uppercase letter.
+//
+func firstSentenceLen(s string) int {
+ var ppp, pp, p rune
+ for i, q := range s {
+ if q == '\n' || q == '\r' || q == '\t' {
+ q = ' '
+ }
+ if q == ' ' && p == '.' && (!unicode.IsUpper(pp) || unicode.IsUpper(ppp)) {
+ return i
+ }
+ ppp, pp, p = pp, p, q
+ }
+ return len(s)
+}
+
+// Synopsis returns a cleaned version of the first sentence in s.
+// That sentence ends after the first period followed by space and
+// not preceded by exactly one uppercase letter. The result string
+// has no \n, \r, or \t characters and uses only single spaces between
+// words.
+//
+func Synopsis(s string) string {
+ n := firstSentenceLen(s)
+ var b []byte
+ p := byte(' ')
+ for i := 0; i < n; i++ {
+ q := s[i]
+ if q == '\n' || q == '\r' || q == '\t' {
+ q = ' '
+ }
+ if q != ' ' || p != ' ' {
+ b = append(b, q)
+ p = q
+ }
+ }
+ // remove trailing blank, if any
+ if n := len(b); n > 0 && p == ' ' {
+ b = b[0 : n-1]
+ }
+ return string(b)
+}
--- /dev/null
+// 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.
+
+package doc
+
+import "testing"
+
+var tests = []struct {
+ txt string
+ fsl int
+ syn string
+}{
+ {"", 0, ""},
+ {"foo", 3, "foo"},
+ {"foo.", 4, "foo."},
+ {"foo.bar", 7, "foo.bar"},
+ {" foo. ", 6, "foo."},
+ {" foo\t bar.\n", 12, "foo bar."},
+ {" foo\t bar.\n", 12, "foo bar."},
+ {"a b\n\nc\r\rd\t\t", 12, "a b c d"},
+ {"a b\n\nc\r\rd\t\t . BLA", 15, "a b c d ."},
+ {"Package poems by T.S.Eliot. To rhyme...", 27, "Package poems by T.S.Eliot."},
+ {"Package poems by T. S. Eliot. To rhyme...", 29, "Package poems by T. S. Eliot."},
+ {"foo implements the foo ABI. The foo ABI is...", 27, "foo implements the foo ABI."},
+ {"Package\nfoo. ..", 12, "Package foo."},
+ {"P . Q.", 3, "P ."},
+ {"P. Q. ", 8, "P. Q."},
+ {"Package Καλημέρα κόσμε.", 36, "Package Καλημέρα κόσμε."},
+ {"Package こんにちは 世界\n", 31, "Package こんにちは 世界"},
+}
+
+func TestSynopsis(t *testing.T) {
+ for _, e := range tests {
+ fsl := firstSentenceLen(e.txt)
+ if fsl != e.fsl {
+ t.Errorf("got fsl = %d; want %d for %q\n", fsl, e.fsl, e.txt)
+ }
+ syn := Synopsis(e.txt)
+ if syn != e.syn {
+ t.Errorf("got syn = %q; want %q for %q\n", syn, e.syn, e.txt)
+ }
+ }
+}
var benchTime = flag.Float64("test.benchtime", 1, "approximate run time for each benchmark, in seconds")
// An internal type but exported because it is cross-package; part of the implementation
-// of gotest.
+// of go test.
type InternalBenchmark struct {
Name string
F func(b *B)
}
// An internal function but exported because it is cross-package; part of the implementation
-// of gotest.
+// of go test.
func RunBenchmarks(matchString func(pat, str string) (bool, error), benchmarks []InternalBenchmark) {
// If no flag was specified, don't run benchmarks.
if len(*matchBenchmarks) == 0 {
}
// Benchmark benchmarks a single function. Useful for creating
-// custom benchmarks that do not use gotest.
+// custom benchmarks that do not use go test.
func Benchmark(f func(b *B)) BenchmarkResult {
b := &B{
common: common{
// The short flag requests that tests run more quickly, but its functionality
// is provided by test writers themselves. The testing package is just its
// home. The all.bash installation script sets it to make installation more
- // efficient, but by default the flag is off so a plain "gotest" will do a
+ // efficient, but by default the flag is off so a plain "go test" will do a
// full test of the package.
short = flag.Bool("test.short", false, "run smaller test suite to save time")
// license that can be found in the LICENSE file.
// Package testing provides support for automated testing of Go packages.
-// It is intended to be used in concert with the ``gotest'' utility, which automates
+// It is intended to be used in concert with the ``go test'' utility, which automates
// execution of any function of the form
// func TestXxx(*testing.T)
// where Xxx can be any alphanumeric string (but the first letter must not be in
//
// Functions of the form
// func BenchmarkXxx(*testing.B)
-// are considered benchmarks, and are executed by gotest when the -test.bench
+// are considered benchmarks, and are executed by go test when the -test.bench
// flag is provided.
//
// A sample benchmark function looks like this:
// The short flag requests that tests run more quickly, but its functionality
// is provided by test writers themselves. The testing package is just its
// home. The all.bash installation script sets it to make installation more
- // efficient, but by default the flag is off so a plain "gotest" will do a
+ // efficient, but by default the flag is off so a plain "go test" will do a
// full test of the package.
short = flag.Bool("test.short", false, "run smaller test suite to save time")
}
// An internal type but exported because it is cross-package; part of the implementation
-// of gotest.
+// of go test.
type InternalTest struct {
Name string
F func(*T)
}
// An internal function but exported because it is cross-package; part of the implementation
-// of gotest.
+// of go test.
func Main(matchString func(pat, str string) (bool, error), tests []InternalTest, benchmarks []InternalBenchmark, examples []InternalExample) {
flag.Parse()
parseCpuList()
}
}
- comment = &ast.Comment{p.pos, p.lit}
+ comment = &ast.Comment{Slash: p.pos, Text: p.lit}
p.next0()
return
}
// add comment group to the comments list
- comments = &ast.CommentGroup{list}
+ comments = &ast.CommentGroup{List: list}
p.comments = append(p.comments, comments)
return
} else {
p.expect(token.IDENT) // use expect() error handling
}
- return &ast.Ident{pos, name, nil}
+ return &ast.Ident{NamePos: pos, Name: name}
}
func (p *parser) parseIdentList() (list []*ast.Ident) {
pos := p.pos
p.errorExpected(pos, "type")
p.next() // make progress
- return &ast.BadExpr{pos, p.pos}
+ return &ast.BadExpr{From: pos, To: p.pos}
}
return typ
p.next()
p.resolve(ident)
sel := p.parseIdent()
- return &ast.SelectorExpr{ident, sel}
+ return &ast.SelectorExpr{X: ident, Sel: sel}
}
return ident
lbrack := p.expect(token.LBRACK)
var len ast.Expr
if ellipsisOk && p.tok == token.ELLIPSIS {
- len = &ast.Ellipsis{p.pos, nil}
+ len = &ast.Ellipsis{Ellipsis: p.pos}
p.next()
} else if p.tok != token.RBRACK {
len = p.parseRhs()
p.expect(token.RBRACK)
elt := p.parseType()
- return &ast.ArrayType{lbrack, len, elt}
+ return &ast.ArrayType{Lbrack: lbrack, Len: len, Elt: elt}
}
func (p *parser) makeIdentList(list []ast.Expr) []*ast.Ident {
if !isIdent {
pos := x.Pos()
p.errorExpected(pos, "identifier")
- ident = &ast.Ident{pos, "_", nil}
+ ident = &ast.Ident{NamePos: pos, Name: "_"}
}
idents[i] = ident
}
// optional tag
var tag *ast.BasicLit
if p.tok == token.STRING {
- tag = &ast.BasicLit{p.pos, p.tok, p.lit}
+ tag = &ast.BasicLit{ValuePos: p.pos, Kind: p.tok, Value: p.lit}
p.next()
}
if n := len(list); n > 1 || !isTypeName(deref(typ)) {
pos := typ.Pos()
p.errorExpected(pos, "anonymous field")
- typ = &ast.BadExpr{pos, list[n-1].End()}
+ typ = &ast.BadExpr{From: pos, To: list[n-1].End()}
}
}
p.expectSemi() // call before accessing p.linecomment
- field := &ast.Field{doc, idents, typ, tag, p.lineComment}
+ field := &ast.Field{Doc: doc, Names: idents, Type: typ, Tag: tag, Comment: p.lineComment}
p.declare(field, nil, scope, ast.Var, idents...)
return field
}
rbrace := p.expect(token.RBRACE)
- return &ast.StructType{pos, &ast.FieldList{lbrace, list, rbrace}, false}
+ return &ast.StructType{
+ Struct: pos,
+ Fields: &ast.FieldList{
+ Opening: lbrace,
+ List: list,
+ Closing: rbrace,
+ },
+ }
}
func (p *parser) parsePointerType() *ast.StarExpr {
star := p.expect(token.MUL)
base := p.parseType()
- return &ast.StarExpr{star, base}
+ return &ast.StarExpr{Star: star, X: base}
}
func (p *parser) tryVarType(isParam bool) ast.Expr {
typ := p.tryIdentOrType(isParam) // don't use parseType so we can provide better error message
if typ == nil {
p.error(pos, "'...' parameter is missing type")
- typ = &ast.BadExpr{pos, p.pos}
+ typ = &ast.BadExpr{From: pos, To: p.pos}
}
- return &ast.Ellipsis{pos, typ}
+ return &ast.Ellipsis{Ellipsis: pos, Elt: typ}
}
return p.tryIdentOrType(false)
}
pos := p.pos
p.errorExpected(pos, "type")
p.next() // make progress
- typ = &ast.BadExpr{pos, p.pos}
+ typ = &ast.BadExpr{From: pos, To: p.pos}
}
return typ
}
if typ != nil {
// IdentifierList Type
idents := p.makeIdentList(list)
- field := &ast.Field{nil, idents, typ, nil, nil}
+ field := &ast.Field{Names: idents, Type: typ}
params = append(params, field)
// Go spec: The scope of an identifier denoting a function
// parameter or result variable is the function body.
for p.tok != token.RPAREN && p.tok != token.EOF {
idents := p.parseIdentList()
typ := p.parseVarType(ellipsisOk)
- field := &ast.Field{nil, idents, typ, nil, nil}
+ field := &ast.Field{Names: idents, Type: typ}
params = append(params, field)
// Go spec: The scope of an identifier denoting a function
// parameter or result variable is the function body.
}
rparen := p.expect(token.RPAREN)
- return &ast.FieldList{lparen, params, rparen}
+ return &ast.FieldList{Opening: lparen, List: params, Closing: rparen}
}
func (p *parser) parseResult(scope *ast.Scope) *ast.FieldList {
scope := ast.NewScope(p.topScope) // function scope
params, results := p.parseSignature(scope)
- return &ast.FuncType{pos, params, results}, scope
+ return &ast.FuncType{Func: pos, Params: params, Results: results}, scope
}
func (p *parser) parseMethodSpec(scope *ast.Scope) *ast.Field {
idents = []*ast.Ident{ident}
scope := ast.NewScope(nil) // method scope
params, results := p.parseSignature(scope)
- typ = &ast.FuncType{token.NoPos, params, results}
+ typ = &ast.FuncType{Func: token.NoPos, Params: params, Results: results}
} else {
// embedded interface
typ = x
}
p.expectSemi() // call before accessing p.linecomment
- spec := &ast.Field{doc, idents, typ, nil, p.lineComment}
+ spec := &ast.Field{Doc: doc, Names: idents, Type: typ, Comment: p.lineComment}
p.declare(spec, nil, scope, ast.Fun, idents...)
return spec
}
rbrace := p.expect(token.RBRACE)
- return &ast.InterfaceType{pos, &ast.FieldList{lbrace, list, rbrace}, false}
+ return &ast.InterfaceType{
+ Interface: pos,
+ Methods: &ast.FieldList{
+ Opening: lbrace,
+ List: list,
+ Closing: rbrace,
+ },
+ }
}
func (p *parser) parseMapType() *ast.MapType {
p.expect(token.RBRACK)
value := p.parseType()
- return &ast.MapType{pos, key, value}
+ return &ast.MapType{Map: pos, Key: key, Value: value}
}
func (p *parser) parseChanType() *ast.ChanType {
}
value := p.parseType()
- return &ast.ChanType{pos, dir, value}
+ return &ast.ChanType{Begin: pos, Dir: dir, Value: value}
}
// If the result is an identifier, it is not resolved.
p.next()
typ := p.parseType()
rparen := p.expect(token.RPAREN)
- return &ast.ParenExpr{lparen, typ, rparen}
+ return &ast.ParenExpr{Lparen: lparen, X: typ, Rparen: rparen}
}
// no type found
p.closeScope()
rbrace := p.expect(token.RBRACE)
- return &ast.BlockStmt{lbrace, list, rbrace}
+ return &ast.BlockStmt{Lbrace: lbrace, List: list, Rbrace: rbrace}
}
func (p *parser) parseBlockStmt() *ast.BlockStmt {
p.closeScope()
rbrace := p.expect(token.RBRACE)
- return &ast.BlockStmt{lbrace, list, rbrace}
+ return &ast.BlockStmt{Lbrace: lbrace, List: list, Rbrace: rbrace}
}
// ----------------------------------------------------------------------------
body := p.parseBody(scope)
p.exprLev--
- return &ast.FuncLit{typ, body}
+ return &ast.FuncLit{Type: typ, Body: body}
}
// parseOperand may return an expression or a raw type (incl. array
return x
case token.INT, token.FLOAT, token.IMAG, token.CHAR, token.STRING:
- x := &ast.BasicLit{p.pos, p.tok, p.lit}
+ x := &ast.BasicLit{ValuePos: p.pos, Kind: p.tok, Value: p.lit}
p.next()
return x
x := p.parseRhsOrType() // types may be parenthesized: (some type)
p.exprLev--
rparen := p.expect(token.RPAREN)
- return &ast.ParenExpr{lparen, x, rparen}
+ return &ast.ParenExpr{Lparen: lparen, X: x, Rparen: rparen}
case token.FUNC:
return p.parseFuncTypeOrLit()
pos := p.pos
p.errorExpected(pos, "operand")
p.next() // make progress
- return &ast.BadExpr{pos, p.pos}
+ return &ast.BadExpr{From: pos, To: p.pos}
}
func (p *parser) parseSelector(x ast.Expr) ast.Expr {
sel := p.parseIdent()
- return &ast.SelectorExpr{x, sel}
+ return &ast.SelectorExpr{X: x, Sel: sel}
}
func (p *parser) parseTypeAssertion(x ast.Expr) ast.Expr {
}
p.expect(token.RPAREN)
- return &ast.TypeAssertExpr{x, typ}
+ return &ast.TypeAssertExpr{X: x, Type: typ}
}
func (p *parser) parseIndexOrSlice(x ast.Expr) ast.Expr {
rbrack := p.expect(token.RBRACK)
if isSlice {
- return &ast.SliceExpr{x, lbrack, low, high, rbrack}
+ return &ast.SliceExpr{X: x, Lbrack: lbrack, Low: low, High: high, Rbrack: rbrack}
}
- return &ast.IndexExpr{x, lbrack, low, rbrack}
+ return &ast.IndexExpr{X: x, Lbrack: lbrack, Index: low, Rbrack: rbrack}
}
func (p *parser) parseCallOrConversion(fun ast.Expr) *ast.CallExpr {
p.exprLev--
rparen := p.expectClosing(token.RPAREN, "argument list")
- return &ast.CallExpr{fun, lparen, list, ellipsis, rparen}
+ return &ast.CallExpr{Fun: fun, Lparen: lparen, Args: list, Ellipsis: ellipsis, Rparen: rparen}
}
func (p *parser) parseElement(keyOk bool) ast.Expr {
if p.tok == token.COLON {
colon := p.pos
p.next()
- return &ast.KeyValueExpr{x, colon, p.parseElement(false)}
+ return &ast.KeyValueExpr{Key: x, Colon: colon, Value: p.parseElement(false)}
}
p.resolve(x) // not a map key
}
}
p.exprLev--
rbrace := p.expectClosing(token.RBRACE, "composite literal")
- return &ast.CompositeLit{typ, lbrace, elts, rbrace}
+ return &ast.CompositeLit{Type: typ, Lbrace: lbrace, Elts: elts, Rbrace: rbrace}
}
// checkExpr checks that x is an expression (and not a type).
default:
// all other nodes are not proper expressions
p.errorExpected(x.Pos(), "expression")
- x = &ast.BadExpr{x.Pos(), x.End()}
+ x = &ast.BadExpr{From: x.Pos(), To: x.End()}
}
return x
}
case *ast.ArrayType:
if len, isEllipsis := t.Len.(*ast.Ellipsis); isEllipsis {
p.error(len.Pos(), "expected array length, found '...'")
- x = &ast.BadExpr{x.Pos(), x.End()}
+ x = &ast.BadExpr{From: x.Pos(), To: x.End()}
}
}
pos := p.pos
p.next() // make progress
p.errorExpected(pos, "selector or type assertion")
- x = &ast.BadExpr{pos, p.pos}
+ x = &ast.BadExpr{From: pos, To: p.pos}
}
case token.LBRACK:
if lhs {
pos, op := p.pos, p.tok
p.next()
x := p.parseUnaryExpr(false)
- return &ast.UnaryExpr{pos, op, p.checkExpr(x)}
+ return &ast.UnaryExpr{OpPos: pos, Op: op, X: p.checkExpr(x)}
case token.ARROW:
// channel type or receive expression
if p.tok == token.CHAN {
p.next()
value := p.parseType()
- return &ast.ChanType{pos, ast.RECV, value}
+ return &ast.ChanType{Begin: pos, Dir: ast.RECV, Value: value}
}
x := p.parseUnaryExpr(false)
- return &ast.UnaryExpr{pos, token.ARROW, p.checkExpr(x)}
+ return &ast.UnaryExpr{OpPos: pos, Op: token.ARROW, X: p.checkExpr(x)}
case token.MUL:
// pointer type or unary "*" expression
pos := p.pos
p.next()
x := p.parseUnaryExpr(false)
- return &ast.StarExpr{pos, p.checkExprOrType(x)}
+ return &ast.StarExpr{Star: pos, X: p.checkExprOrType(x)}
}
return p.parsePrimaryExpr(lhs)
lhs = false
}
y := p.parseBinaryExpr(false, prec+1)
- x = &ast.BinaryExpr{p.checkExpr(x), pos, op, p.checkExpr(y)}
+ x = &ast.BinaryExpr{X: p.checkExpr(x), OpPos: pos, Op: op, Y: p.checkExpr(y)}
}
}
if mode == rangeOk && p.tok == token.RANGE && (tok == token.DEFINE || tok == token.ASSIGN) {
pos := p.pos
p.next()
- y = []ast.Expr{&ast.UnaryExpr{pos, token.RANGE, p.parseRhs()}}
+ y = []ast.Expr{&ast.UnaryExpr{OpPos: pos, Op: token.RANGE, X: p.parseRhs()}}
isRange = true
} else {
y = p.parseRhsList()
}
- as := &ast.AssignStmt{x, pos, tok, y}
+ as := &ast.AssignStmt{Lhs: x, TokPos: pos, Tok: tok, Rhs: y}
if tok == token.DEFINE {
p.shortVarDecl(as, x)
}
// Go spec: The scope of a label is the body of the function
// in which it is declared and excludes the body of any nested
// function.
- stmt := &ast.LabeledStmt{label, colon, p.parseStmt()}
+ stmt := &ast.LabeledStmt{Label: label, Colon: colon, Stmt: p.parseStmt()}
p.declare(stmt, nil, p.labelScope, ast.Lbl, label)
return stmt, false
}
// before the ':' that caused the problem. Thus, use the (latest) colon
// position for error reporting.
p.error(colon, "illegal label declaration")
- return &ast.BadStmt{x[0].Pos(), colon + 1}, false
+ return &ast.BadStmt{From: x[0].Pos(), To: colon + 1}, false
case token.ARROW:
// send statement
arrow := p.pos
p.next()
y := p.parseRhs()
- return &ast.SendStmt{x[0], arrow, y}, false
+ return &ast.SendStmt{Chan: x[0], Arrow: arrow, Value: y}, false
case token.INC, token.DEC:
// increment or decrement
- s := &ast.IncDecStmt{x[0], p.pos, p.tok}
+ s := &ast.IncDecStmt{X: x[0], TokPos: p.pos, Tok: p.tok}
p.next()
return s, false
}
// expression
- return &ast.ExprStmt{x[0]}, false
+ return &ast.ExprStmt{X: x[0]}, false
}
func (p *parser) parseCallExpr() *ast.CallExpr {
call := p.parseCallExpr()
p.expectSemi()
if call == nil {
- return &ast.BadStmt{pos, pos + 2} // len("go")
+ return &ast.BadStmt{From: pos, To: pos + 2} // len("go")
}
- return &ast.GoStmt{pos, call}
+ return &ast.GoStmt{Go: pos, Call: call}
}
func (p *parser) parseDeferStmt() ast.Stmt {
call := p.parseCallExpr()
p.expectSemi()
if call == nil {
- return &ast.BadStmt{pos, pos + 5} // len("defer")
+ return &ast.BadStmt{From: pos, To: pos + 5} // len("defer")
}
- return &ast.DeferStmt{pos, call}
+ return &ast.DeferStmt{Defer: pos, Call: call}
}
func (p *parser) parseReturnStmt() *ast.ReturnStmt {
}
p.expectSemi()
- return &ast.ReturnStmt{pos, x}
+ return &ast.ReturnStmt{Return: pos, Results: x}
}
func (p *parser) parseBranchStmt(tok token.Token) *ast.BranchStmt {
}
p.expectSemi()
- return &ast.BranchStmt{pos, tok, label}
+ return &ast.BranchStmt{TokPos: pos, Tok: tok, Label: label}
}
func (p *parser) makeExpr(s ast.Stmt) ast.Expr {
return p.checkExpr(es.X)
}
p.error(s.Pos(), "expected condition, found simple statement")
- return &ast.BadExpr{s.Pos(), s.End()}
+ return &ast.BadExpr{From: s.Pos(), To: s.End()}
}
func (p *parser) parseIfStmt() *ast.IfStmt {
p.expectSemi()
}
- return &ast.IfStmt{pos, s, x, body, else_}
+ return &ast.IfStmt{If: pos, Init: s, Cond: x, Body: body, Else: else_}
}
func (p *parser) parseTypeList() (list []ast.Expr) {
body := p.parseStmtList()
p.closeScope()
- return &ast.CaseClause{pos, list, colon, body}
+ return &ast.CaseClause{Case: pos, List: list, Colon: colon, Body: body}
}
func isTypeSwitchAssert(x ast.Expr) bool {
}
rbrace := p.expect(token.RBRACE)
p.expectSemi()
- body := &ast.BlockStmt{lbrace, list, rbrace}
+ body := &ast.BlockStmt{Lbrace: lbrace, List: list, Rbrace: rbrace}
if typeSwitch {
- return &ast.TypeSwitchStmt{pos, s1, s2, body}
+ return &ast.TypeSwitchStmt{Switch: pos, Init: s1, Assign: s2, Body: body}
}
- return &ast.SwitchStmt{pos, s1, p.makeExpr(s2), body}
+ return &ast.SwitchStmt{Switch: pos, Init: s1, Tag: p.makeExpr(s2), Body: body}
}
func (p *parser) parseCommClause() *ast.CommClause {
arrow := p.pos
p.next()
rhs := p.parseRhs()
- comm = &ast.SendStmt{lhs[0], arrow, rhs}
+ comm = &ast.SendStmt{Chan: lhs[0], Arrow: arrow, Value: rhs}
} else {
// RecvStmt
if tok := p.tok; tok == token.ASSIGN || tok == token.DEFINE {
pos := p.pos
p.next()
rhs := p.parseRhs()
- as := &ast.AssignStmt{lhs, pos, tok, []ast.Expr{rhs}}
+ as := &ast.AssignStmt{Lhs: lhs, TokPos: pos, Tok: tok, Rhs: []ast.Expr{rhs}}
if tok == token.DEFINE {
p.shortVarDecl(as, lhs)
}
p.errorExpected(lhs[0].Pos(), "1 expression")
// continue with first expression
}
- comm = &ast.ExprStmt{lhs[0]}
+ comm = &ast.ExprStmt{X: lhs[0]}
}
}
} else {
body := p.parseStmtList()
p.closeScope()
- return &ast.CommClause{pos, comm, colon, body}
+ return &ast.CommClause{Case: pos, Comm: comm, Colon: colon, Body: body}
}
func (p *parser) parseSelectStmt() *ast.SelectStmt {
}
rbrace := p.expect(token.RBRACE)
p.expectSemi()
- body := &ast.BlockStmt{lbrace, list, rbrace}
+ body := &ast.BlockStmt{Lbrace: lbrace, List: list, Rbrace: rbrace}
- return &ast.SelectStmt{pos, body}
+ return &ast.SelectStmt{Select: pos, Body: body}
}
func (p *parser) parseForStmt() ast.Stmt {
key = as.Lhs[0]
default:
p.errorExpected(as.Lhs[0].Pos(), "1 or 2 expressions")
- return &ast.BadStmt{pos, body.End()}
+ return &ast.BadStmt{From: pos, To: body.End()}
}
// parseSimpleStmt returned a right-hand side that
// is a single unary expression of the form "range x"
x := as.Rhs[0].(*ast.UnaryExpr).X
- return &ast.RangeStmt{pos, key, value, as.TokPos, as.Tok, x, body}
+ return &ast.RangeStmt{
+ For: pos,
+ Key: key,
+ Value: value,
+ TokPos: as.TokPos,
+ Tok: as.Tok,
+ X: x,
+ Body: body,
+ }
}
// regular for statement
- return &ast.ForStmt{pos, s1, p.makeExpr(s2), s3, body}
+ return &ast.ForStmt{
+ For: pos,
+ Init: s1,
+ Cond: p.makeExpr(s2),
+ Post: s3,
+ Body: body,
+ }
}
func (p *parser) parseStmt() (s ast.Stmt) {
switch p.tok {
case token.CONST, token.TYPE, token.VAR:
- s = &ast.DeclStmt{p.parseDecl()}
+ s = &ast.DeclStmt{Decl: p.parseDecl()}
case
- // tokens that may start a top-level expression
- token.IDENT, token.INT, token.FLOAT, token.CHAR, token.STRING, token.FUNC, token.LPAREN, // operand
- token.LBRACK, token.STRUCT, // composite type
- token.MUL, token.AND, token.ARROW, token.ADD, token.SUB, token.XOR: // unary operators
+ // tokens that may start an expression
+ token.IDENT, token.INT, token.FLOAT, token.IMAG, token.CHAR, token.STRING, token.FUNC, token.LPAREN, // operands
+ token.LBRACK, token.STRUCT, // composite types
+ token.ADD, token.SUB, token.MUL, token.AND, token.XOR, token.ARROW, token.NOT: // unary operators
s, _ = p.parseSimpleStmt(labelOk)
// because of the required look-ahead, labeled statements are
// parsed by parseSimpleStmt - don't expect a semicolon after
case token.FOR:
s = p.parseForStmt()
case token.SEMICOLON:
- s = &ast.EmptyStmt{p.pos}
+ s = &ast.EmptyStmt{Semicolon: p.pos}
p.next()
case token.RBRACE:
// a semicolon may be omitted before a closing "}"
- s = &ast.EmptyStmt{p.pos}
+ s = &ast.EmptyStmt{Semicolon: p.pos}
default:
// no statement found
pos := p.pos
p.errorExpected(pos, "statement")
p.next() // make progress
- s = &ast.BadStmt{pos, p.pos}
+ s = &ast.BadStmt{From: pos, To: p.pos}
}
return
var ident *ast.Ident
switch p.tok {
case token.PERIOD:
- ident = &ast.Ident{p.pos, ".", nil}
+ ident = &ast.Ident{NamePos: p.pos, Name: "."}
p.next()
case token.IDENT:
ident = p.parseIdent()
var path *ast.BasicLit
if p.tok == token.STRING {
- path = &ast.BasicLit{p.pos, p.tok, p.lit}
+ path = &ast.BasicLit{ValuePos: p.pos, Kind: p.tok, Value: p.lit}
p.next()
} else {
p.expect(token.STRING) // use expect() error handling
p.expectSemi() // call before accessing p.linecomment
// collect imports
- spec := &ast.ImportSpec{doc, ident, path, p.lineComment, token.NoPos}
+ spec := &ast.ImportSpec{
+ Doc: doc,
+ Name: ident,
+ Path: path,
+ Comment: p.lineComment,
+ }
p.imports = append(p.imports, spec)
return spec
// a function begins at the end of the ConstSpec or VarSpec and ends at
// the end of the innermost containing block.
// (Global identifiers are resolved in a separate phase after parsing.)
- spec := &ast.ValueSpec{doc, idents, typ, values, p.lineComment}
+ spec := &ast.ValueSpec{
+ Doc: doc,
+ Names: idents,
+ Type: typ,
+ Values: values,
+ Comment: p.lineComment,
+ }
p.declare(spec, iota, p.topScope, ast.Con, idents...)
return spec
// at the identifier in the TypeSpec and ends at the end of the innermost
// containing block.
// (Global identifiers are resolved in a separate phase after parsing.)
- spec := &ast.TypeSpec{doc, ident, nil, nil}
+ spec := &ast.TypeSpec{Doc: doc, Name: ident}
p.declare(spec, nil, p.topScope, ast.Typ, ident)
spec.Type = p.parseType()
// a function begins at the end of the ConstSpec or VarSpec and ends at
// the end of the innermost containing block.
// (Global identifiers are resolved in a separate phase after parsing.)
- spec := &ast.ValueSpec{doc, idents, typ, values, p.lineComment}
+ spec := &ast.ValueSpec{
+ Doc: doc,
+ Names: idents,
+ Type: typ,
+ Values: values,
+ Comment: p.lineComment,
+ }
p.declare(spec, nil, p.topScope, ast.Var, idents...)
return spec
list = append(list, f(p, nil, 0))
}
- return &ast.GenDecl{doc, pos, keyword, lparen, list, rparen}
+ return &ast.GenDecl{
+ Doc: doc,
+ TokPos: pos,
+ Tok: keyword,
+ Lparen: lparen,
+ Specs: list,
+ Rparen: rparen,
+ }
}
func (p *parser) parseReceiver(scope *ast.Scope) *ast.FieldList {
// must have exactly one receiver
if par.NumFields() != 1 {
p.errorExpected(par.Opening, "exactly one receiver")
- par.List = []*ast.Field{{Type: &ast.BadExpr{par.Opening, par.Closing + 1}}}
+ par.List = []*ast.Field{{Type: &ast.BadExpr{From: par.Opening, To: par.Closing + 1}}}
return par
}
base := deref(recv.Type)
if _, isIdent := base.(*ast.Ident); !isIdent {
p.errorExpected(base.Pos(), "(unqualified) identifier")
- par.List = []*ast.Field{{Type: &ast.BadExpr{recv.Pos(), recv.End()}}}
+ par.List = []*ast.Field{{Type: &ast.BadExpr{From: recv.Pos(), To: recv.End()}}}
}
return par
}
p.expectSemi()
- decl := &ast.FuncDecl{doc, recv, ident, &ast.FuncType{pos, params, results}, body}
+ decl := &ast.FuncDecl{
+ Doc: doc,
+ Recv: recv,
+ Name: ident,
+ Type: &ast.FuncType{
+ Func: pos,
+ Params: params,
+ Results: results,
+ },
+ Body: body,
+ }
if recv == nil {
// Go spec: The scope of an identifier denoting a constant, type,
// variable, or function (but not method) declared at top level
pos := p.pos
p.errorExpected(pos, "declaration")
p.next() // make progress
- decl := &ast.BadDecl{pos, p.pos}
+ decl := &ast.BadDecl{From: pos, To: p.pos}
return decl
}
}
}
- return &ast.File{doc, pos, ident, decls, p.pkgScope, p.imports, p.unresolved[0:i], p.comments}
+ return &ast.File{
+ Doc: doc,
+ Package: pos,
+ Name: ident,
+ Decls: decls,
+ Scope: p.pkgScope,
+ Imports: p.imports,
+ Unresolved: p.unresolved[0:i],
+ Comments: p.comments,
+ }
}
commaSep // elements are separated by commas
commaTerm // list is optionally terminated by a comma
noIndent // no extra indentation in multi-line lists
- periodSep // elements are separated by periods
)
// Sets multiLine to true if the identifier list spans multiple lines.
for i, x := range list {
if i > 0 {
if mode&commaSep != 0 {
- p.print(token.COMMA)
+ // use position of expression following the comma as
+ // comma position for correct comment placement
+ p.print(x.Pos(), token.COMMA)
}
p.print(blank)
}
}
if i > 0 {
- switch {
- case mode&commaSep != 0:
+ needsLinebreak := prevLine < line && prevLine > 0 && line > 0
+ if mode&commaSep != 0 {
+ // use position of expression following the comma as
+ // comma position for correct comment placement, but
+ // only if the expression is on the same line
+ if !needsLinebreak {
+ p.print(x.Pos())
+ }
p.print(token.COMMA)
- case mode&periodSep != 0:
- p.print(token.PERIOD)
}
- needsBlank := mode&periodSep == 0 // period-separated list elements don't need a blank
- if prevLine < line && prevLine > 0 && line > 0 {
+ needsBlank := true
+ if needsLinebreak {
// lines are broken using newlines so comments remain aligned
// unless forceFF is set or there are multiple expressions on
// the same line in which case formfeed is used
parLineBeg = parLineEnd
}
// separating "," if needed
+ needsLinebreak := 0 < prevLine && prevLine < parLineBeg
if i > 0 {
+ // use position of parameter following the comma as
+ // comma position for correct comma placement, but
+ // only if the next parameter is on the same line
+ if !needsLinebreak {
+ p.print(par.Pos())
+ }
p.print(token.COMMA)
}
// separator if needed (linebreak or blank)
- if 0 < prevLine && prevLine < parLineBeg && p.linebreak(parLineBeg, 0, ws, true) {
+ if needsLinebreak && p.linebreak(parLineBeg, 0, ws, true) {
// break line if the opening "(" or previous parameter ended on a different line
ws = ignore
*multiLine = true
// if the closing ")" is on a separate line from the last parameter,
// print an additional "," and line break
if closing := p.lineFor(fields.Closing); 0 < prevLine && prevLine < closing {
- p.print(",")
+ p.print(token.COMMA)
p.linebreak(closing, 0, ignore, true)
}
// unindent if we indented
}
func (p *printer) setLineComment(text string) {
- p.setComment(&ast.CommentGroup{[]*ast.Comment{{token.NoPos, text}}})
+ p.setComment(&ast.CommentGroup{List: []*ast.Comment{{Slash: token.NoPos, Text: text}}})
}
func (p *printer) fieldList(fields *ast.FieldList, isStruct, isIncomplete bool) {
f := list[0]
for i, x := range f.Names {
if i > 0 {
+ // no comments so no need for comma position
p.print(token.COMMA, blank)
}
p.expr(x, ignoreMultiLine)
return ok
}
-// If the expression contains one or more selector expressions, splits it into
-// two expressions at the rightmost period. Writes entire expr to suffix when
-// selector isn't found. Rewrites AST nodes for calls, index expressions and
-// type assertions, all of which may be found in selector chains, to make them
-// parts of the chain.
-func splitSelector(expr ast.Expr) (body, suffix ast.Expr) {
- switch x := expr.(type) {
- case *ast.SelectorExpr:
- body, suffix = x.X, x.Sel
- return
- case *ast.CallExpr:
- body, suffix = splitSelector(x.Fun)
- if body != nil {
- suffix = &ast.CallExpr{suffix, x.Lparen, x.Args, x.Ellipsis, x.Rparen}
- return
- }
- case *ast.IndexExpr:
- body, suffix = splitSelector(x.X)
- if body != nil {
- suffix = &ast.IndexExpr{suffix, x.Lbrack, x.Index, x.Rbrack}
- return
- }
- case *ast.SliceExpr:
- body, suffix = splitSelector(x.X)
- if body != nil {
- suffix = &ast.SliceExpr{suffix, x.Lbrack, x.Low, x.High, x.Rbrack}
- return
- }
- case *ast.TypeAssertExpr:
- body, suffix = splitSelector(x.X)
- if body != nil {
- suffix = &ast.TypeAssertExpr{suffix, x.Type}
- return
- }
- }
- suffix = expr
- return
-}
-
-// Convert an expression into an expression list split at the periods of
-// selector expressions.
-func selectorExprList(expr ast.Expr) (list []ast.Expr) {
- // split expression
- for expr != nil {
- var suffix ast.Expr
- expr, suffix = splitSelector(expr)
- list = append(list, suffix)
- }
-
- // reverse list
- for i, j := 0, len(list)-1; i < j; i, j = i+1, j-1 {
- list[i], list[j] = list[j], list[i]
- }
-
- return
-}
-
// Sets multiLine to true if the expression spans multiple lines.
func (p *printer) expr1(expr ast.Expr, prec1, depth int, multiLine *bool) {
p.print(expr.Pos())
}
case *ast.SelectorExpr:
- parts := selectorExprList(expr)
- p.exprList(token.NoPos, parts, depth, periodSep, multiLine, token.NoPos)
+ p.expr1(x.X, token.HighestPrec, depth, multiLine)
+ p.print(token.PERIOD)
+ if line := p.lineFor(x.Sel.Pos()); p.pos.IsValid() && p.pos.Line < line {
+ p.print(indent, newline, x.Sel.Pos(), x.Sel, unindent)
+ *multiLine = true
+ } else {
+ p.print(x.Sel.Pos(), x.Sel)
+ }
case *ast.TypeAssertExpr:
p.expr1(x.X, token.HighestPrec, depth, multiLine)
p.print(token.FOR, blank)
p.expr(s.Key, multiLine)
if s.Value != nil {
- p.print(token.COMMA, blank)
+ // use position of value following the comma as
+ // comma position for correct comment placement
+ p.print(s.Value.Pos(), token.COMMA, blank)
p.expr(s.Value, multiLine)
}
p.print(blank, s.TokPos, s.Tok, blank, token.RANGE, blank)
}
if last != nil {
- if last.Text[1] == '*' && p.lineFor(last.Pos()) == next.Line {
- // the last comment is a /*-style comment and the next item
- // follows on the same line: separate with an extra blank
+ // if the last comment is a /*-style comment and the next item
+ // follows on the same line but is not a comma or a "closing"
+ // token, add an extra blank for separation
+ if last.Text[1] == '*' && p.lineFor(last.Pos()) == next.Line && tok != token.COMMA &&
+ tok != token.RPAREN && tok != token.RBRACK && tok != token.RBRACE {
p.writeByte(' ', 1)
}
// ensure that there is a line break after a //-style comment,
t.Error("expected offset 1") // error in test
}
- testComment(t, f, len(src), &ast.Comment{pos, "//-style comment"})
- testComment(t, f, len(src), &ast.Comment{pos, "/*-style comment */"})
- testComment(t, f, len(src), &ast.Comment{pos, "/*-style \n comment */"})
- testComment(t, f, len(src), &ast.Comment{pos, "/*-style comment \n\n\n */"})
+ testComment(t, f, len(src), &ast.Comment{Slash: pos, Text: "//-style comment"})
+ testComment(t, f, len(src), &ast.Comment{Slash: pos, Text: "/*-style comment */"})
+ testComment(t, f, len(src), &ast.Comment{Slash: pos, Text: "/*-style \n comment */"})
+ testComment(t, f, len(src), &ast.Comment{Slash: pos, Text: "/*-style comment \n\n\n */"})
}
type visitor chan *ast.Ident
}
// Some interesting interspersed comments.
+// See below for more common cases.
func _( /* this */ x /* is */ /* an */ int) {
}
-func _( /* no params */ ) {}
+func _( /* no params */) {}
func _() {
- f( /* no args */ )
+ f( /* no args */)
}
-func ( /* comment1 */ T /* comment2 */ ) _() {}
+func ( /* comment1 */ T /* comment2 */) _() {}
func _() { /* one-line functions with comments are formatted as multi-line functions */
}
}
func _() {
- _ = []int{0, 1 /* don't introduce a newline after this comment - was issue 1365 */ }
+ _ = []int{0, 1 /* don't introduce a newline after this comment - was issue 1365 */}
}
// Test cases from issue 1542:
_ = a
}
-// Comments immediately adjacent to punctuation (for which the go/printer
-// may only have estimated position information) must remain after the punctuation.
+// Comments immediately adjacent to punctuation followed by a newline
+// remain after the punctuation (looks better and permits alignment of
+// comments).
func _() {
_ = T{
1, // comment after comma
}
}
+// If there is no newline following punctuation, commas move before the punctuation.
+// This way, commas interspersed in lists stay with the respective expression.
+func f(x /* comment */, y int, z int /* comment */, u, v, w int /* comment */) {
+ f(x /* comment */, y)
+ f(x, /* comment */
+ y)
+ f(
+ x, /* comment */
+ )
+}
+
+func g(
+ x int, /* comment */
+) {
+}
+
+type _ struct {
+ a, b /* comment */, c int
+}
+
+type _ struct {
+ a, b /* comment */, c int
+}
+
+func _() {
+ for a /* comment */, b := range x {
+ }
+}
+
// Print line directives correctly.
// The following is a legal line directive.
// Some interesting interspersed comments.
+// See below for more common cases.
func _(/* this */x/* is *//* an */ int) {
}
_ = a
}
-// Comments immediately adjacent to punctuation (for which the go/printer
-// may only have estimated position information) must remain after the punctuation.
+// Comments immediately adjacent to punctuation followed by a newline
+// remain after the punctuation (looks better and permits alignment of
+// comments).
func _() {
_ = T{
1, // comment after comma
}
}
+// If there is no newline following punctuation, commas move before the punctuation.
+// This way, commas interspersed in lists stay with the respective expression.
+func f(x/* comment */, y int, z int /* comment */, u, v, w int /* comment */) {
+ f(x /* comment */, y)
+ f(x /* comment */,
+ y)
+ f(
+ x /* comment */,
+ )
+}
+
+func g(
+ x int /* comment */,
+) {}
+
+type _ struct {
+ a, b /* comment */, c int
+}
+
+type _ struct { a, b /* comment */, c int }
+
+func _() {
+ for a /* comment */, b := range x {
+ }
+}
// Print line directives correctly.
// handle multiline argument list correctly
_ = new(T).
foo(
- 1).
+ 1).
foo(2)
_ = new(T).foo(
_ = new(T).
Field.
Array[3+
- 4].
+ 4].
Table["foo"].
Blob.(*Type).
Slices[1:4].
Method(1, 2,
- 3).
+ 3).
Thingy
_ = a.b.c
// handle multiline argument list correctly
_ = new(T).
foo(
- 1).
+ 1).
foo(2)
_ = new(T).foo(
_ = new(T).
Field.
Array[3+
- 4].
+ 4].
Table["foo"].
Blob.(*Type).
Slices[1:4].
Method(1, 2,
- 3).
+ 3).
Thingy
_ = a.b.c
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Package scanner implements a scanner for Go source text. Takes a []byte as
-// source which can then be tokenized through repeated calls to the Scan
-// function. Typical use:
-//
-// var s scanner.Scanner
-// fset := token.NewFileSet() // position information is relative to fset
-// file := fset.AddFile(filename, fset.Base(), len(src)) // register file
-// s.Init(file, src, nil /* no error handler */, 0)
-// for {
-// pos, tok, lit := s.Scan()
-// if tok == token.EOF {
-// break
-// }
-// // do something here with pos, tok, and lit
-// }
+// Package scanner implements a scanner for Go source text.
+// It takes a []byte as source which can then be tokenized
+// through repeated calls to the Scan method.
//
package scanner
+++ /dev/null
-// 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 template
-
-import (
- "text/template/parse"
-)
-
-// clone clones a template Node.
-func clone(n parse.Node) parse.Node {
- switch t := n.(type) {
- case *parse.ActionNode:
- return cloneAction(t)
- case *parse.IfNode:
- b := new(parse.IfNode)
- copyBranch(&b.BranchNode, &t.BranchNode)
- return b
- case *parse.ListNode:
- return cloneList(t)
- case *parse.RangeNode:
- b := new(parse.RangeNode)
- copyBranch(&b.BranchNode, &t.BranchNode)
- return b
- case *parse.TemplateNode:
- return cloneTemplate(t)
- case *parse.TextNode:
- return cloneText(t)
- case *parse.WithNode:
- b := new(parse.WithNode)
- copyBranch(&b.BranchNode, &t.BranchNode)
- return b
- }
- panic("cloning " + n.String() + " is unimplemented")
-}
-
-// cloneAction returns a deep clone of n.
-func cloneAction(n *parse.ActionNode) *parse.ActionNode {
- // We use keyless fields because they won't compile if a field is added.
- return &parse.ActionNode{n.NodeType, n.Line, clonePipe(n.Pipe)}
-}
-
-// cloneList returns a deep clone of n.
-func cloneList(n *parse.ListNode) *parse.ListNode {
- if n == nil {
- return nil
- }
- // We use keyless fields because they won't compile if a field is added.
- c := parse.ListNode{n.NodeType, make([]parse.Node, len(n.Nodes))}
- for i, child := range n.Nodes {
- c.Nodes[i] = clone(child)
- }
- return &c
-}
-
-// clonePipe returns a shallow clone of n.
-// The escaper does not modify pipe descendants in place so there's no need to
-// clone deeply.
-func clonePipe(n *parse.PipeNode) *parse.PipeNode {
- if n == nil {
- return nil
- }
- // We use keyless fields because they won't compile if a field is added.
- return &parse.PipeNode{n.NodeType, n.Line, n.Decl, n.Cmds}
-}
-
-// cloneTemplate returns a deep clone of n.
-func cloneTemplate(n *parse.TemplateNode) *parse.TemplateNode {
- // We use keyless fields because they won't compile if a field is added.
- return &parse.TemplateNode{n.NodeType, n.Line, n.Name, clonePipe(n.Pipe)}
-}
-
-// cloneText clones the given node sharing its []byte.
-func cloneText(n *parse.TextNode) *parse.TextNode {
- // We use keyless fields because they won't compile if a field is added.
- return &parse.TextNode{n.NodeType, n.Text}
-}
-
-// copyBranch clones src into dst.
-func copyBranch(dst, src *parse.BranchNode) {
- // We use keyless fields because they won't compile if a field is added.
- *dst = parse.BranchNode{
- src.NodeType,
- src.Line,
- clonePipe(src.Pipe),
- cloneList(src.List),
- cloneList(src.ElseList),
- }
-}
import (
"bytes"
"testing"
+ "text/template/parse"
)
+func TestAddParseTree(t *testing.T) {
+ root := Must(New("root").Parse(`{{define "a"}} {{.}} {{template "b"}} {{.}} "></a>{{end}}`))
+ tree, err := parse.Parse("t", `{{define "b"}}<a href="{{end}}`, "", "", nil, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ added := Must(root.AddParseTree("b", tree["b"]))
+ b := new(bytes.Buffer)
+ err = added.ExecuteTemplate(b, "a", "1>0")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if got, want := b.String(), ` 1>0 <a href=" 1%3e0 "></a>`; got != want {
+ t.Errorf("got %q want %q", got, want)
+ }
+}
+
func TestClone(t *testing.T) {
- tests := []struct {
- input, want, wantClone string
- }{
- {
- `Hello, {{if true}}{{"<World>"}}{{end}}!`,
- "Hello, <World>!",
- "Hello, <World>!",
- },
- {
- `Hello, {{if false}}{{.X}}{{else}}{{"<World>"}}{{end}}!`,
- "Hello, <World>!",
- "Hello, <World>!",
- },
- {
- `Hello, {{with "<World>"}}{{.}}{{end}}!`,
- "Hello, <World>!",
- "Hello, <World>!",
- },
- {
- `{{range .}}<p>{{.}}</p>{{end}}`,
- "<p>foo</p><p><bar></p><p>baz</p>",
- "<p>foo</p><p><bar></p><p>baz</p>",
- },
- {
- `Hello, {{"<World>" | html}}!`,
- "Hello, <World>!",
- "Hello, <World>!",
- },
- {
- `Hello{{if 1}}, World{{else}}{{template "d"}}{{end}}!`,
- "Hello, World!",
- "Hello, World!",
- },
+ // The {{.}} will be executed with data "<i>*/" in different contexts.
+ // In the t0 template, it will be in a text context.
+ // In the t1 template, it will be in a URL context.
+ // In the t2 template, it will be in a JavaScript context.
+ // In the t3 template, it will be in a CSS context.
+ const tmpl = `{{define "a"}}{{template "lhs"}}{{.}}{{template "rhs"}}{{end}}`
+ b := new(bytes.Buffer)
+
+ // Create an incomplete template t0.
+ t0 := Must(New("t0").Parse(tmpl))
+
+ // Clone t0 as t1.
+ t1 := Must(t0.Clone())
+ Must(t1.Parse(`{{define "lhs"}} <a href=" {{end}}`))
+ Must(t1.Parse(`{{define "rhs"}} "></a> {{end}}`))
+
+ // Execute t1.
+ b.Reset()
+ if err := t1.ExecuteTemplate(b, "a", "<i>*/"); err != nil {
+ t.Fatal(err)
+ }
+ if got, want := b.String(), ` <a href=" %3ci%3e*/ "></a> `; got != want {
+ t.Errorf("t1: got %q want %q", got, want)
+ }
+
+ // Clone t0 as t2.
+ t2 := Must(t0.Clone())
+ Must(t2.Parse(`{{define "lhs"}} <p onclick="javascript: {{end}}`))
+ Must(t2.Parse(`{{define "rhs"}} "></p> {{end}}`))
+
+ // Execute t2.
+ b.Reset()
+ if err := t2.ExecuteTemplate(b, "a", "<i>*/"); err != nil {
+ t.Fatal(err)
+ }
+ if got, want := b.String(), ` <p onclick="javascript: "\u003ci\u003e*/" "></p> `; got != want {
+ t.Errorf("t2: got %q want %q", got, want)
}
- for _, test := range tests {
- s, err := New("s").Parse(test.input)
- if err != nil {
- t.Errorf("input=%q: unexpected parse error %v", test.input, err)
- }
-
- d, _ := New("d").Parse(test.input)
- // Hack: just replace the root of the tree.
- d.text.Root = cloneList(s.text.Root)
-
- if want, got := s.text.Root.String(), d.text.Root.String(); want != got {
- t.Errorf("want %q, got %q", want, got)
- }
-
- err = escapeTemplates(d, "d")
- if err != nil {
- t.Errorf("%q: failed to escape: %s", test.input, err)
- continue
- }
-
- if want, got := "s", s.Name(); want != got {
- t.Errorf("want %q, got %q", want, got)
- continue
- }
- if want, got := "d", d.Name(); want != got {
- t.Errorf("want %q, got %q", want, got)
- continue
- }
-
- data := []string{"foo", "<bar>", "baz"}
-
- var b bytes.Buffer
- d.Execute(&b, data)
- if got := b.String(); got != test.wantClone {
- t.Errorf("input=%q: want %q, got %q", test.input, test.wantClone, got)
- }
-
- // Make sure escaping d did not affect s.
- b.Reset()
- s.text.Execute(&b, data)
- if got := b.String(); got != test.want {
- t.Errorf("input=%q: want %q, got %q", test.input, test.want, got)
- }
+ // Clone t0 as t3, but do not execute t3 yet.
+ t3 := Must(t0.Clone())
+ Must(t3.Parse(`{{define "lhs"}} <style> {{end}}`))
+ Must(t3.Parse(`{{define "rhs"}} </style> {{end}}`))
+
+ // Complete t0.
+ Must(t0.Parse(`{{define "lhs"}} ( {{end}}`))
+ Must(t0.Parse(`{{define "rhs"}} ) {{end}}`))
+
+ // Clone t0 as t4. Redefining the "lhs" template should fail.
+ t4 := Must(t0.Clone())
+ if _, err := t4.Parse(`{{define "lhs"}} FAIL {{end}}`); err == nil {
+ t.Error(`redefine "lhs": got nil err want non-nil`)
+ }
+
+ // Execute t0.
+ b.Reset()
+ if err := t0.ExecuteTemplate(b, "a", "<i>*/"); err != nil {
+ t.Fatal(err)
+ }
+ if got, want := b.String(), ` ( <i>*/ ) `; got != want {
+ t.Errorf("t0: got %q want %q", got, want)
+ }
+
+ // Clone t0. This should fail, as t0 has already executed.
+ if _, err := t0.Clone(); err == nil {
+ t.Error(`t0.Clone(): got nil err want non-nil`)
+ }
+
+ // Similarly, cloning sub-templates should fail.
+ if _, err := t0.Lookup("a").Clone(); err == nil {
+ t.Error(`t0.Lookup("a").Clone(): got nil err want non-nil`)
+ }
+ if _, err := t0.Lookup("lhs").Clone(); err == nil {
+ t.Error(`t0.Lookup("lhs").Clone(): got nil err want non-nil`)
+ }
+
+ // Execute t3.
+ b.Reset()
+ if err := t3.ExecuteTemplate(b, "a", "<i>*/"); err != nil {
+ t.Fatal(err)
+ }
+ if got, want := b.String(), ` <style> ZgotmplZ </style> `; got != want {
+ t.Errorf("t3: got %q want %q", got, want)
}
}
return v.Interface()
}
+var (
+ errorType = reflect.TypeOf((*error)(nil)).Elem()
+ fmtStringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
+)
+
+// indirectToStringerOrError returns the value, after dereferencing as many times
+// as necessary to reach the base type (or nil) or an implementation of fmt.Stringer
+// or error,
+func indirectToStringerOrError(a interface{}) interface{} {
+ v := reflect.ValueOf(a)
+ for !v.Type().Implements(fmtStringerType) && !v.Type().Implements(errorType) && v.Kind() == reflect.Ptr && !v.IsNil() {
+ v = v.Elem()
+ }
+ return v.Interface()
+}
+
// stringify converts its arguments to a string and the type of the content.
// All pointers are dereferenced, as in the text/template package.
func stringify(args ...interface{}) (string, contentType) {
}
}
for i, arg := range args {
- args[i] = indirect(arg)
+ args[i] = indirectToStringerOrError(arg)
}
return fmt.Sprint(args...), contentTypePlain
}
import (
"bytes"
+ "fmt"
"strings"
"testing"
)
}
}
}
+
+// Test that we print using the String method. Was issue 3073.
+type stringer struct {
+ v int
+}
+
+func (s *stringer) String() string {
+ return fmt.Sprintf("string=%d", s.v)
+}
+
+type errorer struct {
+ v int
+}
+
+func (s *errorer) Error() string {
+ return fmt.Sprintf("error=%d", s.v)
+}
+
+func TestStringer(t *testing.T) {
+ s := &stringer{3}
+ b := new(bytes.Buffer)
+ tmpl := Must(New("x").Parse("{{.}}"))
+ if err := tmpl.Execute(b, s); err != nil {
+ t.Fatal(err)
+ }
+ var expect = "string=3"
+ if b.String() != expect {
+ t.Errorf("expected %q got %q", expect, b.String())
+ }
+ e := &errorer{7}
+ b.Reset()
+ if err := tmpl.Execute(b, e); err != nil {
+ t.Fatal(err)
+ }
+ expect = "error=7"
+ if b.String() != expect {
+ t.Errorf("expected %q got %q", expect, b.String())
+ }
+}
This package wraps package text/template so you can share its template API
to parse and execute HTML templates safely.
- set, err := new(template.Set).Parse(...)
+ tmpl, err := template.New("name").Parse(...)
// Error checking elided
- err = set.Execute(out, "Foo", data)
+ err = tmpl.Execute(out, "Foo", data)
-If successful, set will now be injection-safe. Otherwise, err is an error
+If successful, tmpl will now be injection-safe. Otherwise, err is an error
defined in the docs for ErrorCode.
HTML templates treat data values as plain text which should be encoded so they
parameter is not, and seeks to preserve the properties below in the face
of untrusted data:
-Structure Preservation Property
+Structure Preservation Property:
"... when a template author writes an HTML tag in a safe templating language,
the browser will interpret the corresponding portion of the output as a tag
regardless of the values of untrusted data, and similarly for other structures
such as attribute boundaries and JS and CSS string boundaries."
-Code Effect Property
+Code Effect Property:
"... only code specified by the template author should run as a result of
injecting the template output into a page and all code specified by the
template author should run as a result of the same."
-Least Surprise Property
+Least Surprise Property:
"A developer (or code reviewer) familiar with HTML, CSS, and JavaScript, who
knows that contextual autoescaping happens should be able to look at a {{.}}
and correctly infer what sanitization happens."
// funcMap maps command names to functions that render their inputs safe.
var funcMap = template.FuncMap{
- "exp_template_html_attrescaper": attrEscaper,
- "exp_template_html_commentescaper": commentEscaper,
- "exp_template_html_cssescaper": cssEscaper,
- "exp_template_html_cssvaluefilter": cssValueFilter,
- "exp_template_html_htmlnamefilter": htmlNameFilter,
- "exp_template_html_htmlescaper": htmlEscaper,
- "exp_template_html_jsregexpescaper": jsRegexpEscaper,
- "exp_template_html_jsstrescaper": jsStrEscaper,
- "exp_template_html_jsvalescaper": jsValEscaper,
- "exp_template_html_nospaceescaper": htmlNospaceEscaper,
- "exp_template_html_rcdataescaper": rcdataEscaper,
- "exp_template_html_urlescaper": urlEscaper,
- "exp_template_html_urlfilter": urlFilter,
- "exp_template_html_urlnormalizer": urlNormalizer,
+ "html_template_attrescaper": attrEscaper,
+ "html_template_commentescaper": commentEscaper,
+ "html_template_cssescaper": cssEscaper,
+ "html_template_cssvaluefilter": cssValueFilter,
+ "html_template_htmlnamefilter": htmlNameFilter,
+ "html_template_htmlescaper": htmlEscaper,
+ "html_template_jsregexpescaper": jsRegexpEscaper,
+ "html_template_jsstrescaper": jsStrEscaper,
+ "html_template_jsvalescaper": jsValEscaper,
+ "html_template_nospaceescaper": htmlNospaceEscaper,
+ "html_template_rcdataescaper": rcdataEscaper,
+ "html_template_urlescaper": urlEscaper,
+ "html_template_urlfilter": urlFilter,
+ "html_template_urlnormalizer": urlNormalizer,
}
// equivEscapers matches contextual escapers to equivalent template builtins.
var equivEscapers = map[string]string{
- "exp_template_html_attrescaper": "html",
- "exp_template_html_htmlescaper": "html",
- "exp_template_html_nospaceescaper": "html",
- "exp_template_html_rcdataescaper": "html",
- "exp_template_html_urlescaper": "urlquery",
- "exp_template_html_urlnormalizer": "urlquery",
+ "html_template_attrescaper": "html",
+ "html_template_htmlescaper": "html",
+ "html_template_nospaceescaper": "html",
+ "html_template_rcdataescaper": "html",
+ "html_template_urlescaper": "urlquery",
+ "html_template_urlnormalizer": "urlquery",
}
// escaper collects type inferences about templates and changes needed to make
case stateURL, stateCSSDqStr, stateCSSSqStr, stateCSSDqURL, stateCSSSqURL, stateCSSURL:
switch c.urlPart {
case urlPartNone:
- s = append(s, "exp_template_html_urlfilter")
+ s = append(s, "html_template_urlfilter")
fallthrough
case urlPartPreQuery:
switch c.state {
case stateCSSDqStr, stateCSSSqStr:
- s = append(s, "exp_template_html_cssescaper")
+ s = append(s, "html_template_cssescaper")
default:
- s = append(s, "exp_template_html_urlnormalizer")
+ s = append(s, "html_template_urlnormalizer")
}
case urlPartQueryOrFrag:
- s = append(s, "exp_template_html_urlescaper")
+ s = append(s, "html_template_urlescaper")
case urlPartUnknown:
return context{
state: stateError,
panic(c.urlPart.String())
}
case stateJS:
- s = append(s, "exp_template_html_jsvalescaper")
+ s = append(s, "html_template_jsvalescaper")
// A slash after a value starts a div operator.
c.jsCtx = jsCtxDivOp
case stateJSDqStr, stateJSSqStr:
- s = append(s, "exp_template_html_jsstrescaper")
+ s = append(s, "html_template_jsstrescaper")
case stateJSRegexp:
- s = append(s, "exp_template_html_jsregexpescaper")
+ s = append(s, "html_template_jsregexpescaper")
case stateCSS:
- s = append(s, "exp_template_html_cssvaluefilter")
+ s = append(s, "html_template_cssvaluefilter")
case stateText:
- s = append(s, "exp_template_html_htmlescaper")
+ s = append(s, "html_template_htmlescaper")
case stateRCDATA:
- s = append(s, "exp_template_html_rcdataescaper")
+ s = append(s, "html_template_rcdataescaper")
case stateAttr:
// Handled below in delim check.
case stateAttrName, stateTag:
c.state = stateAttrName
- s = append(s, "exp_template_html_htmlnamefilter")
+ s = append(s, "html_template_htmlnamefilter")
default:
if isComment(c.state) {
- s = append(s, "exp_template_html_commentescaper")
+ s = append(s, "html_template_commentescaper")
} else {
panic("unexpected state " + c.state.String())
}
case delimNone:
// No extra-escaping needed for raw text content.
case delimSpaceOrTagEnd:
- s = append(s, "exp_template_html_nospaceescaper")
+ s = append(s, "html_template_nospaceescaper")
default:
- s = append(s, "exp_template_html_attrescaper")
+ s = append(s, "html_template_attrescaper")
}
e.editActionNode(n, s)
return c
// redundantFuncs[a][b] implies that funcMap[b](funcMap[a](x)) == funcMap[a](x)
// for all x.
var redundantFuncs = map[string]map[string]bool{
- "exp_template_html_commentescaper": {
- "exp_template_html_attrescaper": true,
- "exp_template_html_nospaceescaper": true,
- "exp_template_html_htmlescaper": true,
+ "html_template_commentescaper": {
+ "html_template_attrescaper": true,
+ "html_template_nospaceescaper": true,
+ "html_template_htmlescaper": true,
},
- "exp_template_html_cssescaper": {
- "exp_template_html_attrescaper": true,
+ "html_template_cssescaper": {
+ "html_template_attrescaper": true,
},
- "exp_template_html_jsregexpescaper": {
- "exp_template_html_attrescaper": true,
+ "html_template_jsregexpescaper": {
+ "html_template_attrescaper": true,
},
- "exp_template_html_jsstrescaper": {
- "exp_template_html_attrescaper": true,
+ "html_template_jsstrescaper": {
+ "html_template_attrescaper": true,
},
- "exp_template_html_urlescaper": {
- "exp_template_html_urlnormalizer": true,
+ "html_template_urlescaper": {
+ "html_template_urlnormalizer": true,
},
}
dt := e.template(dname)
if dt == nil {
dt = template.New(dname)
- dt.Tree = &parse.Tree{Name: dname, Root: cloneList(t.Root)}
+ dt.Tree = &parse.Tree{Name: dname, Root: t.Root.CopyList()}
e.derived[dname] = dt
}
t = dt
// ExecuteTemplate applies the template associated with t that has the given
// name to the specified data object and writes the output to wr.
func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{}) error {
- tmpl, err := t.lookupAndEscapeTemplate(wr, name)
+ tmpl, err := t.lookupAndEscapeTemplate(name)
if err != nil {
return err
}
// lookupAndEscapeTemplate guarantees that the template with the given name
// is escaped, or returns an error if it cannot be. It returns the named
// template.
-func (t *Template) lookupAndEscapeTemplate(wr io.Writer, name string) (tmpl *Template, err error) {
+func (t *Template) lookupAndEscapeTemplate(name string) (tmpl *Template, err error) {
t.nameSpace.mu.Lock()
defer t.nameSpace.mu.Unlock()
tmpl = t.set[name]
return t, nil
}
-// AddParseTree is unimplemented.
-func (t *Template) AddParseTree(name string, tree *parse.Tree) error {
- return fmt.Errorf("html/template: AddParseTree unimplemented")
+// AddParseTree creates a new template with the name and parse tree
+// and associates it with t.
+//
+// It returns an error if t has already been executed.
+func (t *Template) AddParseTree(name string, tree *parse.Tree) (*Template, error) {
+ t.nameSpace.mu.Lock()
+ defer t.nameSpace.mu.Unlock()
+ if t.escaped {
+ return nil, fmt.Errorf("html/template: cannot AddParseTree to %q after it has executed", t.Name())
+ }
+ text, err := t.text.AddParseTree(name, tree)
+ if err != nil {
+ return nil, err
+ }
+ ret := &Template{
+ false,
+ text,
+ t.nameSpace,
+ }
+ t.set[name] = ret
+ return ret, nil
}
-// Clone is unimplemented.
-func (t *Template) Clone(name string) error {
- return fmt.Errorf("html/template: Clone unimplemented")
+// Clone returns a duplicate of the template, including all associated
+// templates. The actual representation is not copied, but the name space of
+// associated templates is, so further calls to Parse in the copy will add
+// templates to the copy but not to the original. Clone can be used to prepare
+// common templates and use them with variant definitions for other templates
+// by adding the variants after the clone is made.
+//
+// It returns an error if t has already been executed.
+func (t *Template) Clone() (*Template, error) {
+ t.nameSpace.mu.Lock()
+ defer t.nameSpace.mu.Unlock()
+ if t.escaped {
+ return nil, fmt.Errorf("html/template: cannot Clone %q after it has executed", t.Name())
+ }
+ textClone, err := t.text.Clone()
+ if err != nil {
+ return nil, err
+ }
+ ret := &Template{
+ false,
+ textClone,
+ &nameSpace{
+ set: make(map[string]*Template),
+ },
+ }
+ for _, x := range textClone.Templates() {
+ name := x.Name()
+ src := t.set[name]
+ if src == nil || src.escaped {
+ return nil, fmt.Errorf("html/template: cannot Clone %q after it has executed", t.Name())
+ }
+ x.Tree = &parse.Tree{
+ Name: x.Tree.Name,
+ Root: x.Tree.Root.CopyList(),
+ }
+ ret.set[name] = &Template{
+ false,
+ x,
+ ret.nameSpace,
+ }
+ }
+ return ret, nil
}
// New allocates a new HTML template with the given name.
--- /dev/null
+// 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 example demonstrates decoding a JPEG image and examining its pixels.
+package image_test
+
+import (
+ "fmt"
+ "image"
+ "log"
+ "os"
+
+ // Package image/jpeg is not used explicitly in the code below,
+ // but is imported for its initialization side-effect, which allows
+ // image.Decode to understand JPEG formatted images. Uncomment these
+ // two lines to also understand GIF and PNG images:
+ // _ "image/gif"
+ // _ "image/png"
+ _ "image/jpeg"
+)
+
+func Example() {
+ // Open the file.
+ file, err := os.Open("testdata/video-001.jpeg")
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer file.Close()
+
+ // Decode the image.
+ m, _, err := image.Decode(file)
+ if err != nil {
+ log.Fatal(err)
+ }
+ bounds := m.Bounds()
+
+ // Calculate a 16-bin histogram for m's red, green, blue and alpha components.
+ //
+ // An image's bounds do not necessarily start at (0, 0), so the two loops start
+ // at bounds.Min.Y and bounds.Min.X. Looping over Y first and X second is more
+ // likely to result in better memory access patterns than X first and Y second.
+ var histogram [16][4]int
+ for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
+ for x := bounds.Min.X; x < bounds.Max.X; x++ {
+ r, g, b, a := m.At(x, y).RGBA()
+ // A color's RGBA method returns values in the range [0, 65535].
+ // Shifting by 12 reduces this to the range [0, 15].
+ histogram[r>>12][0]++
+ histogram[g>>12][1]++
+ histogram[b>>12][2]++
+ histogram[a>>12][3]++
+ }
+ }
+
+ // Print the results.
+ fmt.Printf("%-14s %6s %6s %6s %6s\n", "bin", "red", "green", "blue", "alpha")
+ for i, x := range histogram {
+ fmt.Printf("0x%04x-0x%04x: %6d %6d %6d %6d\n", i<<12, (i+1)<<12-1, x[0], x[1], x[2], x[3])
+ }
+ // Output:
+ // bin red green blue alpha
+ // 0x0000-0x0fff: 471 819 7596 0
+ // 0x1000-0x1fff: 576 2892 726 0
+ // 0x2000-0x2fff: 1038 2330 943 0
+ // 0x3000-0x3fff: 883 2321 1014 0
+ // 0x4000-0x4fff: 501 1295 525 0
+ // 0x5000-0x5fff: 302 962 242 0
+ // 0x6000-0x6fff: 219 358 150 0
+ // 0x7000-0x7fff: 352 281 192 0
+ // 0x8000-0x8fff: 3688 216 246 0
+ // 0x9000-0x9fff: 2277 237 283 0
+ // 0xa000-0xafff: 971 254 357 0
+ // 0xb000-0xbfff: 317 306 429 0
+ // 0xc000-0xcfff: 203 402 401 0
+ // 0xd000-0xdfff: 256 394 241 0
+ // 0xe000-0xefff: 378 343 173 0
+ // 0xf000-0xffff: 3018 2040 1932 15450
+}
testYCbCr(t, r, subsampleRatio, delta)
}
}
+ if testing.Short() {
+ break
+ }
}
}
for i := 0; i < 10000; i++ {
name := filepath.Join(dir, prefix+nextSuffix())
f, err = os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600)
- if pe, ok := err.(*os.PathError); ok && pe.Err == os.EEXIST {
+ if os.IsExist(err) {
if nconflict++; nconflict > 10 {
rand = reseed()
}
for i := 0; i < 10000; i++ {
try := filepath.Join(dir, prefix+nextSuffix())
err = os.Mkdir(try, 0700)
- if pe, ok := err.(*os.PathError); ok && pe.Err == os.EEXIST {
+ if os.IsExist(err) {
if nconflict++; nconflict > 10 {
rand = reseed()
}
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build !windows,!plan9
+
// Package syslog provides a simple interface to the system log service. It
// can send messages to the syslog daemon using UNIX domain sockets, UDP, or
// TCP connections.
// Copyright 2009 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.
+
+// +build !windows,!plan9
+
package syslog
import (
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build !windows,!plan9
+
package syslog
import (
t.Errorf("failed at %d ** %d in base %d: %s != %s", b, p, b, xs, xs2)
}
}
+ if b >= 3 && testing.Short() {
+ break
+ }
}
}
}
// Int63n returns, as an int64, a non-negative pseudo-random number in [0,n).
+// It panics if n <= 0.
func (r *Rand) Int63n(n int64) int64 {
if n <= 0 {
- return 0
+ panic("invalid argument to Int63n")
}
max := int64((1 << 63) - 1 - (1<<63)%uint64(n))
v := r.Int63()
}
// Int31n returns, as an int32, a non-negative pseudo-random number in [0,n).
+// It panics if n <= 0.
func (r *Rand) Int31n(n int32) int32 {
if n <= 0 {
- return 0
+ panic("invalid argument to Int31n")
}
max := int32((1 << 31) - 1 - (1<<31)%uint32(n))
v := r.Int31()
}
// Intn returns, as an int, a non-negative pseudo-random number in [0,n).
+// It panics if n <= 0.
func (r *Rand) Intn(n int) int {
+ if n <= 0 {
+ panic("invalid argument to Intn")
+ }
if n <= 1<<31-1 {
return int(r.Int31n(int32(n)))
}
func Int() int { return globalRand.Int() }
// Int63n returns, as an int64, a non-negative pseudo-random number in [0,n).
+// It panics if n <= 0.
func Int63n(n int64) int64 { return globalRand.Int63n(n) }
// Int31n returns, as an int32, a non-negative pseudo-random number in [0,n).
+// It panics if n <= 0.
func Int31n(n int32) int32 { return globalRand.Int31n(n) }
// Intn returns, as an int, a non-negative pseudo-random number in [0,n).
+// It panics if n <= 0.
func Intn(n int) int { return globalRand.Intn(n) }
// Float64 returns, as a float64, a pseudo-random number in [0.0,1.0).
for m := 0.5; m < mmax; m *= 2 {
for _, seed := range testSeeds {
testNormalDistribution(t, numTestSamples, m, sd, seed)
+ if testing.Short() {
+ break
+ }
}
}
}
for rate := 0.05; rate < 10; rate *= 2 {
for _, seed := range testSeeds {
testExponentialDistribution(t, numTestSamples, rate, seed)
+ if testing.Short() {
+ break
+ }
}
}
}
)
// If an IPv6 tunnel is running, we can try dialing a real IPv6 address.
-var ipv6 = flag.Bool("ipv6", false, "assume ipv6 tunnel is present")
+var testIPv6 = flag.Bool("ipv6", false, "assume ipv6 tunnel is present")
// fd is already connected to the destination, port 80.
// Run an HTTP request to fetch the appropriate page.
return
}
// Only run tcp6 if the kernel will take it.
- if !*ipv6 || !supportsIPv6 {
+ if !*testIPv6 || !supportsIPv6 {
return
}
} else {
netfd := s.LookupFD(fd, mode)
if netfd == nil {
- print("pollServer: unexpected wakeup for fd=", fd, " mode=", string(mode), "\n")
+ // This can happen because the WaitFD runs without
+ // holding s's lock, so there might be a pending wakeup
+ // for an fd that has been evicted. No harm done.
continue
}
s.WakeFD(netfd, mode, nil)
}
defer fd.decref()
if fd.sysfile == nil {
- return 0, os.EINVAL
+ return 0, syscall.EINVAL
}
var err error
func (fd *netFD) shutdown(how int) error {
if fd == nil || fd.sysfd == syscall.InvalidHandle {
- return os.EINVAL
+ return syscall.EINVAL
}
err := syscall.Shutdown(fd.sysfd, how)
if err != nil {
func (fd *netFD) Read(buf []byte) (int, error) {
if fd == nil {
- return 0, os.EINVAL
+ return 0, syscall.EINVAL
}
fd.rio.Lock()
defer fd.rio.Unlock()
}
defer fd.decref()
if fd.sysfd == syscall.InvalidHandle {
- return 0, os.EINVAL
+ return 0, syscall.EINVAL
}
var o readOp
o.Init(fd, buf, 'r')
func (fd *netFD) ReadFrom(buf []byte) (n int, sa syscall.Sockaddr, err error) {
if fd == nil {
- return 0, nil, os.EINVAL
+ return 0, nil, syscall.EINVAL
}
if len(buf) == 0 {
return 0, nil, nil
func (fd *netFD) Write(buf []byte) (int, error) {
if fd == nil {
- return 0, os.EINVAL
+ return 0, syscall.EINVAL
}
fd.wio.Lock()
defer fd.wio.Unlock()
func (fd *netFD) WriteTo(buf []byte, sa syscall.Sockaddr) (int, error) {
if fd == nil {
- return 0, os.EINVAL
+ return 0, syscall.EINVAL
}
if len(buf) == 0 {
return 0, nil
}
defer fd.decref()
if fd.sysfd == syscall.InvalidHandle {
- return 0, os.EINVAL
+ return 0, syscall.EINVAL
}
var o writeToOp
o.Init(fd, buf, 'w')
return nil, os.NewSyscallError("dup", syscall.EWINDOWS)
}
+var errNoSupport = errors.New("address family not supported")
+
func (fd *netFD) ReadMsg(p []byte, oob []byte) (n, oobn, flags int, sa syscall.Sockaddr, err error) {
- return 0, 0, 0, nil, os.EAFNOSUPPORT
+ return 0, 0, 0, nil, errNoSupport
}
func (fd *netFD) WriteMsg(p []byte, oob []byte, sa syscall.Sockaddr) (n int, oobn int, err error) {
- return 0, 0, os.EAFNOSUPPORT
+ return 0, 0, errNoSupport
}
switch sa.(type) {
default:
closesocket(fd)
- return nil, os.EINVAL
+ return nil, syscall.EINVAL
case *syscall.SockaddrInet4:
family = syscall.AF_INET
if proto == syscall.SOCK_DGRAM {
return newIPConn(fd), nil
}
fd.Close()
- return nil, os.EINVAL
+ return nil, syscall.EINVAL
}
// FileListener returns a copy of the network listener corresponding
return &UnixListener{fd, laddr.Name}, nil
}
fd.Close()
- return nil, os.EINVAL
+ return nil, syscall.EINVAL
}
// FilePacketConn returns a copy of the packet network connection
return newUnixConn(fd), nil
}
fd.Close()
- return nil, os.EINVAL
+ return nil, syscall.EINVAL
}
import (
"os"
+ "syscall"
)
// FileConn returns a copy of the network connection corresponding to
// finished. Closing c does not affect f, and closing f does not
// affect c.
func FileConn(f *os.File) (c Conn, err error) {
- return nil, os.EPLAN9
+ return nil, syscall.EPLAN9
}
// FileListener returns a copy of the network listener corresponding
// when finished. Closing c does not affect l, and closing l does not
// affect c.
func FileListener(f *os.File) (l Listener, err error) {
- return nil, os.EPLAN9
+ return nil, syscall.EPLAN9
}
// FilePacketConn returns a copy of the packet network connection
// responsibility to close f when finished. Closing c does not affect
// f, and closing f does not affect c.
func FilePacketConn(f *os.File) (c PacketConn, err error) {
- return nil, os.EPLAN9
+ return nil, syscall.EPLAN9
}
func TestLookupStaticHost(t *testing.T) {
p := hostsPath
- hostsPath = "hosts_testdata"
+ hostsPath = "testdata/hosts"
for i := 0; i < len(hosttests); i++ {
tt := hosttests[i]
ips := lookupStaticHost(tt.host)
Raw: "NID=99=YsDT5i3E-CXax-; expires=Wed, 23-Nov-2011 01:05:03 GMT; path=/; domain=.google.ch; HttpOnly",
}},
},
+ {
+ Header{"Set-Cookie": {".ASPXAUTH=7E3AA; expires=Wed, 07-Mar-2012 14:25:06 GMT; path=/; HttpOnly"}},
+ []*Cookie{{
+ Name: ".ASPXAUTH",
+ Value: "7E3AA",
+ Path: "/",
+ Expires: time.Date(2012, 3, 7, 14, 25, 6, 0, time.UTC),
+ RawExpires: "Wed, 07-Mar-2012 14:25:06 GMT",
+ HttpOnly: true,
+ Raw: ".ASPXAUTH=7E3AA; expires=Wed, 07-Mar-2012 14:25:06 GMT; path=/; HttpOnly",
+ }},
+ },
+ {
+ Header{"Set-Cookie": {"ASP.NET_SessionId=foo; path=/; HttpOnly"}},
+ []*Cookie{{
+ Name: "ASP.NET_SessionId",
+ Value: "foo",
+ Path: "/",
+ HttpOnly: true,
+ Raw: "ASP.NET_SessionId=foo; path=/; HttpOnly",
+ }},
+ },
+
+ // TODO(bradfitz): users have reported seeing this in the
+ // wild, but do browsers handle it? RFC 6265 just says "don't
+ // do that" (section 3) and then never mentions header folding
+ // again.
+ // Header{"Set-Cookie": {"ASP.NET_SessionId=foo; path=/; HttpOnly, .ASPXAUTH=7E3AA; expires=Wed, 07-Mar-2012 14:25:06 GMT; path=/; HttpOnly"}},
}
func toJSON(v interface{}) string {
--- /dev/null
+// 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.
+
+package http_test
+
+import (
+ "fmt"
+ "io/ioutil"
+ "log"
+ "net/http"
+)
+
+func ExampleHijacker() {
+ http.HandleFunc("/hijack", func(w http.ResponseWriter, r *http.Request) {
+ hj, ok := w.(http.Hijacker)
+ if !ok {
+ http.Error(w, "webserver doesn't support hijacking", http.StatusInternalServerError)
+ return
+ }
+ conn, bufrw, err := hj.Hijack()
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ // Don't forget to close the connection:
+ defer conn.Close()
+ bufrw.WriteString("Now we're speaking raw TCP. Say hi: ")
+ bufrw.Flush()
+ s, err := bufrw.ReadString('\n')
+ if err != nil {
+ log.Printf("error reading string: %v", err)
+ return
+ }
+ fmt.Fprintf(bufrw, "You said: %q\nBye.\n", s)
+ bufrw.Flush()
+ })
+}
+
+func ExampleGet() {
+ res, err := http.Get("http://www.google.com/robots.txt")
+ if err != nil {
+ log.Fatal(err)
+ }
+ robots, err := ioutil.ReadAll(res.Body)
+ if err != nil {
+ log.Fatal(err)
+ }
+ res.Body.Close()
+ fmt.Printf("%s", robots)
+}
import (
"bytes"
+ "errors"
"fmt"
"io"
"io/ioutil"
ch := make(chan string, 1)
fs := FileServer(&testFileSystem{func(name string) (File, error) {
ch <- name
- return nil, os.ENOENT
+ return nil, errors.New("file does not exist")
}})
tests := []struct {
reqPath, openArg string
return
}
- _, err = Get(fmt.Sprintf("http://%s/", ln.Addr()))
+ res, err := Get(fmt.Sprintf("http://%s/", ln.Addr()))
if err != nil {
- t.Errorf("http client error: %v", err)
- return
+ t.Fatalf("http client error: %v", err)
+ }
+ _, err = io.Copy(ioutil.Discard, res.Body)
+ if err != nil {
+ t.Fatalf("client body read error: %v", err)
}
+ res.Body.Close()
// Force child to exit cleanly.
Get(fmt.Sprintf("http://%s/quit", ln.Addr()))
"net"
"net/http"
"net/textproto"
- "os"
"sync"
)
var (
ErrPersistEOF = &http.ProtocolError{ErrorString: "persistent connection closed"}
+ ErrClosed = &http.ProtocolError{ErrorString: "connection closed by user"}
ErrPipeline = &http.ProtocolError{ErrorString: "pipeline error"}
)
}
if sc.c == nil { // connection closed by user in the meantime
defer sc.lk.Unlock()
- return os.EBADF
+ return ErrClosed
}
c := sc.c
if sc.nread <= sc.nwritten {
//
// go tool pprof http://localhost:6060/debug/pprof/profile
//
-// Or to look at the thread creation profile:
+// Or to view all available profiles:
//
-// go tool pprof http://localhost:6060/debug/pprof/thread
+// go tool pprof http://localhost:6060/debug/pprof/
//
// For a study of the facility in action, visit
//
"bufio"
"bytes"
"fmt"
+ "html/template"
"io"
+ "log"
"net/http"
"os"
"runtime"
)
func init() {
+ http.Handle("/debug/pprof/", http.HandlerFunc(Index))
http.Handle("/debug/pprof/cmdline", http.HandlerFunc(Cmdline))
http.Handle("/debug/pprof/profile", http.HandlerFunc(Profile))
- http.Handle("/debug/pprof/heap", http.HandlerFunc(Heap))
http.Handle("/debug/pprof/symbol", http.HandlerFunc(Symbol))
- http.Handle("/debug/pprof/thread", http.HandlerFunc(Thread))
}
// Cmdline responds with the running program's
fmt.Fprintf(w, strings.Join(os.Args, "\x00"))
}
-// Heap responds with the pprof-formatted heap profile.
-// The package initialization registers it as /debug/pprof/heap.
-func Heap(w http.ResponseWriter, r *http.Request) {
- w.Header().Set("Content-Type", "text/plain; charset=utf-8")
- pprof.WriteHeapProfile(w)
-}
-
-// Thread responds with the pprof-formatted thread creation profile.
-// The package initialization registers it as /debug/pprof/thread.
-func Thread(w http.ResponseWriter, r *http.Request) {
- w.Header().Set("Content-Type", "text/plain; charset=utf-8")
- pprof.WriteThreadProfile(w)
-}
-
// Profile responds with the pprof-formatted cpu profile.
// The package initialization registers it as /debug/pprof/profile.
func Profile(w http.ResponseWriter, r *http.Request) {
w.Write(buf.Bytes())
}
+
+// Handler returns an HTTP handler that serves the named profile.
+func Handler(name string) http.Handler {
+ return handler(name)
+}
+
+type handler string
+
+func (name handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "text/plain; charset=utf-8")
+ debug, _ := strconv.Atoi(r.FormValue("debug"))
+ p := pprof.Lookup(string(name))
+ if p == nil {
+ w.WriteHeader(404)
+ fmt.Fprintf(w, "Unknown profile: %s\n", name)
+ return
+ }
+ p.WriteTo(w, debug)
+ return
+}
+
+// Index responds with the pprof-formatted profile named by the request.
+// For example, "/debug/pprof/heap" serves the "heap" profile.
+// Index responds to a request for "/debug/pprof/" with an HTML page
+// listing the available profiles.
+func Index(w http.ResponseWriter, r *http.Request) {
+ if strings.HasPrefix(r.URL.Path, "/debug/pprof/") {
+ name := r.URL.Path[len("/debug/pprof/"):]
+ if name != "" {
+ handler(name).ServeHTTP(w, r)
+ return
+ }
+ }
+
+ profiles := pprof.Profiles()
+ if err := indexTmpl.Execute(w, profiles); err != nil {
+ log.Print(err)
+ }
+}
+
+var indexTmpl = template.Must(template.New("index").Parse(`<html>
+<head>
+<title>/debug/pprof/</title>
+</head>
+/debug/pprof/<br>
+<br>
+<body>
+profiles:<br>
+<table>
+{{range .}}
+<tr><td align=right>{{.Count}}<td><a href="/debug/pprof/{{.Name}}?debug=1">{{.Name}}</a>
+{{end}}
+</table>
+<br>
+<a href="/debug/pprof/goroutine?debug=2">full goroutine stack dump</a><br>
+</body>
+</html>
+`))
return readCookies(r.Header, "")
}
-var ErrNoCookie = errors.New("http: named cookied not present")
+var ErrNoCookie = errors.New("http: named cookie not present")
// Cookie returns the named cookie provided in the request or
// ErrNoCookie if not found.
rawurl = "http://" + rawurl
}
- if req.URL, err = url.ParseRequest(rawurl); err != nil {
+ if req.URL, err = url.ParseRequestURI(rawurl); err != nil {
return nil, err
}
fmt.Fprintf(res, "req=%d", reqNum)
})
- const second = 1000000000 /* nanos */
- server := &Server{Handler: handler, ReadTimeout: 0.25 * second, WriteTimeout: 0.25 * second}
+ server := &Server{Handler: handler, ReadTimeout: 250 * time.Millisecond, WriteTimeout: 250 * time.Millisecond}
go server.Serve(l)
url := fmt.Sprintf("http://%s/", addr)
if n != 0 || err != io.EOF {
t.Errorf("Read = %v, %v, wanted %v, %v", n, err, 0, io.EOF)
}
- if latency < 200*time.Millisecond /* fudge from 0.25 above */ {
+ if latency < 200*time.Millisecond /* fudge from 250 ms above */ {
t.Errorf("got EOF after %s, want >= %s", latency, 200*time.Millisecond)
}
import (
"bufio"
"bytes"
- "crypto/rand"
"crypto/tls"
"errors"
"fmt"
ReadTimeout time.Duration // maximum duration before timing out read of the request
WriteTimeout time.Duration // maximum duration before timing out write of the response
MaxHeaderBytes int // maximum size of request headers, DefaultMaxHeaderBytes if 0
+ TLSConfig *tls.Config // optional TLS config, used by ListenAndServeTLS
}
// ListenAndServe listens on the TCP network address srv.Addr and then
if addr == "" {
addr = ":https"
}
- config := &tls.Config{
- Rand: rand.Reader,
- NextProtos: []string{"http/1.1"},
+ config := &tls.Config{}
+ if srv.TLSConfig != nil {
+ *config = *srv.TLSConfig
+ }
+ if config.NextProtos == nil {
+ config.NextProtos = []string{"http/1.1"}
}
var err error
tr := &Transport{}
c := &Client{Transport: tr}
- n0 := runtime.Goroutines()
+ n0 := runtime.NumGoroutine()
const numReq = 25
didReqCh := make(chan bool)
<-gotReqCh
}
- nhigh := runtime.Goroutines()
+ nhigh := runtime.NumGoroutine()
// Tell all handlers to unblock and reply.
for i := 0; i < numReq; i++ {
time.Sleep(100 * time.Millisecond)
runtime.GC()
runtime.GC() // even more.
- nfinal := runtime.Goroutines()
+ nfinal := runtime.NumGoroutine()
growth := nfinal - n0
return nil, err
}
}
- ifmat4 := parseProcNetIGMP(ifi)
- ifmat6 := parseProcNetIGMP6(ifi)
+ ifmat4 := parseProcNetIGMP("/proc/net/igmp", ifi)
+ ifmat6 := parseProcNetIGMP6("/proc/net/igmp6", ifi)
return append(ifmat4, ifmat6...), nil
}
-func parseProcNetIGMP(ifi *Interface) []Addr {
- fd, err := open("/proc/net/igmp")
+func parseProcNetIGMP(path string, ifi *Interface) []Addr {
+ fd, err := open(path)
if err != nil {
return nil
}
fd.readLine() // skip first line
b := make([]byte, IPv4len)
for l, ok := fd.readLine(); ok; l, ok = fd.readLine() {
- f := getFields(l)
- switch len(f) {
- case 4:
+ f := splitAtBytes(l, " :\r\t\n")
+ if len(f) < 4 {
+ continue
+ }
+ switch {
+ case l[0] != ' ' && l[0] != '\t': // new interface line
+ name = f[1]
+ case len(f[0]) == 8:
if ifi == nil || name == ifi.Name {
fmt.Sscanf(f[0], "%08x", &b)
ifma := IPAddr{IP: IPv4(b[3], b[2], b[1], b[0])}
ifmat = append(ifmat, ifma.toAddr())
}
- case 5:
- name = f[1]
}
}
return ifmat
}
-func parseProcNetIGMP6(ifi *Interface) []Addr {
- fd, err := open("/proc/net/igmp6")
+func parseProcNetIGMP6(path string, ifi *Interface) []Addr {
+ fd, err := open(path)
if err != nil {
return nil
}
var ifmat []Addr
b := make([]byte, IPv6len)
for l, ok := fd.readLine(); ok; l, ok = fd.readLine() {
- f := getFields(l)
+ f := splitAtBytes(l, " \r\t\n")
+ if len(f) < 6 {
+ continue
+ }
if ifi == nil || f[1] == ifi.Name {
fmt.Sscanf(f[2], "%32x", &b)
ifma := IPAddr{IP: IP{b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15]}}
for _, ifi := range ift {
ifxi, err := InterfaceByIndex(ifi.Index)
if err != nil {
- t.Fatalf("InterfaceByIndex(%#q) failed: %v", ifi.Index, err)
+ t.Fatalf("InterfaceByIndex(%q) failed: %v", ifi.Index, err)
}
if !sameInterface(ifxi, &ifi) {
- t.Fatalf("InterfaceByIndex(%#q) = %v, want %v", ifi.Index, *ifxi, ifi)
+ t.Fatalf("InterfaceByIndex(%q) = %v, want %v", ifi.Index, *ifxi, ifi)
}
ifxn, err := InterfaceByName(ifi.Name)
if err != nil {
- t.Fatalf("InterfaceByName(%#q) failed: %v", ifi.Name, err)
+ t.Fatalf("InterfaceByName(%q) failed: %v", ifi.Name, err)
}
if !sameInterface(ifxn, &ifi) {
- t.Fatalf("InterfaceByName(%#q) = %v, want %v", ifi.Name, *ifxn, ifi)
+ t.Fatalf("InterfaceByName(%q) = %v, want %v", ifi.Name, *ifxn, ifi)
}
t.Logf("%q: flags %q, ifindex %v, mtu %v\n", ifi.Name, ifi.Flags.String(), ifi.Index, ifi.MTU)
t.Logf("\thardware address %q", ifi.HardwareAddr.String())
import (
"bytes"
"os"
+ "syscall"
"testing"
"time"
)
net string
laddr string
raddr string
- ipv6 bool
+ ipv6 bool // test with underlying AF_INET6 socket
}{
{"ip4:icmp", "", "127.0.0.1", false},
{"ip6:icmp", "", "::1", true},
}
id := os.Getpid() & 0xffff
seqnum++
- echo := newICMPEchoRequest(tt.ipv6, id, seqnum, 128, []byte("Go Go Gadget Ping!!!"))
- exchangeICMPEcho(t, tt.net, tt.laddr, tt.raddr, tt.ipv6, echo)
+ echo := newICMPEchoRequest(tt.net, id, seqnum, 128, []byte("Go Go Gadget Ping!!!"))
+ exchangeICMPEcho(t, tt.net, tt.laddr, tt.raddr, echo)
}
}
-func exchangeICMPEcho(t *testing.T, net, laddr, raddr string, ipv6 bool, echo []byte) {
+func exchangeICMPEcho(t *testing.T, net, laddr, raddr string, echo []byte) {
c, err := ListenPacket(net, laddr)
if err != nil {
- t.Errorf("ListenPacket(%#q, %#q) failed: %v", net, laddr, err)
+ t.Errorf("ListenPacket(%q, %q) failed: %v", net, laddr, err)
return
}
c.SetDeadline(time.Now().Add(100 * time.Millisecond))
ra, err := ResolveIPAddr(net, raddr)
if err != nil {
- t.Errorf("ResolveIPAddr(%#q, %#q) failed: %v", net, raddr, err)
+ t.Errorf("ResolveIPAddr(%q, %q) failed: %v", net, raddr, err)
return
}
waitForReady := make(chan bool)
- go icmpEchoTransponder(t, net, raddr, ipv6, waitForReady)
+ go icmpEchoTransponder(t, net, raddr, waitForReady)
<-waitForReady
_, err = c.WriteTo(echo, ra)
t.Errorf("ReadFrom failed: %v", err)
return
}
- if !ipv6 && reply[0] != ICMP4_ECHO_REPLY {
- continue
- }
- if ipv6 && reply[0] != ICMP6_ECHO_REPLY {
- continue
+ switch c.(*IPConn).fd.family {
+ case syscall.AF_INET:
+ if reply[0] != ICMP4_ECHO_REPLY {
+ continue
+ }
+ case syscall.AF_INET6:
+ if reply[0] != ICMP6_ECHO_REPLY {
+ continue
+ }
}
xid, xseqnum := parseICMPEchoReply(echo)
rid, rseqnum := parseICMPEchoReply(reply)
}
}
-func icmpEchoTransponder(t *testing.T, net, raddr string, ipv6 bool, waitForReady chan bool) {
+func icmpEchoTransponder(t *testing.T, net, raddr string, waitForReady chan bool) {
c, err := Dial(net, raddr)
if err != nil {
waitForReady <- true
- t.Errorf("Dial(%#q, %#q) failed: %v", net, raddr, err)
+ t.Errorf("Dial(%q, %q) failed: %v", net, raddr, err)
return
}
c.SetDeadline(time.Now().Add(100 * time.Millisecond))
t.Errorf("Read failed: %v", err)
return
}
- if !ipv6 && echo[0] != ICMP4_ECHO_REQUEST {
- continue
- }
- if ipv6 && echo[0] != ICMP6_ECHO_REQUEST {
- continue
+ switch c.(*IPConn).fd.family {
+ case syscall.AF_INET:
+ if echo[0] != ICMP4_ECHO_REQUEST {
+ continue
+ }
+ case syscall.AF_INET6:
+ if echo[0] != ICMP6_ECHO_REQUEST {
+ continue
+ }
}
break
}
- if !ipv6 {
+ switch c.(*IPConn).fd.family {
+ case syscall.AF_INET:
echo[0] = ICMP4_ECHO_REPLY
- } else {
+ case syscall.AF_INET6:
echo[0] = ICMP6_ECHO_REPLY
}
ICMP6_ECHO_REPLY = 129
)
-func newICMPEchoRequest(ipv6 bool, id, seqnum, msglen int, filler []byte) []byte {
- if !ipv6 {
+func newICMPEchoRequest(net string, id, seqnum, msglen int, filler []byte) []byte {
+ afnet, _, _ := parseDialNetwork(net)
+ switch afnet {
+ case "ip4":
return newICMPv4EchoRequest(id, seqnum, msglen, filler)
+ case "ip6":
+ return newICMPv6EchoRequest(id, seqnum, msglen, filler)
}
- return newICMPv6EchoRequest(id, seqnum, msglen, filler)
+ return nil
}
func newICMPv4EchoRequest(id, seqnum, msglen int, filler []byte) []byte {
package net
import (
- "os"
+ "syscall"
"time"
)
// SetDeadline implements the Conn SetDeadline method.
func (c *IPConn) SetDeadline(t time.Time) error {
- return os.EPLAN9
+ return syscall.EPLAN9
}
// SetReadDeadline implements the Conn SetReadDeadline method.
func (c *IPConn) SetReadDeadline(t time.Time) error {
- return os.EPLAN9
+ return syscall.EPLAN9
}
// SetWriteDeadline implements the Conn SetWriteDeadline method.
func (c *IPConn) SetWriteDeadline(t time.Time) error {
- return os.EPLAN9
+ return syscall.EPLAN9
}
// Implementation of the Conn interface - see Conn for documentation.
// Read implements the Conn Read method.
func (c *IPConn) Read(b []byte) (int, error) {
- return 0, os.EPLAN9
+ return 0, syscall.EPLAN9
}
// Write implements the Conn Write method.
func (c *IPConn) Write(b []byte) (int, error) {
- return 0, os.EPLAN9
+ return 0, syscall.EPLAN9
}
// Close closes the IP connection.
func (c *IPConn) Close() error {
- return os.EPLAN9
+ return syscall.EPLAN9
}
// LocalAddr returns the local network address.
// Timeout() == true after a fixed time limit; see SetDeadline and
// SetReadDeadline.
func (c *IPConn) ReadFromIP(b []byte) (int, *IPAddr, error) {
- return 0, nil, os.EPLAN9
+ return 0, nil, syscall.EPLAN9
}
// ReadFrom implements the PacketConn ReadFrom method.
func (c *IPConn) ReadFrom(b []byte) (int, Addr, error) {
- return 0, nil, os.EPLAN9
+ return 0, nil, syscall.EPLAN9
}
// WriteToIP writes a IP packet to addr via c, copying the payload from b.
// see SetDeadline and SetWriteDeadline.
// On packet-oriented connections, write timeouts are rare.
func (c *IPConn) WriteToIP(b []byte, addr *IPAddr) (int, error) {
- return 0, os.EPLAN9
+ return 0, syscall.EPLAN9
}
// WriteTo implements the PacketConn WriteTo method.
func (c *IPConn) WriteTo(b []byte, addr Addr) (int, error) {
- return 0, os.EPLAN9
+ return 0, syscall.EPLAN9
}
// DialIP connects to the remote address raddr on the network protocol netProto,
// which must be "ip", "ip4", or "ip6" followed by a colon and a protocol number or name.
func DialIP(netProto string, laddr, raddr *IPAddr) (*IPConn, error) {
- return nil, os.EPLAN9
+ return nil, syscall.EPLAN9
}
// ListenIP listens for incoming IP packets addressed to the
// and WriteTo methods can be used to receive and send IP
// packets with per-packet addressing.
func ListenIP(netProto string, laddr *IPAddr) (*IPConn, error) {
- return nil, os.EPLAN9
+ return nil, syscall.EPLAN9
}
// Write implements the Conn Write method.
func (c *IPConn) Write(b []byte) (int, error) {
if !c.ok() {
- return 0, os.EINVAL
+ return 0, syscall.EINVAL
}
return c.fd.Write(b)
}
// Close closes the IP connection.
func (c *IPConn) Close() error {
if !c.ok() {
- return os.EINVAL
+ return syscall.EINVAL
}
err := c.fd.Close()
c.fd = nil
// SetDeadline implements the Conn SetDeadline method.
func (c *IPConn) SetDeadline(t time.Time) error {
if !c.ok() {
- return os.EINVAL
+ return syscall.EINVAL
}
return setDeadline(c.fd, t)
}
// SetReadDeadline implements the Conn SetReadDeadline method.
func (c *IPConn) SetReadDeadline(t time.Time) error {
if !c.ok() {
- return os.EINVAL
+ return syscall.EINVAL
}
return setReadDeadline(c.fd, t)
}
// SetWriteDeadline implements the Conn SetWriteDeadline method.
func (c *IPConn) SetWriteDeadline(t time.Time) error {
if !c.ok() {
- return os.EINVAL
+ return syscall.EINVAL
}
return setWriteDeadline(c.fd, t)
}
// receive buffer associated with the connection.
func (c *IPConn) SetReadBuffer(bytes int) error {
if !c.ok() {
- return os.EINVAL
+ return syscall.EINVAL
}
return setReadBuffer(c.fd, bytes)
}
// transmit buffer associated with the connection.
func (c *IPConn) SetWriteBuffer(bytes int) error {
if !c.ok() {
- return os.EINVAL
+ return syscall.EINVAL
}
return setWriteBuffer(c.fd, bytes)
}
// SetReadDeadline.
func (c *IPConn) ReadFromIP(b []byte) (int, *IPAddr, error) {
if !c.ok() {
- return 0, nil, os.EINVAL
+ return 0, nil, syscall.EINVAL
}
// TODO(cw,rsc): consider using readv if we know the family
// type to avoid the header trim/copy
// ReadFrom implements the PacketConn ReadFrom method.
func (c *IPConn) ReadFrom(b []byte) (int, Addr, error) {
if !c.ok() {
- return 0, nil, os.EINVAL
+ return 0, nil, syscall.EINVAL
}
n, uaddr, err := c.ReadFromIP(b)
return n, uaddr.toAddr(), err
// On packet-oriented connections, write timeouts are rare.
func (c *IPConn) WriteToIP(b []byte, addr *IPAddr) (int, error) {
if !c.ok() {
- return 0, os.EINVAL
+ return 0, syscall.EINVAL
}
sa, err := addr.sockaddr(c.fd.family)
if err != nil {
// WriteTo implements the PacketConn WriteTo method.
func (c *IPConn) WriteTo(b []byte, addr Addr) (int, error) {
if !c.ok() {
- return 0, os.EINVAL
+ return 0, syscall.EINVAL
}
a, ok := addr.(*IPAddr)
if !ok {
- return 0, &OpError{"write", c.fd.net, addr, os.EINVAL}
+ return 0, &OpError{"write", c.fd.net, addr, syscall.EINVAL}
}
return c.WriteToIP(b, a)
}
"errors"
"io"
"os"
+ "syscall"
"time"
)
// Read implements the Conn Read method.
func (c *plan9Conn) Read(b []byte) (n int, err error) {
if !c.ok() {
- return 0, os.EINVAL
+ return 0, syscall.EINVAL
}
if c.data == nil {
c.data, err = os.OpenFile(c.dir+"/data", os.O_RDWR, 0)
// Write implements the Conn Write method.
func (c *plan9Conn) Write(b []byte) (n int, err error) {
if !c.ok() {
- return 0, os.EINVAL
+ return 0, syscall.EINVAL
}
if c.data == nil {
c.data, err = os.OpenFile(c.dir+"/data", os.O_RDWR, 0)
// Close closes the connection.
func (c *plan9Conn) Close() error {
if !c.ok() {
- return os.EINVAL
+ return syscall.EINVAL
}
err := c.ctl.Close()
if err != nil {
// SetDeadline implements the Conn SetDeadline method.
func (c *plan9Conn) SetDeadline(t time.Time) error {
- return os.EPLAN9
+ return syscall.EPLAN9
}
// SetReadDeadline implements the Conn SetReadDeadline method.
func (c *plan9Conn) SetReadDeadline(t time.Time) error {
- return os.EPLAN9
+ return syscall.EPLAN9
}
// SetWriteDeadline implements the Conn SetWriteDeadline method.
func (c *plan9Conn) SetWriteDeadline(t time.Time) error {
- return os.EPLAN9
+ return syscall.EPLAN9
}
func startPlan9(net string, addr Addr) (ctl *os.File, dest, proto, name string, err error) {
func (l *plan9Listener) Close() error {
if l == nil || l.ctl == nil {
- return os.EINVAL
+ return syscall.EINVAL
}
return l.ctl.Close()
}
}
func internetSocket(net string, laddr, raddr sockaddr, sotype, proto int, mode string, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err error) {
- var oserr error
var la, ra syscall.Sockaddr
family := favoriteAddrFamily(net, laddr, raddr, mode)
if laddr != nil {
- if la, oserr = laddr.sockaddr(family); oserr != nil {
+ if la, err = laddr.sockaddr(family); err != nil {
goto Error
}
}
if raddr != nil {
- if ra, oserr = raddr.sockaddr(family); oserr != nil {
+ if ra, err = raddr.sockaddr(family); err != nil {
goto Error
}
}
- fd, oserr = socket(net, family, sotype, proto, la, ra, toAddr)
- if oserr != nil {
+ fd, err = socket(net, family, sotype, proto, la, ra, toAddr)
+ if err != nil {
goto Error
}
return fd, nil
if mode == "listen" {
addr = laddr
}
- return nil, &OpError{mode, net, addr, oserr}
+ return nil, &OpError{mode, net, addr, err}
}
func ipToSockaddr(family int, ip IP, port int) (syscall.Sockaddr, error) {
import (
"errors"
"os"
+ "syscall"
)
func query(filename, query string, bufSize int) (res []string, err error) {
func lookupProtocol(name string) (proto int, err error) {
// TODO: Implement this
- return 0, os.EPLAN9
+ return 0, syscall.EPLAN9
}
func lookupHost(host string) (addrs []string, err error) {
package net
import (
- "runtime"
+ "flag"
"testing"
)
-var avoidMacFirewall = runtime.GOOS == "darwin"
+var testExternal = flag.Bool("external", false, "allow use of external networks during test")
func TestGoogleSRV(t *testing.T) {
- if testing.Short() || avoidMacFirewall {
+ if testing.Short() || !*testExternal {
t.Logf("skipping test to avoid external network")
return
}
}
func TestGmailMX(t *testing.T) {
- if testing.Short() || avoidMacFirewall {
+ if testing.Short() || !*testExternal {
t.Logf("skipping test to avoid external network")
return
}
}
func TestGmailTXT(t *testing.T) {
- if testing.Short() || avoidMacFirewall {
+ if testing.Short() || !*testExternal {
t.Logf("skipping test to avoid external network")
return
}
}
func TestGoogleDNSAddr(t *testing.T) {
- if testing.Short() || avoidMacFirewall {
+ if testing.Short() || !*testExternal {
t.Logf("skipping test to avoid external network")
return
}
package net
import (
+ "errors"
"os"
"runtime"
+ "syscall"
"testing"
)
-var listenMulticastUDPTests = []struct {
+var multicastListenerTests = []struct {
net string
gaddr *UDPAddr
flags Flags
- ipv6 bool
+ ipv6 bool // test with underlying AF_INET6 socket
}{
// cf. RFC 4727: Experimental Values in IPv4, IPv6, ICMPv4, ICMPv6, UDP, and TCP Headers
+
{"udp", &UDPAddr{IPv4(224, 0, 0, 254), 12345}, FlagUp | FlagLoopback, false},
- {"udp4", &UDPAddr{IPv4(224, 0, 0, 254), 12345}, FlagUp | FlagLoopback, false},
+ {"udp", &UDPAddr{IPv4(224, 0, 0, 254), 12345}, 0, false},
{"udp", &UDPAddr{ParseIP("ff0e::114"), 12345}, FlagUp | FlagLoopback, true},
+ {"udp", &UDPAddr{ParseIP("ff0e::114"), 12345}, 0, true},
+
+ {"udp4", &UDPAddr{IPv4(224, 0, 0, 254), 12345}, FlagUp | FlagLoopback, false},
+ {"udp4", &UDPAddr{IPv4(224, 0, 0, 254), 12345}, 0, false},
+
{"udp6", &UDPAddr{ParseIP("ff01::114"), 12345}, FlagUp | FlagLoopback, true},
+ {"udp6", &UDPAddr{ParseIP("ff01::114"), 12345}, 0, true},
{"udp6", &UDPAddr{ParseIP("ff02::114"), 12345}, FlagUp | FlagLoopback, true},
+ {"udp6", &UDPAddr{ParseIP("ff02::114"), 12345}, 0, true},
{"udp6", &UDPAddr{ParseIP("ff04::114"), 12345}, FlagUp | FlagLoopback, true},
+ {"udp6", &UDPAddr{ParseIP("ff04::114"), 12345}, 0, true},
{"udp6", &UDPAddr{ParseIP("ff05::114"), 12345}, FlagUp | FlagLoopback, true},
+ {"udp6", &UDPAddr{ParseIP("ff05::114"), 12345}, 0, true},
{"udp6", &UDPAddr{ParseIP("ff08::114"), 12345}, FlagUp | FlagLoopback, true},
+ {"udp6", &UDPAddr{ParseIP("ff08::114"), 12345}, 0, true},
{"udp6", &UDPAddr{ParseIP("ff0e::114"), 12345}, FlagUp | FlagLoopback, true},
+ {"udp6", &UDPAddr{ParseIP("ff0e::114"), 12345}, 0, true},
}
-func TestListenMulticastUDP(t *testing.T) {
+// TestMulticastListener tests both single and double listen to a test
+// listener with same address family, same group address and same port.
+func TestMulticastListener(t *testing.T) {
switch runtime.GOOS {
case "netbsd", "openbsd", "plan9", "windows":
return
}
}
- for _, tt := range listenMulticastUDPTests {
+ for _, tt := range multicastListenerTests {
if tt.ipv6 && (!supportsIPv6 || os.Getuid() != 0) {
continue
}
- ift, err := Interfaces()
+ ifi, err := availMulticastInterface(t, tt.flags)
if err != nil {
- t.Fatalf("Interfaces failed: %v", err)
- }
- var ifi *Interface
- for _, x := range ift {
- if x.Flags&tt.flags == tt.flags {
- ifi = &x
- break
- }
- }
- if ifi == nil {
- t.Logf("an appropriate multicast interface not found")
- return
+ continue
}
- c, err := ListenMulticastUDP(tt.net, ifi, tt.gaddr)
+ c1, err := ListenMulticastUDP(tt.net, ifi, tt.gaddr)
if err != nil {
- t.Fatalf("ListenMulticastUDP failed: %v", err)
+ t.Fatalf("First ListenMulticastUDP failed: %v", err)
}
- defer c.Close() // test to listen concurrently across multiple listeners
- if !tt.ipv6 {
- testIPv4MulticastSocketOptions(t, c.fd, ifi)
- } else {
- testIPv6MulticastSocketOptions(t, c.fd, ifi)
- }
- ifmat, err := ifi.MulticastAddrs()
+ checkMulticastListener(t, err, c1, tt.gaddr)
+ c2, err := ListenMulticastUDP(tt.net, ifi, tt.gaddr)
if err != nil {
- t.Fatalf("MulticastAddrs failed: %v", err)
- }
- var found bool
- for _, ifma := range ifmat {
- if ifma.(*IPAddr).IP.Equal(tt.gaddr.IP) {
- found = true
- break
- }
+ t.Fatalf("Second ListenMulticastUDP failed: %v", err)
}
- if !found {
- t.Fatalf("%q not found in RIB", tt.gaddr.String())
+ checkMulticastListener(t, err, c2, tt.gaddr)
+ c2.Close()
+ switch c1.fd.family {
+ case syscall.AF_INET:
+ testIPv4MulticastSocketOptions(t, c1.fd, ifi)
+ case syscall.AF_INET6:
+ testIPv6MulticastSocketOptions(t, c1.fd, ifi)
}
+ c1.Close()
}
}
-func TestSimpleListenMulticastUDP(t *testing.T) {
+func TestSimpleMulticastListener(t *testing.T) {
switch runtime.GOOS {
case "plan9":
return
}
- for _, tt := range listenMulticastUDPTests {
+ for _, tt := range multicastListenerTests {
if tt.ipv6 {
continue
}
- tt.flags = FlagUp | FlagMulticast
+ tt.flags = FlagUp | FlagMulticast // for windows testing
+ ifi, err := availMulticastInterface(t, tt.flags)
+ if err != nil {
+ continue
+ }
+ c1, err := ListenMulticastUDP(tt.net, ifi, tt.gaddr)
+ if err != nil {
+ t.Fatalf("First ListenMulticastUDP failed: %v", err)
+ }
+ checkSimpleMulticastListener(t, err, c1, tt.gaddr)
+ c2, err := ListenMulticastUDP(tt.net, ifi, tt.gaddr)
+ if err != nil {
+ t.Fatalf("Second ListenMulticastUDP failed: %v", err)
+ }
+ checkSimpleMulticastListener(t, err, c2, tt.gaddr)
+ c2.Close()
+ c1.Close()
+ }
+}
+
+func checkMulticastListener(t *testing.T, err error, c *UDPConn, gaddr *UDPAddr) {
+ if !multicastRIBContains(t, gaddr.IP) {
+ t.Fatalf("%q not found in RIB", gaddr.String())
+ }
+ if c.LocalAddr().String() != gaddr.String() {
+ t.Fatalf("LocalAddr returns %q, expected %q", c.LocalAddr().String(), gaddr.String())
+ }
+}
+
+func checkSimpleMulticastListener(t *testing.T, err error, c *UDPConn, gaddr *UDPAddr) {
+ if c.LocalAddr().String() != gaddr.String() {
+ t.Fatalf("LocalAddr returns %q, expected %q", c.LocalAddr().String(), gaddr.String())
+ }
+}
+
+func availMulticastInterface(t *testing.T, flags Flags) (*Interface, error) {
+ var ifi *Interface
+ if flags != Flags(0) {
ift, err := Interfaces()
if err != nil {
t.Fatalf("Interfaces failed: %v", err)
}
- var ifi *Interface
for _, x := range ift {
- if x.Flags&tt.flags == tt.flags {
+ if x.Flags&flags == flags {
ifi = &x
break
}
}
if ifi == nil {
- t.Logf("an appropriate multicast interface not found")
- return
+ return nil, errors.New("an appropriate multicast interface not found")
}
- c, err := ListenMulticastUDP(tt.net, ifi, tt.gaddr)
+ }
+ return ifi, nil
+}
+
+func multicastRIBContains(t *testing.T, ip IP) bool {
+ ift, err := Interfaces()
+ if err != nil {
+ t.Fatalf("Interfaces failed: %v", err)
+ }
+ for _, ifi := range ift {
+ ifmat, err := ifi.MulticastAddrs()
if err != nil {
- t.Fatalf("ListenMulticastUDP failed: %v", err)
+ t.Fatalf("MulticastAddrs failed: %v", err)
+ }
+ for _, ifma := range ifmat {
+ if ifma.(*IPAddr).IP.Equal(ip) {
+ return true
+ }
}
- c.Close()
}
+ return false
}
func testIPv4MulticastSocketOptions(t *testing.T, fd *netFD, ifi *Interface) {
- ifmc, err := ipv4MulticastInterface(fd)
+ _, err := ipv4MulticastInterface(fd)
if err != nil {
t.Fatalf("ipv4MulticastInterface failed: %v", err)
}
- t.Logf("IPv4 multicast interface: %v", ifmc)
- err = setIPv4MulticastInterface(fd, ifi)
- if err != nil {
- t.Fatalf("setIPv4MulticastInterface failed: %v", err)
+ if ifi != nil {
+ err = setIPv4MulticastInterface(fd, ifi)
+ if err != nil {
+ t.Fatalf("setIPv4MulticastInterface failed: %v", err)
+ }
}
-
- ttl, err := ipv4MulticastTTL(fd)
+ _, err = ipv4MulticastTTL(fd)
if err != nil {
t.Fatalf("ipv4MulticastTTL failed: %v", err)
}
- t.Logf("IPv4 multicast TTL: %v", ttl)
err = setIPv4MulticastTTL(fd, 1)
if err != nil {
t.Fatalf("setIPv4MulticastTTL failed: %v", err)
}
-
- loop, err := ipv4MulticastLoopback(fd)
+ _, err = ipv4MulticastLoopback(fd)
if err != nil {
t.Fatalf("ipv4MulticastLoopback failed: %v", err)
}
- t.Logf("IPv4 multicast loopback: %v", loop)
err = setIPv4MulticastLoopback(fd, false)
if err != nil {
t.Fatalf("setIPv4MulticastLoopback failed: %v", err)
}
func testIPv6MulticastSocketOptions(t *testing.T, fd *netFD, ifi *Interface) {
- ifmc, err := ipv6MulticastInterface(fd)
+ _, err := ipv6MulticastInterface(fd)
if err != nil {
t.Fatalf("ipv6MulticastInterface failed: %v", err)
}
- t.Logf("IPv6 multicast interface: %v", ifmc)
- err = setIPv6MulticastInterface(fd, ifi)
- if err != nil {
- t.Fatalf("setIPv6MulticastInterface failed: %v", err)
+ if ifi != nil {
+ err = setIPv6MulticastInterface(fd, ifi)
+ if err != nil {
+ t.Fatalf("setIPv6MulticastInterface failed: %v", err)
+ }
}
-
- hoplim, err := ipv6MulticastHopLimit(fd)
+ _, err = ipv6MulticastHopLimit(fd)
if err != nil {
t.Fatalf("ipv6MulticastHopLimit failed: %v", err)
}
- t.Logf("IPv6 multicast hop limit: %v", hoplim)
err = setIPv6MulticastHopLimit(fd, 1)
if err != nil {
t.Fatalf("setIPv6MulticastHopLimit failed: %v", err)
}
-
- loop, err := ipv6MulticastLoopback(fd)
+ _, err = ipv6MulticastLoopback(fd)
if err != nil {
t.Fatalf("ipv6MulticastLoopback failed: %v", err)
}
- t.Logf("IPv6 multicast loopback: %v", loop)
err = setIPv6MulticastLoopback(fd, false)
if err != nil {
t.Fatalf("setIPv6MulticastLoopback failed: %v", err)
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Package net provides a portable interface to Unix networks sockets,
-// including TCP/IP, UDP, domain name resolution, and Unix domain sockets.
+/*
+Package net provides a portable interface for network I/O, including
+TCP/IP, UDP, domain name resolution, and Unix domain sockets.
+
+Although the package provides access to low-level networking
+primitives, most clients will need only the basic interface provided
+by the Dial, Listen, and Accept functions and the associated
+Conn and Listener interfaces. The crypto/tls package uses
+the same interfaces and similar Dial and Listen functions.
+
+The Dial function connects to a server:
+
+ conn, err := net.Dial("tcp", "google.com:80")
+ if err != nil {
+ // handle error
+ }
+ fmt.Fprintf(conn, "GET / HTTP/1.0\r\n\r\n")
+ status, err := bufio.NewReader(conn).ReadString('\n')
+ // ...
+
+The Listen function creates servers:
+
+ ln, err := net.Listen("tcp", ":8080")
+ if err != nil {
+ // handle error
+ }
+ for {
+ conn, err := ln.Accept()
+ if err != nil {
+ // handle error
+ continue
+ }
+ go handleConnection(conn)
+ }
+*/
package net
// TODO(rsc):
RemoteAddr() Addr
// SetDeadline sets the read and write deadlines associated
- // with the connection.
+ // with the connection. It is equivalent to calling both
+ // SetReadDeadline and SetWriteDeadline.
+ //
+ // A deadline is an absolute time after which I/O operations
+ // fail with a timeout (see type Error) instead of
+ // blocking. The deadline applies to all future I/O, not just
+ // the immediately following call to Read or Write.
+ //
+ // An idle timeout can be implemented by repeatedly extending
+ // the deadline after successful Read or Write calls.
+ //
+ // A zero value for t means I/O operations will not time out.
SetDeadline(t time.Time) error
- // SetReadDeadline sets the deadline for all Read calls to return.
- // If the deadline is reached, Read will fail with a timeout
- // (see type Error) instead of blocking.
+ // SetReadDeadline sets the deadline for Read calls.
// A zero value for t means Read will not time out.
SetReadDeadline(t time.Time) error
- // SetWriteDeadline sets the deadline for all Write calls to return.
- // If the deadline is reached, Write will fail with a timeout
- // (see type Error) instead of blocking.
- // A zero value for t means Write will not time out.
+ // SetWriteDeadline sets the deadline for Write calls.
// Even if write times out, it may return n > 0, indicating that
// some of the data was successfully written.
+ // A zero value for t means Write will not time out.
SetWriteDeadline(t time.Time) error
}
if err != nil {
return nil, err
}
- return &file{fd, make([]byte, 1024)[0:0], false}, nil
+ return &file{fd, make([]byte, os.Getpagesize())[0:0], false}, nil
}
func byteIndex(s string, c byte) int {
}
func (WriteFailCodec) ReadResponseHeader(*Response) error {
- time.Sleep(120 * time.Second)
+ select {}
panic("unreachable")
}
func (WriteFailCodec) ReadResponseBody(interface{}) error {
- time.Sleep(120 * time.Second)
+ select {}
panic("unreachable")
}
t.Logf("Test %q %q %q", network, listenaddr, dialaddr)
switch listenaddr {
case "", "0.0.0.0", "[::]", "[::ffff:0.0.0.0]":
- if testing.Short() || avoidMacFirewall {
+ if testing.Short() || !*testExternal {
t.Logf("skip wildcard listen during short test")
return
}
// server name to be used when authenticating.
func NewClient(conn net.Conn, host string) (*Client, error) {
text := textproto.NewConn(conn)
- _, msg, err := text.ReadResponse(220)
+ _, _, err := text.ReadResponse(220)
if err != nil {
text.Close()
return nil, err
}
c := &Client{Text: text, conn: conn, serverName: host}
- if strings.Contains(msg, "ESMTP") {
- err = c.ehlo()
- } else {
+ err = c.ehlo()
+ if err != nil {
err = c.helo()
}
return c, err
"bufio"
"bytes"
"io"
+ "net"
"net/textproto"
"strings"
"testing"
+ "time"
)
type authTest struct {
io.ReadWriter
}
-func (f faker) Close() error {
- return nil
-}
+func (f faker) Close() error { return nil }
+func (f faker) LocalAddr() net.Addr { return nil }
+func (f faker) RemoteAddr() net.Addr { return nil }
+func (f faker) SetDeadline(time.Time) error { return nil }
+func (f faker) SetReadDeadline(time.Time) error { return nil }
+func (f faker) SetWriteDeadline(time.Time) error { return nil }
func TestBasic(t *testing.T) {
basicServer = strings.Join(strings.Split(basicServer, "\n"), "\r\n")
.
QUIT
`
+
+func TestNewClient(t *testing.T) {
+ newClientServer = strings.Join(strings.Split(newClientServer, "\n"), "\r\n")
+ newClientClient = strings.Join(strings.Split(newClientClient, "\n"), "\r\n")
+
+ var cmdbuf bytes.Buffer
+ bcmdbuf := bufio.NewWriter(&cmdbuf)
+ out := func() string {
+ bcmdbuf.Flush()
+ return cmdbuf.String()
+ }
+ var fake faker
+ fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(newClientServer)), bcmdbuf)
+ c, err := NewClient(fake, "fake.host")
+ if err != nil {
+ t.Fatalf("NewClient: %v\n(after %v)", err, out())
+ }
+ if ok, args := c.Extension("aUtH"); !ok || args != "LOGIN PLAIN" {
+ t.Fatalf("Expected AUTH supported")
+ }
+ if ok, _ := c.Extension("DSN"); ok {
+ t.Fatalf("Shouldn't support DSN")
+ }
+ if err := c.Quit(); err != nil {
+ t.Fatalf("QUIT failed: %s", err)
+ }
+
+ actualcmds := out()
+ if newClientClient != actualcmds {
+ t.Fatalf("Got:\n%s\nExpected:\n%s", actualcmds, newClientClient)
+ }
+}
+
+var newClientServer = `220 hello world
+250-mx.google.com at your service
+250-SIZE 35651584
+250-AUTH LOGIN PLAIN
+250 8BITMIME
+221 OK
+`
+
+var newClientClient = `EHLO localhost
+QUIT
+`
+
+func TestNewClient2(t *testing.T) {
+ newClient2Server = strings.Join(strings.Split(newClient2Server, "\n"), "\r\n")
+ newClient2Client = strings.Join(strings.Split(newClient2Client, "\n"), "\r\n")
+
+ var cmdbuf bytes.Buffer
+ bcmdbuf := bufio.NewWriter(&cmdbuf)
+ var fake faker
+ fake.ReadWriter = bufio.NewReadWriter(bufio.NewReader(strings.NewReader(newClient2Server)), bcmdbuf)
+ c, err := NewClient(fake, "fake.host")
+ if err != nil {
+ t.Fatalf("NewClient: %v", err)
+ }
+ if ok, _ := c.Extension("DSN"); ok {
+ t.Fatalf("Shouldn't support DSN")
+ }
+ if err := c.Quit(); err != nil {
+ t.Fatalf("QUIT failed: %s", err)
+ }
+
+ bcmdbuf.Flush()
+ actualcmds := cmdbuf.String()
+ if newClient2Client != actualcmds {
+ t.Fatalf("Got:\n%s\nExpected:\n%s", actualcmds, newClient2Client)
+ }
+}
+
+var newClient2Server = `220 hello world
+502 EH?
+250-mx.google.com at your service
+250-SIZE 35651584
+250-AUTH LOGIN PLAIN
+250 8BITMIME
+221 OK
+`
+
+var newClient2Client = `EHLO localhost
+HELO localhost
+QUIT
+`
return nil, err
}
+ var bla syscall.Sockaddr
if la != nil {
- la, err = listenerSockaddr(s, f, la, toAddr)
+ bla, err = listenerSockaddr(s, f, la, toAddr)
if err != nil {
closesocket(s)
return nil, err
}
- err = syscall.Bind(s, la)
+ err = syscall.Bind(s, bla)
if err != nil {
closesocket(s)
return nil, err
}
sa, _ := syscall.Getsockname(s)
- laddr := toAddr(sa)
+ var laddr Addr
+ if la != nil && bla != la {
+ laddr = toAddr(la)
+ } else {
+ laddr = toAddr(sa)
+ }
sa, _ = syscall.Getpeername(s)
raddr := toAddr(sa)
package net
import (
- "os"
+ "syscall"
"time"
)
// SetDeadline implements the Conn SetDeadline method.
func (c *TCPConn) SetDeadline(t time.Time) error {
- return os.EPLAN9
+ return syscall.EPLAN9
}
// SetReadDeadline implements the Conn SetReadDeadline method.
func (c *TCPConn) SetReadDeadline(t time.Time) error {
- return os.EPLAN9
+ return syscall.EPLAN9
}
// SetWriteDeadline implements the Conn SetWriteDeadline method.
func (c *TCPConn) SetWriteDeadline(t time.Time) error {
- return os.EPLAN9
+ return syscall.EPLAN9
}
// CloseRead shuts down the reading side of the TCP connection.
// Most callers should just use Close.
func (c *TCPConn) CloseRead() error {
if !c.ok() {
- return os.EINVAL
+ return syscall.EINVAL
}
- return os.EPLAN9
+ return syscall.EPLAN9
}
// CloseWrite shuts down the writing side of the TCP connection.
// Most callers should just use Close.
func (c *TCPConn) CloseWrite() error {
if !c.ok() {
- return os.EINVAL
+ return syscall.EINVAL
}
- return os.EPLAN9
+ return syscall.EPLAN9
}
// DialTCP connects to the remote address raddr on the network net,
package net
import (
+ "fmt"
"io"
"os"
"syscall"
return &TCPAddr{sa.Addr[0:], sa.Port}
case *syscall.SockaddrInet6:
return &TCPAddr{sa.Addr[0:], sa.Port}
+ default:
+ if sa != nil {
+ // Diagnose when we will turn a non-nil sockaddr into a nil.
+ panic(fmt.Sprintf("unexpected type in sockaddrToTCP: %T", sa))
+ }
}
return nil
}
// Read implements the Conn Read method.
func (c *TCPConn) Read(b []byte) (n int, err error) {
if !c.ok() {
- return 0, os.EINVAL
+ return 0, syscall.EINVAL
}
return c.fd.Read(b)
}
// Write implements the Conn Write method.
func (c *TCPConn) Write(b []byte) (n int, err error) {
if !c.ok() {
- return 0, os.EINVAL
+ return 0, syscall.EINVAL
}
return c.fd.Write(b)
}
// Close closes the TCP connection.
func (c *TCPConn) Close() error {
if !c.ok() {
- return os.EINVAL
+ return syscall.EINVAL
}
err := c.fd.Close()
c.fd = nil
// Most callers should just use Close.
func (c *TCPConn) CloseRead() error {
if !c.ok() {
- return os.EINVAL
+ return syscall.EINVAL
}
return c.fd.CloseRead()
}
// Most callers should just use Close.
func (c *TCPConn) CloseWrite() error {
if !c.ok() {
- return os.EINVAL
+ return syscall.EINVAL
}
return c.fd.CloseWrite()
}
// SetDeadline implements the Conn SetDeadline method.
func (c *TCPConn) SetDeadline(t time.Time) error {
if !c.ok() {
- return os.EINVAL
+ return syscall.EINVAL
}
return setDeadline(c.fd, t)
}
// SetReadDeadline implements the Conn SetReadDeadline method.
func (c *TCPConn) SetReadDeadline(t time.Time) error {
if !c.ok() {
- return os.EINVAL
+ return syscall.EINVAL
}
return setReadDeadline(c.fd, t)
}
// SetWriteDeadline implements the Conn SetWriteDeadline method.
func (c *TCPConn) SetWriteDeadline(t time.Time) error {
if !c.ok() {
- return os.EINVAL
+ return syscall.EINVAL
}
return setWriteDeadline(c.fd, t)
}
// receive buffer associated with the connection.
func (c *TCPConn) SetReadBuffer(bytes int) error {
if !c.ok() {
- return os.EINVAL
+ return syscall.EINVAL
}
return setReadBuffer(c.fd, bytes)
}
// transmit buffer associated with the connection.
func (c *TCPConn) SetWriteBuffer(bytes int) error {
if !c.ok() {
- return os.EINVAL
+ return syscall.EINVAL
}
return setWriteBuffer(c.fd, bytes)
}
// data to be sent and acknowledged.
func (c *TCPConn) SetLinger(sec int) error {
if !c.ok() {
- return os.EINVAL
+ return syscall.EINVAL
}
return setLinger(c.fd, sec)
}
// keepalive messages on the connection.
func (c *TCPConn) SetKeepAlive(keepalive bool) error {
if !c.ok() {
- return os.EINVAL
+ return syscall.EINVAL
}
return setKeepAlive(c.fd, keepalive)
}
// that data is sent as soon as possible after a Write.
func (c *TCPConn) SetNoDelay(noDelay bool) error {
if !c.ok() {
- return os.EINVAL
+ return syscall.EINVAL
}
return setNoDelay(c.fd, noDelay)
}
}
func selfConnect(fd *netFD) bool {
+ // The socket constructor can return an fd with raddr nil under certain
+ // unknown conditions. The errors in the calls there to Getpeername
+ // are discarded, but we can't catch the problem there because those
+ // calls are sometimes legally erroneous with a "socket not connected".
+ // Since this code (selfConnect) is already trying to work around
+ // a problem, we make sure if this happens we recognize trouble and
+ // ask the DialTCP routine to try again.
+ // TODO: try to understand what's really going on.
+ if fd.laddr == nil || fd.raddr == nil {
+ return true
+ }
l := fd.laddr.(*TCPAddr)
r := fd.raddr.(*TCPAddr)
return l.Port == r.Port && l.IP.Equal(r.IP)
// and the remote address.
func (l *TCPListener) AcceptTCP() (c *TCPConn, err error) {
if l == nil || l.fd == nil || l.fd.sysfd < 0 {
- return nil, os.EINVAL
+ return nil, syscall.EINVAL
}
fd, err := l.fd.accept(sockaddrToTCP)
if err != nil {
// Already Accepted connections are not closed.
func (l *TCPListener) Close() error {
if l == nil || l.fd == nil {
- return os.EINVAL
+ return syscall.EINVAL
}
return l.fd.Close()
}
// A zero time value disables the deadline.
func (l *TCPListener) SetDeadline(t time.Time) error {
if l == nil || l.fd == nil {
- return os.EINVAL
+ return syscall.EINVAL
}
return setDeadline(l.fd, t)
}
--- /dev/null
+255.255.255.255 broadcasthost
+127.0.0.2 odin
+127.0.0.3 odin # inline comment
+::2 odin
+127.1.1.1 thor
+# aliases
+127.1.1.2 ullr ullrhost
+# Bogus entries that must be ignored.
+123.123.123 loki
+321.321.321.321
+# TODO(yvesj): Should we be able to parse this? From a Darwin system.
+fe80::1%lo0 localhost
--- /dev/null
+Idx Device : Count Querier Group Users Timer Reporter
+1 lo : 1 V3
+ 010000E0 1 0:00000000 0
+2 eth0 : 2 V2
+ FB0000E0 1 0:00000000 1
+ 010000E0 1 0:00000000 0
+3 eth1 : 1 V3
+ 010000E0 1 0:00000000 0
+4 eth2 : 1 V3
+ 010000E0 1 0:00000000 0
+5 eth0.100 : 2 V3
+ FB0000E0 1 0:00000000 0
+ 010000E0 1 0:00000000 0
+6 eth0.101 : 2 V3
+ FB0000E0 1 0:00000000 0
+ 010000E0 1 0:00000000 0
+7 eth0.102 : 2 V3
+ FB0000E0 1 0:00000000 0
+ 010000E0 1 0:00000000 0
+8 eth0.103 : 2 V3
+ FB0000E0 1 0:00000000 0
+ 010000E0 1 0:00000000 0
+9 device1tap2: 1 V3
+ 010000E0 1 0:00000000 0
--- /dev/null
+1 lo ff020000000000000000000000000001 1 0000000C 0
+2 eth0 ff0200000000000000000001ffac891e 1 00000006 0
+2 eth0 ff020000000000000000000000000001 1 0000000C 0
+3 eth1 ff0200000000000000000001ffac8928 2 00000006 0
+3 eth1 ff020000000000000000000000000001 1 0000000C 0
+4 eth2 ff0200000000000000000001ffac8932 2 00000006 0
+4 eth2 ff020000000000000000000000000001 1 0000000C 0
+5 eth0.100 ff0200000000000000000001ffac891e 1 00000004 0
+5 eth0.100 ff020000000000000000000000000001 1 0000000C 0
+6 pan0 ff020000000000000000000000000001 1 0000000C 0
+7 eth0.101 ff0200000000000000000001ffac891e 1 00000004 0
+7 eth0.101 ff020000000000000000000000000001 1 0000000C 0
+8 eth0.102 ff0200000000000000000001ffac891e 1 00000004 0
+8 eth0.102 ff020000000000000000000000000001 1 0000000C 0
+9 eth0.103 ff0200000000000000000001ffac891e 1 00000004 0
+9 eth0.103 ff020000000000000000000000000001 1 0000000C 0
+10 device1tap2 ff0200000000000000000001ff4cc3a3 1 00000004 0
+10 device1tap2 ff020000000000000000000000000001 1 0000000C 0
// Key ends at first colon; must not have spaces.
i := bytes.IndexByte(kv, ':')
- if i < 0 || bytes.IndexByte(kv[0:i], ' ') >= 0 {
+ if i < 0 {
return m, ProtocolError("malformed MIME header line: " + string(kv))
}
- key := CanonicalMIMEHeaderKey(string(kv[0:i]))
+ key := string(kv[0:i])
+ if strings.Index(key, " ") >= 0 {
+ key = strings.TrimRight(key, " ")
+ }
+ key = CanonicalMIMEHeaderKey(key)
// Skip initial spaces in value.
i++ // skip colon
a := []byte(s)
upper := true
for i, v := range a {
+ if v == ' ' {
+ a[i] = '-'
+ upper = true
+ continue
+ }
if upper && 'a' <= v && v <= 'z' {
a[i] = v + 'A' - 'a'
}
}
}
+// Test that we read slightly-bogus MIME headers seen in the wild,
+// with spaces before colons, and spaces in keys.
+func TestReadMIMEHeaderNonCompliant(t *testing.T) {
+ // Invalid HTTP response header as sent by an Axis security
+ // camera: (this is handled by IE, Firefox, Chrome, curl, etc.)
+ r := reader("Foo: bar\r\n" +
+ "Content-Language: en\r\n" +
+ "SID : 0\r\n" +
+ "Audio Mode : None\r\n" +
+ "Privilege : 127\r\n\r\n")
+ m, err := r.ReadMIMEHeader()
+ want := MIMEHeader{
+ "Foo": {"bar"},
+ "Content-Language": {"en"},
+ "Sid": {"0"},
+ "Audio-Mode": {"None"},
+ "Privilege": {"127"},
+ }
+ if !reflect.DeepEqual(m, want) || err != nil {
+ t.Fatalf("ReadMIMEHeader =\n%v, %v; want:\n%v", m, err, want)
+ }
+}
+
type readResponseTest struct {
in string
inCode int
//
// Writer, to write dot-encoded text blocks.
//
+// Conn, a convenient packaging of Reader, Writer, and Pipeline for use
+// with a single network connection.
+//
package textproto
import (
import (
"errors"
"os"
+ "syscall"
"time"
)
// SetDeadline implements the Conn SetDeadline method.
func (c *UDPConn) SetDeadline(t time.Time) error {
- return os.EPLAN9
+ return syscall.EPLAN9
}
// SetReadDeadline implements the Conn SetReadDeadline method.
func (c *UDPConn) SetReadDeadline(t time.Time) error {
- return os.EPLAN9
+ return syscall.EPLAN9
}
// SetWriteDeadline implements the Conn SetWriteDeadline method.
func (c *UDPConn) SetWriteDeadline(t time.Time) error {
- return os.EPLAN9
+ return syscall.EPLAN9
}
// UDP-specific methods.
// after a fixed time limit; see SetDeadline and SetReadDeadline.
func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err error) {
if !c.ok() {
- return 0, nil, os.EINVAL
+ return 0, nil, syscall.EINVAL
}
if c.data == nil {
c.data, err = os.OpenFile(c.dir+"/data", os.O_RDWR, 0)
// ReadFrom implements the PacketConn ReadFrom method.
func (c *UDPConn) ReadFrom(b []byte) (n int, addr Addr, err error) {
if !c.ok() {
- return 0, nil, os.EINVAL
+ return 0, nil, syscall.EINVAL
}
return c.ReadFromUDP(b)
}
// On packet-oriented connections, write timeouts are rare.
func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (n int, err error) {
if !c.ok() {
- return 0, os.EINVAL
+ return 0, syscall.EINVAL
}
if c.data == nil {
c.data, err = os.OpenFile(c.dir+"/data", os.O_RDWR, 0)
// WriteTo implements the PacketConn WriteTo method.
func (c *UDPConn) WriteTo(b []byte, addr Addr) (n int, err error) {
if !c.ok() {
- return 0, os.EINVAL
+ return 0, syscall.EINVAL
}
a, ok := addr.(*UDPAddr)
if !ok {
- return 0, &OpError{"write", c.dir, addr, os.EINVAL}
+ return 0, &OpError{"write", c.dir, addr, syscall.EINVAL}
}
return c.WriteToUDP(b, a)
}
// the interface to join. ListenMulticastUDP uses default
// multicast interface if ifi is nil.
func ListenMulticastUDP(net string, ifi *Interface, gaddr *UDPAddr) (*UDPConn, error) {
- return nil, os.EPLAN9
+ return nil, syscall.EPLAN9
}
// Read implements the Conn Read method.
func (c *UDPConn) Read(b []byte) (int, error) {
if !c.ok() {
- return 0, os.EINVAL
+ return 0, syscall.EINVAL
}
return c.fd.Read(b)
}
// Write implements the Conn Write method.
func (c *UDPConn) Write(b []byte) (int, error) {
if !c.ok() {
- return 0, os.EINVAL
+ return 0, syscall.EINVAL
}
return c.fd.Write(b)
}
// Close closes the UDP connection.
func (c *UDPConn) Close() error {
if !c.ok() {
- return os.EINVAL
+ return syscall.EINVAL
}
err := c.fd.Close()
c.fd = nil
// SetDeadline implements the Conn SetDeadline method.
func (c *UDPConn) SetDeadline(t time.Time) error {
if !c.ok() {
- return os.EINVAL
+ return syscall.EINVAL
}
return setDeadline(c.fd, t)
}
// SetReadDeadline implements the Conn SetReadDeadline method.
func (c *UDPConn) SetReadDeadline(t time.Time) error {
if !c.ok() {
- return os.EINVAL
+ return syscall.EINVAL
}
return setReadDeadline(c.fd, t)
}
// SetWriteDeadline implements the Conn SetWriteDeadline method.
func (c *UDPConn) SetWriteDeadline(t time.Time) error {
if !c.ok() {
- return os.EINVAL
+ return syscall.EINVAL
}
return setWriteDeadline(c.fd, t)
}
// receive buffer associated with the connection.
func (c *UDPConn) SetReadBuffer(bytes int) error {
if !c.ok() {
- return os.EINVAL
+ return syscall.EINVAL
}
return setReadBuffer(c.fd, bytes)
}
// transmit buffer associated with the connection.
func (c *UDPConn) SetWriteBuffer(bytes int) error {
if !c.ok() {
- return os.EINVAL
+ return syscall.EINVAL
}
return setWriteBuffer(c.fd, bytes)
}
// after a fixed time limit; see SetDeadline and SetReadDeadline.
func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err error) {
if !c.ok() {
- return 0, nil, os.EINVAL
+ return 0, nil, syscall.EINVAL
}
n, sa, err := c.fd.ReadFrom(b)
switch sa := sa.(type) {
// ReadFrom implements the PacketConn ReadFrom method.
func (c *UDPConn) ReadFrom(b []byte) (int, Addr, error) {
if !c.ok() {
- return 0, nil, os.EINVAL
+ return 0, nil, syscall.EINVAL
}
n, uaddr, err := c.ReadFromUDP(b)
return n, uaddr.toAddr(), err
// On packet-oriented connections, write timeouts are rare.
func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (int, error) {
if !c.ok() {
- return 0, os.EINVAL
+ return 0, syscall.EINVAL
}
if c.fd.isConnected {
return 0, &OpError{"write", c.fd.net, addr, ErrWriteToConnected}
// WriteTo implements the PacketConn WriteTo method.
func (c *UDPConn) WriteTo(b []byte, addr Addr) (int, error) {
if !c.ok() {
- return 0, os.EINVAL
+ return 0, syscall.EINVAL
}
a, ok := addr.(*UDPAddr)
if !ok {
- return 0, &OpError{"write", c.fd.net, addr, os.EINVAL}
+ return 0, &OpError{"write", c.fd.net, addr, syscall.EINVAL}
}
return c.WriteToUDP(b, a)
}
return nil, UnknownNetworkError(net)
}
if gaddr == nil || gaddr.IP == nil {
- return nil, &OpError{"listenmulticastudp", "udp", nil, errMissingAddress}
+ return nil, &OpError{"listenmulticast", net, nil, errMissingAddress}
}
fd, err := internetSocket(net, gaddr.toAddr(), nil, syscall.SOCK_DGRAM, 0, "listen", sockaddrToUDP)
if err != nil {
package net
import (
- "os"
+ "syscall"
"time"
)
// Read implements the Conn Read method.
func (c *UnixConn) Read(b []byte) (n int, err error) {
- return 0, os.EPLAN9
+ return 0, syscall.EPLAN9
}
// Write implements the Conn Write method.
func (c *UnixConn) Write(b []byte) (n int, err error) {
- return 0, os.EPLAN9
+ return 0, syscall.EPLAN9
}
// Close closes the Unix domain connection.
func (c *UnixConn) Close() error {
- return os.EPLAN9
+ return syscall.EPLAN9
}
// LocalAddr returns the local network address, a *UnixAddr.
// SetDeadline implements the Conn SetDeadline method.
func (c *UnixConn) SetDeadline(t time.Time) error {
- return os.EPLAN9
+ return syscall.EPLAN9
}
// SetReadDeadline implements the Conn SetReadDeadline method.
func (c *UnixConn) SetReadDeadline(t time.Time) error {
- return os.EPLAN9
+ return syscall.EPLAN9
}
// SetWriteDeadline implements the Conn SetWriteDeadline method.
func (c *UnixConn) SetWriteDeadline(t time.Time) error {
- return os.EPLAN9
+ return syscall.EPLAN9
}
// ReadFrom implements the PacketConn ReadFrom method.
func (c *UnixConn) ReadFrom(b []byte) (n int, addr Addr, err error) {
- err = os.EPLAN9
+ err = syscall.EPLAN9
return
}
// WriteTo implements the PacketConn WriteTo method.
func (c *UnixConn) WriteTo(b []byte, addr Addr) (n int, err error) {
- err = os.EPLAN9
+ err = syscall.EPLAN9
return
}
// which must be "unix" or "unixgram". If laddr is not nil, it is used
// as the local address for the connection.
func DialUnix(net string, laddr, raddr *UnixAddr) (c *UnixConn, err error) {
- return nil, os.EPLAN9
+ return nil, syscall.EPLAN9
}
// UnixListener is a Unix domain socket listener.
// ListenUnix announces on the Unix domain socket laddr and returns a Unix listener.
// Net must be "unix" (stream sockets).
func ListenUnix(net string, laddr *UnixAddr) (l *UnixListener, err error) {
- return nil, os.EPLAN9
+ return nil, syscall.EPLAN9
}
// Accept implements the Accept method in the Listener interface;
// it waits for the next call and returns a generic Conn.
func (l *UnixListener) Accept() (c Conn, err error) {
- return nil, os.EPLAN9
+ return nil, syscall.EPLAN9
}
// Close stops listening on the Unix address.
// Already accepted connections are not closed.
func (l *UnixListener) Close() error {
- return os.EPLAN9
+ return syscall.EPLAN9
}
// Addr returns the listener's network address.
f = sockaddrToUnixpacket
}
- fd, oserr := socket(net, syscall.AF_UNIX, sotype, 0, la, ra, f)
- if oserr != nil {
+ fd, err = socket(net, syscall.AF_UNIX, sotype, 0, la, ra, f)
+ if err != nil {
goto Error
}
return fd, nil
if mode == "listen" {
addr = laddr
}
- return nil, &OpError{Op: mode, Net: net, Addr: addr, Err: oserr}
+ return nil, &OpError{Op: mode, Net: net, Addr: addr, Err: err}
}
func sockaddrToUnix(sa syscall.Sockaddr) Addr {
// Read implements the Conn Read method.
func (c *UnixConn) Read(b []byte) (n int, err error) {
if !c.ok() {
- return 0, os.EINVAL
+ return 0, syscall.EINVAL
}
return c.fd.Read(b)
}
// Write implements the Conn Write method.
func (c *UnixConn) Write(b []byte) (n int, err error) {
if !c.ok() {
- return 0, os.EINVAL
+ return 0, syscall.EINVAL
}
return c.fd.Write(b)
}
// Close closes the Unix domain connection.
func (c *UnixConn) Close() error {
if !c.ok() {
- return os.EINVAL
+ return syscall.EINVAL
}
err := c.fd.Close()
c.fd = nil
// SetDeadline implements the Conn SetDeadline method.
func (c *UnixConn) SetDeadline(t time.Time) error {
if !c.ok() {
- return os.EINVAL
+ return syscall.EINVAL
}
return setDeadline(c.fd, t)
}
// SetReadDeadline implements the Conn SetReadDeadline method.
func (c *UnixConn) SetReadDeadline(t time.Time) error {
if !c.ok() {
- return os.EINVAL
+ return syscall.EINVAL
}
return setReadDeadline(c.fd, t)
}
// SetWriteDeadline implements the Conn SetWriteDeadline method.
func (c *UnixConn) SetWriteDeadline(t time.Time) error {
if !c.ok() {
- return os.EINVAL
+ return syscall.EINVAL
}
return setWriteDeadline(c.fd, t)
}
// receive buffer associated with the connection.
func (c *UnixConn) SetReadBuffer(bytes int) error {
if !c.ok() {
- return os.EINVAL
+ return syscall.EINVAL
}
return setReadBuffer(c.fd, bytes)
}
// transmit buffer associated with the connection.
func (c *UnixConn) SetWriteBuffer(bytes int) error {
if !c.ok() {
- return os.EINVAL
+ return syscall.EINVAL
}
return setWriteBuffer(c.fd, bytes)
}
// see SetDeadline and SetReadDeadline.
func (c *UnixConn) ReadFromUnix(b []byte) (n int, addr *UnixAddr, err error) {
if !c.ok() {
- return 0, nil, os.EINVAL
+ return 0, nil, syscall.EINVAL
}
n, sa, err := c.fd.ReadFrom(b)
switch sa := sa.(type) {
// ReadFrom implements the PacketConn ReadFrom method.
func (c *UnixConn) ReadFrom(b []byte) (n int, addr Addr, err error) {
if !c.ok() {
- return 0, nil, os.EINVAL
+ return 0, nil, syscall.EINVAL
}
n, uaddr, err := c.ReadFromUnix(b)
return n, uaddr.toAddr(), err
// On packet-oriented connections, write timeouts are rare.
func (c *UnixConn) WriteToUnix(b []byte, addr *UnixAddr) (n int, err error) {
if !c.ok() {
- return 0, os.EINVAL
+ return 0, syscall.EINVAL
}
if addr.Net != sotypeToNet(c.fd.sotype) {
- return 0, os.EAFNOSUPPORT
+ return 0, syscall.EAFNOSUPPORT
}
sa := &syscall.SockaddrUnix{Name: addr.Name}
return c.fd.WriteTo(b, sa)
// WriteTo implements the PacketConn WriteTo method.
func (c *UnixConn) WriteTo(b []byte, addr Addr) (n int, err error) {
if !c.ok() {
- return 0, os.EINVAL
+ return 0, syscall.EINVAL
}
a, ok := addr.(*UnixAddr)
if !ok {
- return 0, &OpError{"write", c.fd.net, addr, os.EINVAL}
+ return 0, &OpError{"write", c.fd.net, addr, syscall.EINVAL}
}
return c.WriteToUnix(b, a)
}
func (c *UnixConn) ReadMsgUnix(b, oob []byte) (n, oobn, flags int, addr *UnixAddr, err error) {
if !c.ok() {
- return 0, 0, 0, nil, os.EINVAL
+ return 0, 0, 0, nil, syscall.EINVAL
}
n, oobn, flags, sa, err := c.fd.ReadMsg(b, oob)
switch sa := sa.(type) {
func (c *UnixConn) WriteMsgUnix(b, oob []byte, addr *UnixAddr) (n, oobn int, err error) {
if !c.ok() {
- return 0, 0, os.EINVAL
+ return 0, 0, syscall.EINVAL
}
if addr != nil {
if addr.Net != sotypeToNet(c.fd.sotype) {
- return 0, 0, os.EAFNOSUPPORT
+ return 0, 0, syscall.EAFNOSUPPORT
}
sa := &syscall.SockaddrUnix{Name: addr.Name}
return c.fd.WriteMsg(b, oob, sa)
// and the remote address.
func (l *UnixListener) AcceptUnix() (*UnixConn, error) {
if l == nil || l.fd == nil {
- return nil, os.EINVAL
+ return nil, syscall.EINVAL
}
fd, err := l.fd.accept(sockaddrToUnix)
if err != nil {
// Already accepted connections are not closed.
func (l *UnixListener) Close() error {
if l == nil || l.fd == nil {
- return os.EINVAL
+ return syscall.EINVAL
}
// The operating system doesn't clean up
// A zero time value disables the deadline.
func (l *UnixListener) SetDeadline(t time.Time) (err error) {
if l == nil || l.fd == nil {
- return os.EINVAL
+ return syscall.EINVAL
}
return setDeadline(l.fd, t)
}
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Package URL parses URLs and implements query escaping.
+// Package url parses URLs and implements query escaping.
// See RFC 3986.
package url
}
// Parse parses rawurl into a URL structure.
-// The string rawurl is assumed not to have a #fragment suffix.
-// (Web browsers strip #fragment before sending the URL to a web server.)
// The rawurl may be relative or absolute.
func Parse(rawurl string) (url *URL, err error) {
- return parse(rawurl, false)
+ // Cut off #frag
+ u, frag := split(rawurl, '#', true)
+ if url, err = parse(u, false); err != nil {
+ return nil, err
+ }
+ if frag == "" {
+ return url, nil
+ }
+ if url.Fragment, err = unescape(frag, encodeFragment); err != nil {
+ return nil, &Error{"parse", rawurl, err}
+ }
+ return url, nil
}
-// ParseRequest parses rawurl into a URL structure. It assumes that
-// rawurl was received from an HTTP request, so the rawurl is interpreted
+// ParseRequestURI parses rawurl into a URL structure. It assumes that
+// rawurl was received in an HTTP request, so the rawurl is interpreted
// only as an absolute URI or an absolute path.
// The string rawurl is assumed not to have a #fragment suffix.
// (Web browsers strip #fragment before sending the URL to a web server.)
-func ParseRequest(rawurl string) (url *URL, err error) {
+func ParseRequestURI(rawurl string) (url *URL, err error) {
return parse(rawurl, true)
}
return
}
-// ParseWithReference is like Parse but allows a trailing #fragment.
-func ParseWithReference(rawurlref string) (url *URL, err error) {
- // Cut off #frag
- rawurl, frag := split(rawurlref, '#', true)
- if url, err = Parse(rawurl); err != nil {
- return nil, err
- }
- if frag == "" {
- return url, nil
- }
- if url.Fragment, err = unescape(frag, encodeFragment); err != nil {
- return nil, &Error{"parse", rawurlref, err}
- }
- return url, nil
-}
-
// String reassembles the URL into a valid URL string.
func (u *URL) String() string {
// TODO: Rewrite to use bytes.Buffer
return u.Scheme != ""
}
-// Parse parses a URL in the context of a base URL. The URL in ref
+// Parse parses a URL in the context of the receiver. The provided URL
// may be relative or absolute. Parse returns nil, err on parse
// failure, otherwise its return value is the same as ResolveReference.
-func (base *URL) Parse(ref string) (*URL, error) {
+func (u *URL) Parse(ref string) (*URL, error) {
refurl, err := Parse(ref)
if err != nil {
return nil, err
}
- return base.ResolveReference(refurl), nil
+ return u.ResolveReference(refurl), nil
}
// ResolveReference resolves a URI reference to an absolute URI from
// URL instance, even if the returned URL is identical to either the
// base or reference. If ref is an absolute URL, then ResolveReference
// ignores base and returns a copy of ref.
-func (base *URL) ResolveReference(ref *URL) *URL {
+func (u *URL) ResolveReference(ref *URL) *URL {
if ref.IsAbs() {
url := *ref
return &url
}
// relativeURI = ( net_path | abs_path | rel_path ) [ "?" query ]
- url := *base
+ url := *u
url.RawQuery = ref.RawQuery
url.Fragment = ref.Fragment
if ref.Opaque != "" {
url.Path = ref.Path
} else {
// The "rel_path" case.
- path := resolvePath(base.Path, ref.Path)
+ path := resolvePath(u.Path, ref.Path)
if !strings.HasPrefix(path, "/") {
path = "/" + path
}
},
"http://user:password@google.com",
},
-}
-
-var urlnofragtests = []URLTest{
- {
- "http://www.google.com/?q=go+language#foo",
- &URL{
- Scheme: "http",
- Host: "www.google.com",
- Path: "/",
- RawQuery: "q=go+language#foo",
- },
- "",
- },
-}
-
-var urlfragtests = []URLTest{
{
"http://www.google.com/?q=go+language#foo",
&URL{
func TestParse(t *testing.T) {
DoTest(t, Parse, "Parse", urltests)
- DoTest(t, Parse, "Parse", urlnofragtests)
-}
-
-func TestParseWithReference(t *testing.T) {
- DoTest(t, ParseWithReference, "ParseWithReference", urltests)
- DoTest(t, ParseWithReference, "ParseWithReference", urlfragtests)
}
const pathThatLooksSchemeRelative = "//not.a.user@not.a.host/just/a/path"
{"../dir/", false},
}
-func TestParseRequest(t *testing.T) {
+func TestParseRequestURI(t *testing.T) {
for _, test := range parseRequestUrlTests {
- _, err := ParseRequest(test.url)
+ _, err := ParseRequestURI(test.url)
valid := err == nil
if valid != test.expectedValid {
t.Errorf("Expected valid=%v for %q; got %v", test.expectedValid, test.url, valid)
}
}
- url, err := ParseRequest(pathThatLooksSchemeRelative)
+ url, err := ParseRequestURI(pathThatLooksSchemeRelative)
if err != nil {
t.Fatalf("Unexpected error %v", err)
}
func TestURLString(t *testing.T) {
DoTestString(t, Parse, "Parse", urltests)
- DoTestString(t, Parse, "Parse", urlnofragtests)
- DoTestString(t, ParseWithReference, "ParseWithReference", urltests)
- DoTestString(t, ParseWithReference, "ParseWithReference", urlfragtests)
}
type EscapeTest struct {
func TestResolveReference(t *testing.T) {
mustParse := func(url string) *URL {
- u, err := ParseWithReference(url)
+ u, err := Parse(url)
if err != nil {
t.Fatalf("Expected URL to parse: %q, got error: %v", url, err)
}
func TestResolveReferenceOpaque(t *testing.T) {
mustParse := func(url string) *URL {
- u, err := ParseWithReference(url)
+ u, err := Parse(url)
if err != nil {
t.Fatalf("Expected URL to parse: %q, got error: %v", url, err)
}
// This test checks that channel operations can proceed
// even when other concurrent operations are blocked.
func TestIndependentSends(t *testing.T) {
+ if testing.Short() {
+ t.Logf("disabled test during -short")
+ return
+ }
exp, imp := pair(t)
exportLoopback(exp, t)
// test flow control from exporter to importer.
func TestExportFlowControl(t *testing.T) {
+ if testing.Short() {
+ t.Logf("disabled test during -short")
+ return
+ }
exp, imp := pair(t)
sendDone := make(chan bool, 1)
// test flow control from importer to exporter.
func TestImportFlowControl(t *testing.T) {
+ if testing.Short() {
+ t.Logf("disabled test during -short")
+ return
+ }
exp, imp := pair(t)
ch := make(chan int)
"syscall"
)
+var errShortStat = errors.New("short stat message")
+var errBadStat = errors.New("bad stat message format")
+
func (file *File) readdir(n int) (fi []FileInfo, err error) {
// If this file has no dirinfo, create one.
if file.dirinfo == nil {
break
}
if d.nbuf < syscall.STATFIXLEN {
- return result, &PathError{"readdir", file.name, Eshortstat}
+ return result, &PathError{"readdir", file.name, errShortStat}
}
}
m, _ := gbit16(d.buf[d.bufp:])
m += 2
if m < syscall.STATFIXLEN {
- return result, &PathError{"readdir", file.name, Eshortstat}
+ return result, &PathError{"readdir", file.name, errShortStat}
}
dir, e := UnmarshalDir(d.buf[d.bufp : d.bufp+int(m)])
if e != nil {
n, b = gbit16(b)
if int(n) != len(b) {
- return nil, Ebadstat
+ return nil, errBadStat
}
d = new(Dir)
d.Muid, b = gstring(b)
if len(b) != 0 {
- return nil, Ebadstat
+ return nil, errBadStat
}
return d, nil
package os
-import (
- "errors"
- "syscall"
-)
+import "syscall"
// Expand replaces ${var} or $var in the string based on the mapping function.
// Invocations of undefined variables are replaced with the empty string.
return s[:i], i
}
-// ENOENV is the error indicating that an environment variable does not exist.
-var ENOENV = errors.New("no such environment variable")
-
-// Getenverror retrieves the value of the environment variable named by the key.
-// It returns the value and an error, if any.
-func Getenverror(key string) (value string, err error) {
- if len(key) == 0 {
- return "", EINVAL
- }
- val, found := syscall.Getenv(key)
- if !found {
- return "", ENOENV
- }
- return val, nil
-}
-
// Getenv retrieves the value of the environment variable named by the key.
// It returns the value, which will be empty if the variable is not present.
func Getenv(key string) string {
- v, _ := Getenverror(key)
+ v, _ := syscall.Getenv(key)
return v
}
package os
+import (
+ "errors"
+)
+
+// Portable analogs of some common system call errors.
+var (
+ ErrInvalid = errors.New("invalid argument")
+ ErrPermission = errors.New("permission denied")
+ ErrExist = errors.New("file already exists")
+ ErrNotExist = errors.New("file does not exist")
+)
+
// PathError records an error and the operation and file path that caused it.
type PathError struct {
Op string
package os
-import (
- "errors"
- "syscall"
-)
+// IsExist returns whether the error is known to report that a file already exists.
+func IsExist(err error) bool {
+ if pe, ok := err.(*PathError); ok {
+ err = pe.Err
+ }
+ return contains(err.Error(), " exists")
+}
-var (
- Eshortstat = errors.New("stat buffer too small")
- Ebadstat = errors.New("malformed stat buffer")
- Ebadfd = errors.New("fd out of range or not open")
- Ebadarg = errors.New("bad arg in system call")
- Enotdir = errors.New("not a directory")
- Enonexist = errors.New("file does not exist")
- Eexist = errors.New("file already exists")
- Eio = errors.New("i/o error")
- Eperm = errors.New("permission denied")
+// IsNotExist returns whether the error is known to report that a file does not exist.
+func IsNotExist(err error) bool {
+ if pe, ok := err.(*PathError); ok {
+ err = pe.Err
+ }
+ return contains(err.Error(), "does not exist")
+}
- EINVAL = Ebadarg
- ENOTDIR = Enotdir
- ENOENT = Enonexist
- EEXIST = Eexist
- EIO = Eio
- EACCES = Eperm
- EPERM = Eperm
- EISDIR = syscall.EISDIR
+// IsPermission returns whether the error is known to report that permission is denied.
+func IsPermission(err error) bool {
+ if pe, ok := err.(*PathError); ok {
+ err = pe.Err
+ }
+ return contains(err.Error(), "permission denied")
+}
- EBADF = errors.New("bad file descriptor")
- ENAMETOOLONG = errors.New("file name too long")
- ERANGE = errors.New("math result not representable")
- EPIPE = errors.New("Broken Pipe")
- EPLAN9 = errors.New("not supported by plan 9")
-)
+// contains is a local version of strings.Contains. It knows len(sep) > 1.
+func contains(s, sep string) bool {
+ n := len(sep)
+ c := sep[0]
+ for i := 0; i+n <= len(s); i++ {
+ if s[i] == c && s[i:i+n] == sep {
+ return true
+ }
+ }
+ return false
+}
import "syscall"
-// Commonly known Unix errors.
-var (
- EPERM error = syscall.EPERM
- ENOENT error = syscall.ENOENT
- ESRCH error = syscall.ESRCH
- EINTR error = syscall.EINTR
- EIO error = syscall.EIO
- E2BIG error = syscall.E2BIG
- ENOEXEC error = syscall.ENOEXEC
- EBADF error = syscall.EBADF
- ECHILD error = syscall.ECHILD
- EDEADLK error = syscall.EDEADLK
- ENOMEM error = syscall.ENOMEM
- EACCES error = syscall.EACCES
- EFAULT error = syscall.EFAULT
- EBUSY error = syscall.EBUSY
- EEXIST error = syscall.EEXIST
- EXDEV error = syscall.EXDEV
- ENODEV error = syscall.ENODEV
- ENOTDIR error = syscall.ENOTDIR
- EISDIR error = syscall.EISDIR
- EINVAL error = syscall.EINVAL
- ENFILE error = syscall.ENFILE
- EMFILE error = syscall.EMFILE
- ENOTTY error = syscall.ENOTTY
- EFBIG error = syscall.EFBIG
- ENOSPC error = syscall.ENOSPC
- ESPIPE error = syscall.ESPIPE
- EROFS error = syscall.EROFS
- EMLINK error = syscall.EMLINK
- EPIPE error = syscall.EPIPE
- EAGAIN error = syscall.EAGAIN
- EDOM error = syscall.EDOM
- ERANGE error = syscall.ERANGE
- EADDRINUSE error = syscall.EADDRINUSE
- ECONNREFUSED error = syscall.ECONNREFUSED
- ENAMETOOLONG error = syscall.ENAMETOOLONG
- EAFNOSUPPORT error = syscall.EAFNOSUPPORT
- ETIMEDOUT error = syscall.ETIMEDOUT
- ENOTCONN error = syscall.ENOTCONN
-)
+// IsExist returns whether the error is known to report that a file already exists.
+// It is satisfied by ErrExist as well as some syscall errors.
+func IsExist(err error) bool {
+ if pe, ok := err.(*PathError); ok {
+ err = pe.Err
+ }
+ return err == syscall.EEXIST || err == ErrExist
+}
+
+// IsNotExist returns whether the error is known to report that a file does not exist.
+// It is satisfied by ErrNotExist as well as some syscall errors.
+func IsNotExist(err error) bool {
+ if pe, ok := err.(*PathError); ok {
+ err = pe.Err
+ }
+ return err == syscall.ENOENT || err == ErrNotExist
+}
+
+// IsPermission returns whether the error is known to report that permission is denied.
+// It is satisfied by ErrPermission as well as some syscall errors.
+func IsPermission(err error) bool {
+ if pe, ok := err.(*PathError); ok {
+ err = pe.Err
+ }
+ return err == syscall.EACCES || err == syscall.EPERM || err == ErrPermission
+}
// Process is the underlying process, once started.
Process *os.Process
- // Waitmsg contains information about an exited process,
+ // ProcessState contains information about an exited process,
// available after a call to Wait or Run.
- Waitmsg *os.Waitmsg
+ ProcessState *os.ProcessState
err error // last error (from LookPath, stdin, stdout, stderr)
finished bool // when Wait was called
// An ExitError reports an unsuccessful exit by a command.
type ExitError struct {
- *os.Waitmsg
+ *os.ProcessState
}
func (e *ExitError) Error() string {
- return e.Waitmsg.String()
+ return e.ProcessState.String()
}
// Wait waits for the command to exit.
return errors.New("exec: Wait was already called")
}
c.finished = true
- msg, err := c.Process.Wait(0)
- c.Waitmsg = msg
+ state, err := c.Process.Wait()
+ c.ProcessState = state
var copyError error
for _ = range c.goroutine {
if err != nil {
return err
- } else if !msg.Exited() || msg.ExitStatus() != 0 {
- return &ExitError{msg}
+ } else if !state.Success() {
+ return &ExitError{state}
}
return copyError
"errors"
"os"
"strings"
+ "syscall"
)
// ErrNotFound is the error resulting if a path search failed to find an executable file.
if m := d.Mode(); !m.IsDir() && m&0111 != 0 {
return nil
}
- return os.EPERM
+ return syscall.EPERM
}
// LookPath searches for an executable binary named file
if m := d.Mode(); !m.IsDir() && m&0111 != 0 {
return nil
}
- return os.EPERM
+ return os.ErrPermission
}
// LookPath searches for an executable binary named file
return err
}
if d.IsDir() {
- return os.EPERM
+ return os.ErrPermission
}
return nil
}
return f, nil
}
}
- return ``, os.ENOENT
+ return ``, os.ErrNotExist
}
// LookPath searches for an executable binary named file
"errors"
"runtime"
"syscall"
+ "time"
)
// StartProcess starts a new process with the program, arguments and attributes
Sys: attr.Sys,
}
- // Create array of integer (system) fds.
- intfd := make([]int, len(attr.Files))
- for i, f := range attr.Files {
- if f == nil {
- intfd[i] = -1
- } else {
- intfd[i] = f.Fd()
- }
+ for _, f := range attr.Files {
+ sysattr.Files = append(sysattr.Files, f.Fd())
}
- sysattr.Files = intfd
-
pid, h, e := syscall.StartProcess(name, argv, sysattr)
if e != nil {
return nil, &PathError{"fork/exec", name, e}
return e
}
-// Waitmsg stores the information about an exited process as reported by Wait.
-type Waitmsg struct {
- syscall.Waitmsg
-}
-
// Wait waits for the Process to exit or stop, and then returns a
-// Waitmsg describing its status and an error, if any. The options
-// (WNOHANG etc.) affect the behavior of the Wait call.
-func (p *Process) Wait(options int) (w *Waitmsg, err error) {
+// ProcessState describing its status and an error, if any.
+func (p *Process) Wait() (ps *ProcessState, err error) {
var waitmsg syscall.Waitmsg
if p.Pid == -1 {
- return nil, EINVAL
+ return nil, ErrInvalid
}
for true {
}
}
- return &Waitmsg{waitmsg}, nil
-}
-
-// Wait waits for process pid to exit or stop, and then returns a
-// Waitmsg describing its status and an error, if any. The options
-// (WNOHANG etc.) affect the behavior of the Wait call.
-// Wait is equivalent to calling FindProcess and then Wait
-// and Release on the result.
-func Wait(pid int, options int) (w *Waitmsg, err error) {
- p, e := FindProcess(pid)
- if e != nil {
- return nil, e
+ ps = &ProcessState{
+ pid: waitmsg.Pid,
+ status: &waitmsg,
}
- defer p.Release()
- return p.Wait(options)
+ return ps, nil
}
// Release releases any resources associated with the Process.
return newProcess(pid, 0), nil
}
-func (w *Waitmsg) String() string {
- if w == nil {
+// ProcessState stores information about process as reported by Wait.
+type ProcessState struct {
+ pid int // The process's id.
+ status *syscall.Waitmsg // System-dependent status info.
+}
+
+// Pid returns the process id of the exited process.
+func (p *ProcessState) Pid() int {
+ return p.pid
+}
+
+// Exited returns whether the program has exited.
+func (p *ProcessState) Exited() bool {
+ return p.status.Exited()
+}
+
+// Success reports whether the program exited successfully,
+// such as with exit status 0 on Unix.
+func (p *ProcessState) Success() bool {
+ return p.status.ExitStatus() == 0
+}
+
+// Sys returns system-dependent exit information about
+// the process. Convert it to the appropriate underlying
+// type, such as *syscall.Waitmsg on Plan 9, to access its contents.
+func (p *ProcessState) Sys() interface{} {
+ return p.status
+}
+
+// SysUsage returns system-dependent resource usage information about
+// the exited process. Convert it to the appropriate underlying
+// type, such as *syscall.Waitmsg on Plan 9, to access its contents.
+func (p *ProcessState) SysUsage() interface{} {
+ return p.status
+}
+
+// UserTime returns the user CPU time of the exited process and its children.
+// It is always reported as 0 on Windows.
+func (p *ProcessState) UserTime() time.Duration {
+ return time.Duration(p.status.Time[0]) * time.Millisecond
+}
+
+// SystemTime returns the system CPU time of the exited process and its children.
+// It is always reported as 0 on Windows.
+func (p *ProcessState) SystemTime() time.Duration {
+ return time.Duration(p.status.Time[1]) * time.Millisecond
+}
+
+func (p *ProcessState) String() string {
+ if p == nil {
return "<nil>"
}
- return "exit status: " + w.Msg
+ return "exit status: " + p.status.Msg
}
return p.Signal(Kill)
}
-// TODO(rsc): Should os implement its own syscall.WaitStatus
-// wrapper with the methods, or is exposing the underlying one enough?
-//
-// TODO(rsc): Certainly need to have Rusage struct,
-// since syscall one might have different field types across
-// different OS.
-
-// Waitmsg stores the information about an exited process as reported by Wait.
-type Waitmsg struct {
- Pid int // The process's id.
- syscall.WaitStatus // System-dependent status info.
- Rusage *syscall.Rusage // System-dependent resource usage info.
+// ProcessState stores information about process as reported by Wait.
+type ProcessState struct {
+ pid int // The process's id.
+ status syscall.WaitStatus // System-dependent status info.
+ rusage *syscall.Rusage
}
-// Wait waits for process pid to exit or stop, and then returns a
-// Waitmsg describing its status and an error, if any. The options
-// (WNOHANG etc.) affect the behavior of the Wait call.
-// Wait is equivalent to calling FindProcess and then Wait
-// and Release on the result.
-func Wait(pid int, options int) (w *Waitmsg, err error) {
- p, e := FindProcess(pid)
- if e != nil {
- return nil, e
- }
- defer p.Release()
- return p.Wait(options)
+// Pid returns the process id of the exited process.
+func (p *ProcessState) Pid() int {
+ return p.pid
+}
+
+// Exited returns whether the program has exited.
+func (p *ProcessState) Exited() bool {
+ return p.status.Exited()
+}
+
+// Success reports whether the program exited successfully,
+// such as with exit status 0 on Unix.
+func (p *ProcessState) Success() bool {
+ return p.status.ExitStatus() == 0
+}
+
+// Sys returns system-dependent exit information about
+// the process. Convert it to the appropriate underlying
+// type, such as syscall.WaitStatus on Unix, to access its contents.
+func (p *ProcessState) Sys() interface{} {
+ return p.status
+}
+
+// SysUsage returns system-dependent resource usage information about
+// the exited process. Convert it to the appropriate underlying
+// type, such as *syscall.Rusage on Unix, to access its contents.
+func (p *ProcessState) SysUsage() interface{} {
+ return p.rusage
}
// Convert i to decimal string.
return string(b[bp:])
}
-func (w *Waitmsg) String() string {
- if w == nil {
+func (p *ProcessState) String() string {
+ if p == nil {
return "<nil>"
}
- // TODO(austin) Use signal names when possible?
+ status := p.Sys().(syscall.WaitStatus)
res := ""
switch {
- case w.Exited():
- res = "exit status " + itod(w.ExitStatus())
- case w.Signaled():
- res = "signal " + itod(int(w.Signal()))
- case w.Stopped():
- res = "stop signal " + itod(int(w.StopSignal()))
- if w.StopSignal() == syscall.SIGTRAP && w.TrapCause() != 0 {
- res += " (trap " + itod(w.TrapCause()) + ")"
+ case status.Exited():
+ res = "exit status " + itod(status.ExitStatus())
+ case status.Signaled():
+ res = "signal " + itod(int(status.Signal()))
+ case status.Stopped():
+ res = "stop signal " + itod(int(status.StopSignal()))
+ if status.StopSignal() == syscall.SIGTRAP && status.TrapCause() != 0 {
+ res += " (trap " + itod(status.TrapCause()) + ")"
}
- case w.Continued():
+ case status.Continued():
res = "continued"
}
- if w.CoreDump() {
+ if status.CoreDump() {
res += " (core dumped)"
}
return res
"errors"
"runtime"
"syscall"
+ "time"
)
-// Options for Wait.
-const (
- WNOHANG = syscall.WNOHANG // Don't wait if no process has exited.
- WSTOPPED = syscall.WSTOPPED // If set, status of stopped subprocesses is also reported.
- WUNTRACED = syscall.WUNTRACED // Usually an alias for WSTOPPED.
- WRUSAGE = 1 << 20 // Record resource usage.
-)
-
-// WRUSAGE must not be too high a bit, to avoid clashing with Linux's
-// WCLONE, WALL, and WNOTHREAD flags, which sit in the top few bits of
-// the options
-
// Wait waits for the Process to exit or stop, and then returns a
-// Waitmsg describing its status and an error, if any. The options
-// (WNOHANG etc.) affect the behavior of the Wait call.
-func (p *Process) Wait(options int) (w *Waitmsg, err error) {
+// ProcessState describing its status and an error, if any.
+func (p *Process) Wait() (ps *ProcessState, err error) {
if p.Pid == -1 {
- return nil, EINVAL
+ return nil, syscall.EINVAL
}
var status syscall.WaitStatus
- var rusage *syscall.Rusage
- if options&WRUSAGE != 0 {
- rusage = new(syscall.Rusage)
- options ^= WRUSAGE
- }
- pid1, e := syscall.Wait4(p.Pid, &status, options, rusage)
+ var rusage syscall.Rusage
+ pid1, e := syscall.Wait4(p.Pid, &status, 0, &rusage)
if e != nil {
return nil, NewSyscallError("wait", e)
}
- // With WNOHANG pid is 0 if child has not exited.
- if pid1 != 0 && options&WSTOPPED == 0 {
+ if pid1 != 0 {
p.done = true
}
- w = new(Waitmsg)
- w.Pid = pid1
- w.WaitStatus = status
- w.Rusage = rusage
- return w, nil
+ ps = &ProcessState{
+ pid: pid1,
+ status: status,
+ rusage: &rusage,
+ }
+ return ps, nil
}
// Signal sends a signal to the Process.
// NOOP for unix.
return newProcess(pid, 0), nil
}
+
+// UserTime returns the user CPU time of the exited process and its children.
+func (p *ProcessState) UserTime() time.Duration {
+ return time.Duration(p.rusage.Utime.Nano()) * time.Nanosecond
+}
+
+// SystemTime returns the system CPU time of the exited process and its children.
+func (p *ProcessState) SystemTime() time.Duration {
+ return time.Duration(p.rusage.Stime.Nano()) * time.Nanosecond
+}
"errors"
"runtime"
"syscall"
+ "time"
"unsafe"
)
// Wait waits for the Process to exit or stop, and then returns a
-// Waitmsg describing its status and an error, if any.
-func (p *Process) Wait(options int) (w *Waitmsg, err error) {
+// ProcessState describing its status and an error, if any.
+func (p *Process) Wait() (ps *ProcessState, err error) {
s, e := syscall.WaitForSingleObject(syscall.Handle(p.handle), syscall.INFINITE)
switch s {
case syscall.WAIT_OBJECT_0:
return nil, NewSyscallError("GetExitCodeProcess", e)
}
p.done = true
- return &Waitmsg{p.Pid, syscall.WaitStatus{Status: s, ExitCode: ec}, new(syscall.Rusage)}, nil
+ return &ProcessState{p.Pid, syscall.WaitStatus{Status: s, ExitCode: ec}, new(syscall.Rusage)}, nil
}
// Signal sends a signal to the Process.
// Release releases any resources associated with the Process.
func (p *Process) Release() error {
if p.handle == uintptr(syscall.InvalidHandle) {
- return EINVAL
+ return syscall.EINVAL
}
e := syscall.CloseHandle(syscall.Handle(p.handle))
if e != nil {
Args[i] = string(syscall.UTF16ToString((*v)[:]))
}
}
+
+// UserTime returns the user CPU time of the exited process and its children.
+// For now, it is always reported as 0 on Windows.
+func (p *ProcessState) UserTime() time.Duration {
+ return 0
+}
+
+// SystemTime returns the system CPU time of the exited process and its children.
+// For now, it is always reported as 0 on Windows.
+func (p *ProcessState) SystemTime() time.Duration {
+ return 0
+}
// Go-like; failing calls return values of type error rather than error numbers.
// Often, more information is available within the error. For example,
// if a call that takes a file name fails, such as Open or Stat, the error
-// will include failing file name when printed and will be of type *PathError,
-// which may be unpacked for more information.
+// will include the failing file name when printed and will be of type
+// *PathError, which may be unpacked for more information.
//
// The os interface is intended to be uniform across all operating systems.
// Features not generally available appear in the system-specific package syscall.
+//
+// Here is a simple example, opening a file and reading some of it.
+//
+// file, err := os.Open("file.go") // For read access.
+// if err != nil {
+// log.Fatal(err)
+// }
+//
+// If the open fails, the error string will be self-explanatory, like
+//
+// open file.go: no such file or directory
+//
+// The file's data can then be read into a slice of bytes. Read and
+// Write take their byte counts from the length of the artument slice.
+//
+// data := make([]byte, 100)
+// count, err := file.Read(data)
+// if err != nil {
+// log.Fatal(err)
+// }
+// fmt.Printf("read %d bytes: %q\n", count, data[:count])
+//
package os
import (
SEEK_END int = 2 // seek relative to the end
)
+// LinkError records an error during a link or symlink or rename
+// system call and the paths that caused it.
+type LinkError struct {
+ Op string
+ Old string
+ New string
+ Err error
+}
+
+func (e *LinkError) Error() string {
+ return e.Op + " " + e.Old + " " + e.New + ": " + e.Err.Error()
+}
+
// Read reads up to len(b) bytes from the File.
// It returns the number of bytes read and an error, if any.
// EOF is signaled by a zero count with err set to io.EOF.
func (f *File) Read(b []byte) (n int, err error) {
if f == nil {
- return 0, EINVAL
+ return 0, ErrInvalid
}
n, e := f.read(b)
if n < 0 {
// At end of file, that error is io.EOF.
func (f *File) ReadAt(b []byte, off int64) (n int, err error) {
if f == nil {
- return 0, EINVAL
+ return 0, ErrInvalid
}
for len(b) > 0 {
m, e := f.pread(b, off)
// Write returns a non-nil error when n != len(b).
func (f *File) Write(b []byte) (n int, err error) {
if f == nil {
- return 0, EINVAL
+ return 0, ErrInvalid
}
n, e := f.write(b)
if n < 0 {
// WriteAt returns a non-nil error when n != len(b).
func (f *File) WriteAt(b []byte, off int64) (n int, err error) {
if f == nil {
- return 0, EINVAL
+ return 0, ErrInvalid
}
for len(b) > 0 {
m, e := f.pwrite(b, off)
// an array of bytes.
func (f *File) WriteString(s string) (ret int, err error) {
if f == nil {
- return 0, EINVAL
+ return 0, ErrInvalid
}
return f.Write([]byte(s))
}
package os
import (
+ "errors"
"runtime"
"syscall"
"time"
)
+var ErrPlan9 = errors.New("unimplemented on Plan 9")
+
// File represents an open file descriptor.
type File struct {
*file
}
// Fd returns the integer Unix file descriptor referencing the open file.
-func (file *File) Fd() int {
- if file == nil {
- return -1
+func (f *File) Fd() uintptr {
+ if f == nil {
+ return ^(uintptr(0))
}
- return file.fd
+ return uintptr(f.fd)
}
// NewFile returns a new File with the given file descriptor and name.
-func NewFile(fd int, name string) *File {
- if fd < 0 {
+func NewFile(fd uintptr, name string) *File {
+ fdi := int(fd)
+ if fdi < 0 {
return nil
}
- f := &File{&file{fd: fd, name: name}}
+ f := &File{&file{fd: fdi, name: name}}
runtime.SetFinalizer(f.file, (*file).close)
return f
}
}
}
- return NewFile(fd, name), nil
+ return NewFile(uintptr(fd), name), nil
}
// Close closes the File, rendering it unusable for I/O.
func (file *file) close() error {
if file == nil || file.fd < 0 {
- return Ebadfd
+ return ErrInvalid
}
var err error
syscall.ForkLock.RLock()
// of recently written data to disk.
func (f *File) Sync() (err error) {
if f == nil {
- return EINVAL
+ return ErrInvalid
}
var d Dir
}
// Rename renames a file.
-// If there is an error, it will be of type *PathError.
func Rename(oldname, newname string) error {
var d Dir
d.Null()
}
syscall.ForkLock.RUnlock()
- return NewFile(p[0], "|0"), NewFile(p[1], "|1"), nil
+ return NewFile(uintptr(p[0]), "|0"), NewFile(uintptr(p[1]), "|1"), nil
}
// not supported on Plan 9
// Link creates a hard link.
+// If there is an error, it will be of type *LinkError.
func Link(oldname, newname string) error {
- return EPLAN9
+ return &LinkError{"link", oldname, newname, ErrPlan9}
}
+// Symlink creates newname as a symbolic link to oldname.
+// If there is an error, it will be of type *LinkError.
func Symlink(oldname, newname string) error {
- return EPLAN9
+ return &LinkError{"symlink", oldname, newname, ErrPlan9}
}
func Readlink(name string) (string, error) {
- return "", EPLAN9
+ return "", ErrPlan9
}
func Chown(name string, uid, gid int) error {
- return EPLAN9
+ return ErrPlan9
}
func Lchown(name string, uid, gid int) error {
- return EPLAN9
+ return ErrPlan9
}
func (f *File) Chown(uid, gid int) error {
- return EPLAN9
+ return ErrPlan9
}
// TempDir returns the default directory to use for temporary files.
}
}
-// LinkError records an error during a link or symlink or rename
-// system call and the paths that caused it.
-type LinkError struct {
- Op string
- Old string
- New string
- Err error
-}
-
-func (e *LinkError) Error() string {
- return e.Op + " " + e.Old + " " + e.New + ": " + e.Err.Error()
-}
-
// Link creates newname as a hard link to the oldname file.
+// If there is an error, it will be of type *LinkError.
func Link(oldname, newname string) error {
e := syscall.Link(oldname, newname)
if e != nil {
}
// Symlink creates newname as a symbolic link to oldname.
+// If there is an error, it will be of type *LinkError.
func Symlink(oldname, newname string) error {
e := syscall.Symlink(oldname, newname)
if e != nil {
// of recently written data to disk.
func (f *File) Sync() (err error) {
if f == nil {
- return EINVAL
+ return syscall.EINVAL
}
if e := syscall.Fsync(f.fd); e != nil {
return NewSyscallError("fsync", e)
func (file *file) close() error {
if file == nil || file.fd < 0 {
- return EINVAL
+ return syscall.EINVAL
}
var err error
if e := syscall.Close(file.fd); e != nil {
pwd = ""
for parent := ".."; ; parent = "../" + parent {
if len(parent) >= 1024 { // Sanity check
- return "", ENAMETOOLONG
+ return "", syscall.ENAMETOOLONG
}
fd, err := Open(parent)
if err != nil {
}
}
fd.Close()
- return "", ENOENT
+ return "", ErrNotExist
Found:
pd, err := fd.Stat()
"path/filepath"
"runtime"
"strings"
+ "syscall"
"testing"
"time"
)
t.Errorf("exec %q returned %q wanted %q",
strings.Join(append([]string{cmd}, args...), " "), output, expect)
}
- p.Wait(0)
+ p.Wait()
}
func TestStartProcess(t *testing.T) {
for i, tt := range tests {
off, err := f.Seek(tt.in, tt.whence)
if off != tt.out || err != nil {
- if e, ok := err.(*PathError); ok && e.Err == EINVAL && tt.out > 1<<32 {
+ if e, ok := err.(*PathError); ok && e.Err == syscall.EINVAL && tt.out > 1<<32 {
// Reiserfs rejects the big seeks.
// http://code.google.com/p/go/issues/detail?id=91
break
{
sfdir + "/no-such-file",
O_RDONLY,
- ENOENT,
+ syscall.ENOENT,
},
{
sfdir,
O_WRONLY,
- EISDIR,
+ syscall.EISDIR,
},
{
sfdir + "/" + sfname + "/no-such-file",
O_WRONLY,
- ENOTDIR,
+ syscall.ENOTDIR,
},
}
var b bytes.Buffer
io.Copy(&b, r)
- _, err = p.Wait(0)
+ _, err = p.Wait()
if err != nil {
t.Fatalf("run hostname Wait: %v", err)
}
}
func TestStatDirWithTrailingSlash(t *testing.T) {
- // Create new dir, in _test so it will get
- // cleaned up by make if not by us.
- path := "_test/_TestStatDirWithSlash_"
- err := MkdirAll(path, 0777)
+ // Create new temporary directory and arrange to clean it up.
+ path, err := ioutil.TempDir("", "/_TestStatDirWithSlash_")
if err != nil {
- t.Fatalf("MkdirAll %q: %s", path, err)
+ t.Fatalf("TempDir: %s", err)
}
defer RemoveAll(path)
// Stat of path should succeed.
_, err = Stat(path)
if err != nil {
- t.Fatal("stat failed:", err)
+ t.Fatalf("stat %s failed: %s", path, err)
}
// Stat of path+"/" should succeed too.
- _, err = Stat(path + "/")
+ path += "/"
+ _, err = Stat(path)
if err != nil {
- t.Fatal("stat failed:", err)
+ t.Fatalf("stat %s failed: %s", path, err)
}
}
-func TestNilWaitmsgString(t *testing.T) {
- var w *Waitmsg
- s := w.String()
+func TestNilProcessStateString(t *testing.T) {
+ var ps *ProcessState
+ s := ps.String()
if s != "<nil>" {
- t.Errorf("(*Waitmsg)(nil).String() = %q, want %q", s, "<nil>")
+ t.Errorf("(*ProcessState)(nil).String() = %q, want %q", s, "<nil>")
}
}
package os
-import "io"
+import (
+ "io"
+ "syscall"
+)
// MkdirAll creates a directory named path,
// along with any necessary parents, and returns nil,
if dir.IsDir() {
return nil
}
- return &PathError{"mkdir", path, ENOTDIR}
+ return &PathError{"mkdir", path, syscall.ENOTDIR}
}
// Doesn't already exist; make sure parent does.
// Otherwise, is this a directory we need to recurse into?
dir, serr := Lstat(path)
if serr != nil {
- if serr, ok := serr.(*PathError); ok && (serr.Err == ENOENT || serr.Err == ENOTDIR) {
+ if serr, ok := serr.(*PathError); ok && (IsNotExist(serr.Err) || serr.Err == syscall.ENOTDIR) {
return nil
}
return serr
. "os"
"path/filepath"
"runtime"
+ "syscall"
"testing"
)
func TestMkdirAll(t *testing.T) {
- // Create new dir, in _test so it will get
- // cleaned up by make if not by us.
- path := "_test/_TestMkdirAll_/dir/./dir2"
+ tmpDir := TempDir()
+ path := tmpDir + "/_TestMkdirAll_/dir/./dir2"
err := MkdirAll(path, 0777)
if err != nil {
t.Fatalf("MkdirAll %q: %s", path, err)
}
- defer RemoveAll("_test/_TestMkdirAll_")
+ defer RemoveAll(tmpDir + "/_TestMkdirAll_")
// Already exists, should succeed.
err = MkdirAll(path, 0777)
}
if runtime.GOOS == "windows" {
- path := `_test\_TestMkdirAll_\dir\.\dir2\`
+ path := tmpDir + `\_TestMkdirAll_\dir\.\dir2\`
err := MkdirAll(path, 0777)
if err != nil {
t.Fatalf("MkdirAll %q: %s", path, err)
}
func TestRemoveAll(t *testing.T) {
+ tmpDir := TempDir()
// Work directory.
- path := "_test/_TestRemoveAll_"
+ path := tmpDir + "/_TestRemoveAll_"
fpath := path + "/file"
dpath := path + "/dir"
return
}
- err := Mkdir("_test/dir", 0755)
+ tmpDir := TempDir()
+ dir := tmpDir + "/dir"
+ err := Mkdir(dir, 0755)
if err != nil {
- t.Fatal(`Mkdir "_test/dir":`, err)
+ t.Fatalf("Mkdir %s: %s", dir, err)
}
- defer RemoveAll("_test/dir")
+ defer RemoveAll(dir)
- err = Symlink("dir", "_test/link")
+ link := tmpDir + "/link"
+ err = Symlink("dir", link)
if err != nil {
- t.Fatal(`Symlink "dir", "_test/link":`, err)
+ t.Fatalf("Symlink %s: %s", link, err)
}
- defer RemoveAll("_test/link")
+ defer RemoveAll(link)
- path := "_test/link/foo"
+ path := link + "/foo"
err = MkdirAll(path, 0755)
if err != nil {
t.Errorf("MkdirAll %q: %s", path, err)
if err != nil {
pathErr, ok := err.(*PathError)
// common for users not to be able to write to /
- if ok && pathErr.Err == EACCES {
+ if ok && pathErr.Err == syscall.EACCES {
return
}
t.Fatalf(`MkdirAll "/_go_os_test/dir": %v`, err)
--- /dev/null
+// 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.
+
+// +build plan9
+
+package signal
+
+import "os"
+
+func enableSignal(sig os.Signal) {}
return nil, &PathError{"stat", name, err}
}
if n < syscall.STATFIXLEN {
- return nil, &PathError{"stat", name, Eshortstat}
+ return nil, &PathError{"stat", name, errShortStat}
}
// Pull the real size out of the stat message.
return
}
}
- return nil, &PathError{"stat", name, Ebadstat}
+ return nil, &PathError{"stat", name, errBadStat}
}
// Stat returns a FileInfo structure describing the named file.
"path"
)
-// b
func ExampleBase() {
fmt.Println(path.Base("/a/b"))
+ // Output: b
}
-// Clean("a/c") = "a/c"
-// Clean("a//c") = "a/c"
-// Clean("a/c/.") = "a/c"
-// Clean("a/c/b/..") = "a/c"
-// Clean("/../a/c") = "/a/c"
-// Clean("/../a/b/../././/c") = "/a/c"
func ExampleClean() {
paths := []string{
"a/c",
for _, p := range paths {
fmt.Printf("Clean(%q) = %q\n", p, path.Clean(p))
}
+
+ // Output:
+ // Clean("a/c") = "a/c"
+ // Clean("a//c") = "a/c"
+ // Clean("a/c/.") = "a/c"
+ // Clean("a/c/b/..") = "a/c"
+ // Clean("/../a/c") = "/a/c"
+ // Clean("/../a/b/../././/c") = "/a/c"
}
-// /a/b
func ExampleDir() {
fmt.Println(path.Dir("/a/b/c"))
+ // Output: /a/b
}
-// .css
func ExampleExt() {
fmt.Println(path.Ext("/a/b/c/bar.css"))
+ // Output: .css
}
-// true
func ExampleIsAbs() {
fmt.Println(path.IsAbs("/dev/null"))
+ // Output: true
}
-// a/b/c
func ExampleJoin() {
fmt.Println(path.Join("a", "b", "c"))
+ // Output: a/b/c
}
-// static/ myfile.css
func ExampleSplit() {
fmt.Println(path.Split("static/myfile.css"))
+ // Output: static/ myfile.css
}
*/
"unicode/utf8"
)
+// ErrBadPattern indicates a globbing pattern was malformed.
var ErrBadPattern = errors.New("syntax error in pattern")
// Match returns true if name matches the shell file name pattern.
// lo '-' hi matches character c for lo <= c <= hi
//
// Match requires pattern to match all of name, not just a substring.
-// The only possible error return occurs when the pattern is malformed.
+// The only possible returned error is ErrBadPattern, when pattern
+// is malformed.
//
func Match(pattern, name string) (matched bool, err error) {
Pattern:
// if there is no matching file. The syntax of patterns is the same
// as in Match. The pattern may describe hierarchical names such as
// /usr/*/bin/ed (assuming the Separator is '/').
-// The only possible error return occurs when the pattern is malformed.
//
func Glob(pattern string) (matches []string, err error) {
if !hasMeta(pattern) {
// and appends them to matches. If the directory cannot be
// opened, it returns the existing matches. New matches are
// added in lexicographical order.
-// The only possible error return occurs when the pattern is malformed.
func glob(dir, pattern string, matches []string) (m []string, e error) {
m = matches
fi, err := os.Stat(dir)
// returns the string ".".
//
// See also Rob Pike, ``Lexical File Names in Plan 9 or
-// Getting Dot-Dot right,''
+// Getting Dot-Dot Right,''
// http://plan9.bell-labs.com/sys/doc/lexnames.html
func Clean(path string) string {
vol := VolumeName(path)
}
// ToSlash returns the result of replacing each separator character
-// in path with a slash ('/') character.
+// in path with a slash ('/') character. Multiple separators are
+// replaced by multiple slashes.
func ToSlash(path string) string {
if Separator == '/' {
return path
}
// FromSlash returns the result of replacing each slash ('/') character
-// in path with a separator character.
+// in path with a separator character. Multiple slashes are replaced
+// by multiple separators.
func FromSlash(path string) string {
if Separator == '/' {
return path
return strings.Replace(path, "/", string(Separator), -1)
}
-// SplitList splits a list of paths joined by the OS-specific ListSeparator.
+// SplitList splits a list of paths joined by the OS-specific ListSeparator,
+// usually found in PATH or GOPATH environment variables.
func SplitList(path string) []string {
if path == "" {
return []string{}
}
// Join joins any number of path elements into a single path, adding
-// a Separator if necessary. All empty strings are ignored.
+// a Separator if necessary. The result is Cleaned, in particular
+// all empty strings are ignored.
func Join(elem ...string) string {
for i, e := range elem {
if e != "" {
// EvalSymlinks returns the path name after the evaluation of any symbolic
// links.
-// If path is relative it will be evaluated relative to the current directory.
+// If path is relative the result will be relative to the current directory,
+// unless one of the components is an absolute symbolic link.
func EvalSymlinks(path string) (string, error) {
if runtime.GOOS == "windows" {
// Symlinks are not supported under windows.
return path
}
-// Dir returns the all but the last element of path, typically the path's directory.
+// Dir returns all but the last element of path, typically the path's directory.
// Trailing path separators are removed before processing.
// If the path is empty, Dir returns ".".
// If the path consists entirely of separators, Dir returns a single separator.
{"test/dir/link3", "../../"},
{"test/link1", "../test"},
{"test/link2", "dir"},
+ {"test/linkabs", "/"},
}
var EvalSymlinksTests = []EvalSymlinksTest{
{"test/link2/..", "test"},
{"test/dir/link3", "."},
{"test/link2/link3/test", "test"},
+ {"test/linkabs", "/"},
}
var EvalSymlinksAbsWindowsTests = []EvalSymlinksTest{
for _, d := range tests {
path := simpleJoin(tmpDir, d.path)
dest := simpleJoin(tmpDir, d.dest)
+ if filepath.IsAbs(d.dest) {
+ dest = d.dest
+ }
if p, err := filepath.EvalSymlinks(path); err != nil {
t.Errorf("EvalSymlinks(%q) error: %v", d.path, err)
} else if filepath.Clean(p) != filepath.Clean(dest) {
"unicode/utf8"
)
+// ErrBadPattern indicates a globbing pattern was malformed.
var ErrBadPattern = errors.New("syntax error in pattern")
// Match returns true if name matches the shell file name pattern.
// lo '-' hi matches character c for lo <= c <= hi
//
// Match requires pattern to match all of name, not just a substring.
-// The only possible error return is when pattern is malformed.
+// The only possible returned error is ErrBadPattern, when pattern
+// is malformed.
//
func Match(pattern, name string) (matched bool, err error) {
Pattern:
// license that can be found in the LICENSE file.
// Package path implements utility routines for manipulating slash-separated
-// filename paths.
+// paths.
package path
import (
// returns the string ".".
//
// See also Rob Pike, ``Lexical File Names in Plan 9 or
-// Getting Dot-Dot right,''
+// Getting Dot-Dot Right,''
// http://plan9.bell-labs.com/sys/doc/lexnames.html
func Clean(path string) string {
if path == "" {
return string(buf[0:w])
}
-// Split splits path immediately following the final path separator,
+// Split splits path immediately following the final slash.
// separating it into a directory and file name component.
-// If there is no separator in path, Split returns an empty dir and
+// If there is no slash path, Split returns an empty dir and
// file set to path.
+// The returned values have the property that path = dir+file.
func Split(path string) (dir, file string) {
i := strings.LastIndex(path, "/")
return path[:i+1], path[i+1:]
}
// Join joins any number of path elements into a single path, adding a
-// separating slash if necessary. All empty strings are ignored.
+// separating slash if necessary. The result is Cleaned; in particular,
+// all empty strings are ignored.
func Join(elem ...string) string {
for i, e := range elem {
if e != "" {
return len(path) > 0 && path[0] == '/'
}
-// Dir returns the all but the last element of path, typically the path's directory.
-// Trailing path separators are removed before processing.
+// Dir returns all but the last element of path, typically the path's directory.
+// The path is Cleaned and trailing slashes are removed before processing.
// If the path is empty, Dir returns ".".
-// If the path consists entirely of separators, Dir returns a single separator.
-// The returned path does not end in a separator unless it is the root directory.
+// If the path consists entirely of slashes followed by non-slash bytes, Dir
+// returns a single slash. In any other case, the returned path does not end in a
+// slash.
func Dir(path string) string {
dir, _ := Split(path)
dir = Clean(dir)
// NumCPU returns the number of logical CPUs on the local machine.
func NumCPU() int
-// Cgocalls returns the number of cgo calls made by the current process.
-func Cgocalls() int64
+// NumCgoCall returns the number of cgo calls made by the current process.
+func NumCgoCall() int64
-// Goroutines returns the number of goroutines that currently exist.
-func Goroutines() int32
-
-// Alloc allocates a block of the given size.
-// FOR TESTING AND DEBUGGING ONLY.
-func Alloc(uintptr) *byte
-
-// Free frees the block starting at the given pointer.
-// FOR TESTING AND DEBUGGING ONLY.
-func Free(*byte)
-
-// Lookup returns the base and size of the block containing the given pointer.
-// FOR TESTING AND DEBUGGING ONLY.
-func Lookup(*byte) (*byte, uintptr)
+// NumGoroutine returns the number of goroutines that currently exist.
+func NumGoroutine() int
// MemProfileRate controls the fraction of memory allocations
// that are recorded and reported in the memory profile.
// of calling MemProfile directly.
func MemProfile(p []MemProfileRecord, inuseZero bool) (n int, ok bool)
-// A ThreadProfileRecord describes the execution stack that
-// caused a new thread to be created.
-type ThreadProfileRecord struct {
+// A StackRecord describes a single execution stack.
+type StackRecord struct {
Stack0 [32]uintptr // stack trace for this record; ends at first 0 entry
}
// Stack returns the stack trace associated with the record,
// a prefix of r.Stack0.
-func (r *ThreadProfileRecord) Stack() []uintptr {
+func (r *StackRecord) Stack() []uintptr {
for i, v := range r.Stack0 {
if v == 0 {
return r.Stack0[0:i]
return r.Stack0[0:]
}
-// ThreadProfile returns n, the number of records in the current thread profile.
-// If len(p) >= n, ThreadProfile copies the profile into p and returns n, true.
-// If len(p) < n, ThreadProfile does not change p and returns n, false.
+// ThreadCreateProfile returns n, the number of records in the thread creation profile.
+// If len(p) >= n, ThreadCreateProfile copies the profile into p and returns n, true.
+// If len(p) < n, ThreadCreateProfile does not change p and returns n, false.
//
// Most clients should use the runtime/pprof package instead
-// of calling ThreadProfile directly.
-func ThreadProfile(p []ThreadProfileRecord) (n int, ok bool)
+// of calling ThreadCreateProfile directly.
+func ThreadCreateProfile(p []StackRecord) (n int, ok bool)
+
+// GoroutineProfile returns n, the number of records in the active goroutine stack profile.
+// If len(p) >= n, GoroutineProfile copies the profile into p and returns n, true.
+// If len(p) < n, GoroutineProfile does not change p and returns n, false.
+//
+// Most clients should use the runtime/pprof package instead
+// of calling GoroutineProfile directly.
+func GoroutineProfile(p []StackRecord) (n int, ok bool)
// CPUProfile returns the next chunk of binary CPU profiling stack trace data,
// blocking until data is available. If profiling is turned off and all the profile
// the testing package's -test.cpuprofile flag instead of calling
// SetCPUProfileRate directly.
func SetCPUProfileRate(hz int)
+
+// Stack formats a stack trace of the calling goroutine into buf
+// and returns the number of bytes written to buf.
+// If all is true, Stack formats stack traces of all other goroutines
+// into buf after the trace for the current goroutine.
+func Stack(buf []byte, all bool) int
// mid returns the current os thread (m) id.
func mid() uint32
-// Semacquire waits until *s > 0 and then atomically decrements it.
-// It is intended as a simple sleep primitive for use by the synchronization
-// library and should not be used directly.
-func Semacquire(s *uint32)
-
-// Semrelease atomically increments *s and notifies a waiting goroutine
-// if one is blocked in Semacquire.
-// It is intended as a simple wakeup primitive for use by the synchronization
-// library and should not be used directly.
-func Semrelease(s *uint32)
-
// SetFinalizer sets the finalizer associated with x to f.
// When the garbage collector finds an unreachable block
// with an associated finalizer, it clears the association and runs
return theVersion
}
-// GOOS is the Go tree's operating system target:
+// GOOS is the running program's operating system target:
// one of darwin, freebsd, linux, and so on.
const GOOS string = theGoos
-// GOARCH is the Go tree's architecture target:
+// GOARCH is the running program's architecture target:
// 386, amd64, or arm.
const GOARCH string = theGoarch
--- /dev/null
+// Copyright 2009 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.
+
+// +build ignore
+
+// trivial malloc test
+
+package main
+
+import (
+ "flag"
+ "fmt"
+ "runtime"
+)
+
+var chatty = flag.Bool("v", false, "chatty")
+
+func main() {
+ memstats := new(runtime.MemStats)
+ runtime.Free(runtime.Alloc(1))
+ runtime.ReadMemStats(memstats)
+ if *chatty {
+ fmt.Printf("%+v %v\n", memstats, uint64(0))
+ }
+}
--- /dev/null
+// Copyright 2009 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.
+
+// +build ignore
+
+// Random malloc test.
+
+package main
+
+import (
+ "flag"
+ "math/rand"
+ "runtime"
+ "unsafe"
+)
+
+var chatty = flag.Bool("v", false, "chatty")
+
+var footprint uint64
+var allocated uint64
+
+func bigger() {
+ memstats := new(runtime.MemStats)
+ runtime.ReadMemStats(memstats)
+ if f := memstats.Sys; footprint < f {
+ footprint = f
+ if *chatty {
+ println("Footprint", footprint, " for ", allocated)
+ }
+ if footprint > 1e9 {
+ println("too big")
+ panic("fail")
+ }
+ }
+}
+
+// Prime the data structures by allocating one of
+// each block in order. After this, there should be
+// little reason to ask for more memory from the OS.
+func prime() {
+ for i := 0; i < 16; i++ {
+ b := runtime.Alloc(1 << uint(i))
+ runtime.Free(b)
+ }
+ for i := uintptr(0); i < 256; i++ {
+ b := runtime.Alloc(i << 12)
+ runtime.Free(b)
+ }
+}
+
+func memset(b *byte, c byte, n uintptr) {
+ np := uintptr(n)
+ for i := uintptr(0); i < np; i++ {
+ *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(b)) + i)) = c
+ }
+}
+
+func main() {
+ flag.Parse()
+ // prime()
+ var blocks [1]struct {
+ base *byte
+ siz uintptr
+ }
+ for i := 0; i < 1<<10; i++ {
+ if i%(1<<10) == 0 && *chatty {
+ println(i)
+ }
+ b := rand.Int() % len(blocks)
+ if blocks[b].base != nil {
+ // println("Free", blocks[b].siz, blocks[b].base)
+ runtime.Free(blocks[b].base)
+ blocks[b].base = nil
+ allocated -= uint64(blocks[b].siz)
+ continue
+ }
+ siz := uintptr(rand.Int() >> (11 + rand.Uint32()%20))
+ base := runtime.Alloc(siz)
+ // ptr := uintptr(syscall.BytePtr(base))+uintptr(siz/2)
+ // obj, size, ref, ok := allocator.find(ptr)
+ // if obj != base || *ref != 0 || !ok {
+ // println("find", siz, obj, ref, ok)
+ // panic("fail")
+ // }
+ blocks[b].base = base
+ blocks[b].siz = siz
+ allocated += uint64(siz)
+ // println("Alloc", siz, base)
+ memset(base, 0xbb, siz)
+ bigger()
+ }
+}
--- /dev/null
+// Copyright 2009 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.
+
+// Repeated malloc test.
+
+// +build ignore
+
+package main
+
+import (
+ "flag"
+ "runtime"
+)
+
+var chatty = flag.Bool("v", false, "chatty")
+
+var oldsys uint64
+var memstats runtime.MemStats
+
+func bigger() {
+ st := &memstats
+ runtime.ReadMemStats(st)
+ if oldsys < st.Sys {
+ oldsys = st.Sys
+ if *chatty {
+ println(st.Sys, " system bytes for ", st.Alloc, " Go bytes")
+ }
+ if st.Sys > 1e9 {
+ println("too big")
+ panic("fail")
+ }
+ }
+}
+
+func main() {
+ runtime.GC() // clean up garbage from init
+ runtime.ReadMemStats(&memstats) // first call can do some allocations
+ runtime.MemProfileRate = 0 // disable profiler
+ stacks := memstats.Alloc // ignore stacks
+ flag.Parse()
+ for i := 0; i < 1<<7; i++ {
+ for j := 1; j <= 1<<22; j <<= 1 {
+ if i == 0 && *chatty {
+ println("First alloc:", j)
+ }
+ if a := memstats.Alloc - stacks; a != 0 {
+ println("no allocations but stats report", a, "bytes allocated")
+ panic("fail")
+ }
+ b := runtime.Alloc(uintptr(j))
+ runtime.ReadMemStats(&memstats)
+ during := memstats.Alloc - stacks
+ runtime.Free(b)
+ runtime.ReadMemStats(&memstats)
+ if a := memstats.Alloc - stacks; a != 0 {
+ println("allocated ", j, ": wrong stats: during=", during, " after=", a, " (want 0)")
+ panic("fail")
+ }
+ bigger()
+ }
+ if i%(1<<10) == 0 && *chatty {
+ println(i)
+ }
+ if i == 0 {
+ if *chatty {
+ println("Primed", i)
+ }
+ // runtime.frozen = true
+ }
+ }
+}
--- /dev/null
+// Copyright 2009 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.
+
+// +build ignore
+
+// Repeated malloc test.
+
+package main
+
+import (
+ "flag"
+ "fmt"
+ "runtime"
+ "strconv"
+)
+
+var chatty = flag.Bool("v", false, "chatty")
+var reverse = flag.Bool("r", false, "reverse")
+var longtest = flag.Bool("l", false, "long test")
+
+var b []*byte
+var stats = new(runtime.MemStats)
+
+func OkAmount(size, n uintptr) bool {
+ if n < size {
+ return false
+ }
+ if size < 16*8 {
+ if n > size+16 {
+ return false
+ }
+ } else {
+ if n > size*9/8 {
+ return false
+ }
+ }
+ return true
+}
+
+func AllocAndFree(size, count int) {
+ if *chatty {
+ fmt.Printf("size=%d count=%d ...\n", size, count)
+ }
+ runtime.ReadMemStats(stats)
+ n1 := stats.Alloc
+ for i := 0; i < count; i++ {
+ b[i] = runtime.Alloc(uintptr(size))
+ base, n := runtime.Lookup(b[i])
+ if base != b[i] || !OkAmount(uintptr(size), n) {
+ println("lookup failed: got", base, n, "for", b[i])
+ panic("fail")
+ }
+ runtime.ReadMemStats(stats)
+ if stats.Sys > 1e9 {
+ println("too much memory allocated")
+ panic("fail")
+ }
+ }
+ runtime.ReadMemStats(stats)
+ n2 := stats.Alloc
+ if *chatty {
+ fmt.Printf("size=%d count=%d stats=%+v\n", size, count, *stats)
+ }
+ n3 := stats.Alloc
+ for j := 0; j < count; j++ {
+ i := j
+ if *reverse {
+ i = count - 1 - j
+ }
+ alloc := uintptr(stats.Alloc)
+ base, n := runtime.Lookup(b[i])
+ if base != b[i] || !OkAmount(uintptr(size), n) {
+ println("lookup failed: got", base, n, "for", b[i])
+ panic("fail")
+ }
+ runtime.Free(b[i])
+ runtime.ReadMemStats(stats)
+ if stats.Alloc != uint64(alloc-n) {
+ println("free alloc got", stats.Alloc, "expected", alloc-n, "after free of", n)
+ panic("fail")
+ }
+ if stats.Sys > 1e9 {
+ println("too much memory allocated")
+ panic("fail")
+ }
+ }
+ runtime.ReadMemStats(stats)
+ n4 := stats.Alloc
+
+ if *chatty {
+ fmt.Printf("size=%d count=%d stats=%+v\n", size, count, *stats)
+ }
+ if n2-n1 != n3-n4 {
+ println("wrong alloc count: ", n2-n1, n3-n4)
+ panic("fail")
+ }
+}
+
+func atoi(s string) int {
+ i, _ := strconv.Atoi(s)
+ return i
+}
+
+func main() {
+ runtime.MemProfileRate = 0 // disable profiler
+ flag.Parse()
+ b = make([]*byte, 10000)
+ if flag.NArg() > 0 {
+ AllocAndFree(atoi(flag.Arg(0)), atoi(flag.Arg(1)))
+ return
+ }
+ maxb := 1 << 22
+ if !*longtest {
+ maxb = 1 << 19
+ }
+ for j := 1; j <= maxb; j <<= 1 {
+ n := len(b)
+ max := uintptr(1 << 28)
+ if !*longtest {
+ max = uintptr(maxb)
+ }
+ if uintptr(j)*uintptr(n) > max {
+ n = int(max / uintptr(j))
+ }
+ if n < 10 {
+ n = 10
+ }
+ for m := 1; m <= n; {
+ AllocAndFree(j, m)
+ if m == n {
+ break
+ }
+ m = 5 * m / 4
+ if m < 4 {
+ m++
+ }
+ if m > n {
+ m = n
+ }
+ }
+ }
+}
Frees uint64 // number of frees
// Main allocation heap statistics.
- HeapAlloc uint64 // bytes allocated and still in use
- HeapSys uint64 // bytes obtained from system
- HeapIdle uint64 // bytes in idle spans
- HeapInuse uint64 // bytes in non-idle span
- HeapObjects uint64 // total number of allocated objects
+ HeapAlloc uint64 // bytes allocated and still in use
+ HeapSys uint64 // bytes obtained from system
+ HeapIdle uint64 // bytes in idle spans
+ HeapInuse uint64 // bytes in non-idle span
+ HeapReleased uint64 // bytes released to the OS
+ HeapObjects uint64 // total number of allocated objects
// Low-level fixed-size structure allocator statistics.
// Inuse is bytes used now.
BuckHashSys uint64 // profiling bucket hash table
// Garbage collector statistics.
- NextGC uint64
+ NextGC uint64 // next run in HeapAlloc time (bytes)
+ LastGC uint64 // last run in absolute time (ns)
PauseTotalNs uint64
PauseNs [256]uint64 // most recent GC pause times
NumGC uint32
import (
"bufio"
+ "bytes"
"fmt"
"io"
"runtime"
+ "sort"
+ "strings"
"sync"
+ "text/tabwriter"
)
// BUG(rsc): CPU profiling is broken on OS X, due to an Apple kernel bug.
// For details, see http://code.google.com/p/go/source/detail?r=35b716c94225.
-// WriteHeapProfile writes a pprof-formatted heap profile to w.
-// If a write to w returns an error, WriteHeapProfile returns that error.
-// Otherwise, WriteHeapProfile returns nil.
+// A Profile is a collection of stack traces showing the call sequences
+// that led to instances of a particular event, such as allocation.
+// Packages can create and maintain their own profiles; the most common
+// use is for tracking resources that must be explicitly closed, such as files
+// or network connections.
+//
+// A Profile's methods can be called from multiple goroutines simultaneously.
+//
+// Each Profile has a unique name. A few profiles are predefined:
+//
+// goroutine - stack traces of all current goroutines
+// heap - a sampling of all heap allocations
+// threadcreate - stack traces that led to the creation of new OS threads
+//
+// These predefine profiles maintain themselves and panic on an explicit
+// Add or Remove method call.
+//
+// The CPU profile is not available as a Profile. It has a special API,
+// the StartCPUProfile and StopCPUProfile functions, because it streams
+// output to a writer during profiling.
+//
+type Profile struct {
+ name string
+ mu sync.Mutex
+ m map[interface{}][]uintptr
+ count func() int
+ write func(io.Writer, int) error
+}
+
+// profiles records all registered profiles.
+var profiles struct {
+ mu sync.Mutex
+ m map[string]*Profile
+}
+
+var goroutineProfile = &Profile{
+ name: "goroutine",
+ count: countGoroutine,
+ write: writeGoroutine,
+}
+
+var threadcreateProfile = &Profile{
+ name: "threadcreate",
+ count: countThreadCreate,
+ write: writeThreadCreate,
+}
+
+var heapProfile = &Profile{
+ name: "heap",
+ count: countHeap,
+ write: writeHeap,
+}
+
+func lockProfiles() {
+ profiles.mu.Lock()
+ if profiles.m == nil {
+ // Initial built-in profiles.
+ profiles.m = map[string]*Profile{
+ "goroutine": goroutineProfile,
+ "threadcreate": threadcreateProfile,
+ "heap": heapProfile,
+ }
+ }
+}
+
+func unlockProfiles() {
+ profiles.mu.Unlock()
+}
+
+// NewProfile creates a new profile with the given name.
+// If a profile with that name already exists, NewProfile panics.
+// The convention is to use a 'import/path.' prefix to create
+// separate name spaces for each package.
+func NewProfile(name string) *Profile {
+ lockProfiles()
+ defer unlockProfiles()
+ if name == "" {
+ panic("pprof: NewProfile with empty name")
+ }
+ if profiles.m[name] != nil {
+ panic("pprof: NewProfile name already in use: " + name)
+ }
+ p := &Profile{
+ name: name,
+ m: map[interface{}][]uintptr{},
+ }
+ profiles.m[name] = p
+ return p
+}
+
+// Lookup returns the profile with the given name, or nil if no such profile exists.
+func Lookup(name string) *Profile {
+ lockProfiles()
+ defer unlockProfiles()
+ return profiles.m[name]
+}
+
+// Profiles returns a slice of all the known profiles, sorted by name.
+func Profiles() []*Profile {
+ lockProfiles()
+ defer unlockProfiles()
+
+ var all []*Profile
+ for _, p := range profiles.m {
+ all = append(all, p)
+ }
+
+ sort.Sort(byName(all))
+ return all
+}
+
+type byName []*Profile
+
+func (x byName) Len() int { return len(x) }
+func (x byName) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
+func (x byName) Less(i, j int) bool { return x[i].name < x[j].name }
+
+// Name returns this profile's name, which can be passed to Lookup to reobtain the profile.
+func (p *Profile) Name() string {
+ return p.name
+}
+
+// Count returns the number of execution stacks currently in the profile.
+func (p *Profile) Count() int {
+ p.mu.Lock()
+ defer p.mu.Unlock()
+ if p.count != nil {
+ return p.count()
+ }
+ return len(p.m)
+}
+
+// Add adds the current execution stack to the profile, associated with value.
+// Add stores value in an internal map, so value must be suitable for use as
+// a map key and will not be garbage collected until the corresponding
+// call to Remove. Add panics if the profile already contains a stack for value.
+//
+// The skip parameter has the same meaning as runtime.Caller's skip
+// and controls where the stack trace begins. Passing skip=0 begins the
+// trace in the function calling Add. For example, given this
+// execution stack:
+//
+// Add
+// called from rpc.NewClient
+// called from mypkg.Run
+// called from main.main
+//
+// Passing skip=0 begins the stack trace at the call to Add inside rpc.NewClient.
+// Passing skip=1 begins the stack trace at the call to NewClient inside mypkg.Run.
+//
+func (p *Profile) Add(value interface{}, skip int) {
+ if p.name == "" {
+ panic("pprof: use of uninitialized Profile")
+ }
+ if p.write != nil {
+ panic("pprof: Add called on built-in Profile " + p.name)
+ }
+
+ stk := make([]uintptr, 32)
+ n := runtime.Callers(skip+1, stk[:])
+
+ p.mu.Lock()
+ defer p.mu.Unlock()
+ if p.m[value] != nil {
+ panic("pprof: Profile.Add of duplicate value")
+ }
+ p.m[value] = stk[:n]
+}
+
+// Remove removes the execution stack associated with value from the profile.
+// It is a no-op if the value is not in the profile.
+func (p *Profile) Remove(value interface{}) {
+ p.mu.Lock()
+ defer p.mu.Unlock()
+ delete(p.m, value)
+}
+
+// WriteTo writes a pprof-formatted snapshot of the profile to w.
+// If a write to w returns an error, WriteTo returns that error.
+// Otherwise, WriteTo returns nil.
+//
+// The debug parameter enables additional output.
+// Passing debug=0 prints only the hexadecimal addresses that pprof needs.
+// Passing debug=1 adds comments translating addresses to function names
+// and line numbers, so that a programmer can read the profile without tools.
+//
+// The predefined profiles may assign meaning to other debug values;
+// for example, when printing the "goroutine" profile, debug=2 means to
+// print the goroutine stacks in the same form that a Go program uses
+// when dying due to an unrecovered panic.
+func (p *Profile) WriteTo(w io.Writer, debug int) error {
+ if p.name == "" {
+ panic("pprof: use of zero Profile")
+ }
+ if p.write != nil {
+ return p.write(w, debug)
+ }
+
+ // Obtain consistent snapshot under lock; then process without lock.
+ var all [][]uintptr
+ p.mu.Lock()
+ for _, stk := range p.m {
+ all = append(all, stk)
+ }
+ p.mu.Unlock()
+
+ // Map order is non-deterministic; make output deterministic.
+ sort.Sort(stackProfile(all))
+
+ return printCountProfile(w, debug, p.name, stackProfile(all))
+}
+
+type stackProfile [][]uintptr
+
+func (x stackProfile) Len() int { return len(x) }
+func (x stackProfile) Stack(i int) []uintptr { return x[i] }
+func (x stackProfile) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
+func (x stackProfile) Less(i, j int) bool {
+ t, u := x[i], x[j]
+ for k := 0; k < len(t) && k < len(u); k++ {
+ if t[k] != u[k] {
+ return t[k] < u[k]
+ }
+ }
+ return len(t) < len(u)
+}
+
+// A countProfile is a set of stack traces to be printed as counts
+// grouped by stack trace. There are multiple implementations:
+// all that matters is that we can find out how many traces there are
+// and obtain each trace in turn.
+type countProfile interface {
+ Len() int
+ Stack(i int) []uintptr
+}
+
+// printCountProfile prints a countProfile at the specified debug level.
+func printCountProfile(w io.Writer, debug int, name string, p countProfile) error {
+ b := bufio.NewWriter(w)
+ var tw *tabwriter.Writer
+ w = b
+ if debug > 0 {
+ tw = tabwriter.NewWriter(w, 1, 8, 1, '\t', 0)
+ w = tw
+ }
+
+ fmt.Fprintf(w, "%s profile: total %d\n", name, p.Len())
+
+ // Build count of each stack.
+ var buf bytes.Buffer
+ key := func(stk []uintptr) string {
+ buf.Reset()
+ fmt.Fprintf(&buf, "@")
+ for _, pc := range stk {
+ fmt.Fprintf(&buf, " %#x", pc)
+ }
+ return buf.String()
+ }
+ m := map[string]int{}
+ n := p.Len()
+ for i := 0; i < n; i++ {
+ m[key(p.Stack(i))]++
+ }
+
+ // Print stacks, listing count on first occurrence of a unique stack.
+ for i := 0; i < n; i++ {
+ stk := p.Stack(i)
+ s := key(stk)
+ if count := m[s]; count != 0 {
+ fmt.Fprintf(w, "%d %s\n", count, s)
+ if debug > 0 {
+ printStackRecord(w, stk, false)
+ }
+ delete(m, s)
+ }
+ }
+
+ if tw != nil {
+ tw.Flush()
+ }
+ return b.Flush()
+}
+
+// printStackRecord prints the function + source line information
+// for a single stack trace.
+func printStackRecord(w io.Writer, stk []uintptr, allFrames bool) {
+ show := allFrames
+ for _, pc := range stk {
+ f := runtime.FuncForPC(pc)
+ if f == nil {
+ show = true
+ fmt.Fprintf(w, "#\t%#x\n", pc)
+ } else {
+ file, line := f.FileLine(pc)
+ name := f.Name()
+ // Hide runtime.goexit and any runtime functions at the beginning.
+ // This is useful mainly for allocation traces.
+ if name == "runtime.goexit" || !show && strings.HasPrefix(name, "runtime.") {
+ continue
+ }
+ show = true
+ fmt.Fprintf(w, "#\t%#x\t%s+%#x\t%s:%d\n", pc, f.Name(), pc-f.Entry(), file, line)
+ }
+ }
+ if !show {
+ // We didn't print anything; do it again,
+ // and this time include runtime functions.
+ printStackRecord(w, stk, true)
+ return
+ }
+ fmt.Fprintf(w, "\n")
+}
+
+// Interface to system profiles.
+
+type byInUseBytes []runtime.MemProfileRecord
+
+func (x byInUseBytes) Len() int { return len(x) }
+func (x byInUseBytes) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
+func (x byInUseBytes) Less(i, j int) bool { return x[i].InUseBytes() > x[j].InUseBytes() }
+
+// WriteHeapProfile is shorthand for Lookup("heap").WriteTo(w, 0).
+// It is preserved for backwards compatibility.
func WriteHeapProfile(w io.Writer) error {
+ return writeHeap(w, 0)
+}
+
+// countHeap returns the number of records in the heap profile.
+func countHeap() int {
+ n, _ := runtime.MemProfile(nil, false)
+ return n
+}
+
+// writeHeapProfile writes the current runtime heap profile to w.
+func writeHeap(w io.Writer, debug int) error {
// Find out how many records there are (MemProfile(nil, false)),
// allocate that many records, and get the data.
// There's a race—more records might be added between
// Profile grew; try again.
}
+ sort.Sort(byInUseBytes(p))
+
+ b := bufio.NewWriter(w)
+ var tw *tabwriter.Writer
+ w = b
+ if debug > 0 {
+ tw = tabwriter.NewWriter(w, 1, 8, 1, '\t', 0)
+ w = tw
+ }
+
var total runtime.MemProfileRecord
for i := range p {
r := &p[i]
// Technically the rate is MemProfileRate not 2*MemProfileRate,
// but early versions of the C++ heap profiler reported 2*MemProfileRate,
// so that's what pprof has come to expect.
- b := bufio.NewWriter(w)
- fmt.Fprintf(b, "heap profile: %d: %d [%d: %d] @ heap/%d\n",
+ fmt.Fprintf(w, "heap profile: %d: %d [%d: %d] @ heap/%d\n",
total.InUseObjects(), total.InUseBytes(),
total.AllocObjects, total.AllocBytes,
2*runtime.MemProfileRate)
for i := range p {
r := &p[i]
- fmt.Fprintf(b, "%d: %d [%d: %d] @",
+ fmt.Fprintf(w, "%d: %d [%d: %d] @",
r.InUseObjects(), r.InUseBytes(),
r.AllocObjects, r.AllocBytes)
for _, pc := range r.Stack() {
- fmt.Fprintf(b, " %#x", pc)
+ fmt.Fprintf(w, " %#x", pc)
+ }
+ fmt.Fprintf(w, "\n")
+ if debug > 0 {
+ printStackRecord(w, r.Stack(), false)
}
- fmt.Fprintf(b, "\n")
}
// Print memstats information too.
- // Pprof will ignore, but useful for people.
- s := new(runtime.MemStats)
- runtime.ReadMemStats(s)
- fmt.Fprintf(b, "\n# runtime.MemStats\n")
- fmt.Fprintf(b, "# Alloc = %d\n", s.Alloc)
- fmt.Fprintf(b, "# TotalAlloc = %d\n", s.TotalAlloc)
- fmt.Fprintf(b, "# Sys = %d\n", s.Sys)
- fmt.Fprintf(b, "# Lookups = %d\n", s.Lookups)
- fmt.Fprintf(b, "# Mallocs = %d\n", s.Mallocs)
-
- fmt.Fprintf(b, "# HeapAlloc = %d\n", s.HeapAlloc)
- fmt.Fprintf(b, "# HeapSys = %d\n", s.HeapSys)
- fmt.Fprintf(b, "# HeapIdle = %d\n", s.HeapIdle)
- fmt.Fprintf(b, "# HeapInuse = %d\n", s.HeapInuse)
-
- fmt.Fprintf(b, "# Stack = %d / %d\n", s.StackInuse, s.StackSys)
- fmt.Fprintf(b, "# MSpan = %d / %d\n", s.MSpanInuse, s.MSpanSys)
- fmt.Fprintf(b, "# MCache = %d / %d\n", s.MCacheInuse, s.MCacheSys)
- fmt.Fprintf(b, "# BuckHashSys = %d\n", s.BuckHashSys)
-
- fmt.Fprintf(b, "# NextGC = %d\n", s.NextGC)
- fmt.Fprintf(b, "# PauseNs = %d\n", s.PauseNs)
- fmt.Fprintf(b, "# NumGC = %d\n", s.NumGC)
- fmt.Fprintf(b, "# EnableGC = %v\n", s.EnableGC)
- fmt.Fprintf(b, "# DebugGC = %v\n", s.DebugGC)
-
- fmt.Fprintf(b, "# BySize = Size * (Active = Mallocs - Frees)\n")
- fmt.Fprintf(b, "# (Excluding large blocks.)\n")
- for _, t := range s.BySize {
- if t.Mallocs > 0 {
- fmt.Fprintf(b, "# %d * (%d = %d - %d)\n", t.Size, t.Mallocs-t.Frees, t.Mallocs, t.Frees)
- }
+ // Pprof will ignore, but useful for people
+ if debug > 0 {
+ s := new(runtime.MemStats)
+ runtime.ReadMemStats(s)
+ fmt.Fprintf(w, "\n# runtime.MemStats\n")
+ fmt.Fprintf(w, "# Alloc = %d\n", s.Alloc)
+ fmt.Fprintf(w, "# TotalAlloc = %d\n", s.TotalAlloc)
+ fmt.Fprintf(w, "# Sys = %d\n", s.Sys)
+ fmt.Fprintf(w, "# Lookups = %d\n", s.Lookups)
+ fmt.Fprintf(w, "# Mallocs = %d\n", s.Mallocs)
+
+ fmt.Fprintf(w, "# HeapAlloc = %d\n", s.HeapAlloc)
+ fmt.Fprintf(w, "# HeapSys = %d\n", s.HeapSys)
+ fmt.Fprintf(w, "# HeapIdle = %d\n", s.HeapIdle)
+ fmt.Fprintf(w, "# HeapInuse = %d\n", s.HeapInuse)
+
+ fmt.Fprintf(w, "# Stack = %d / %d\n", s.StackInuse, s.StackSys)
+ fmt.Fprintf(w, "# MSpan = %d / %d\n", s.MSpanInuse, s.MSpanSys)
+ fmt.Fprintf(w, "# MCache = %d / %d\n", s.MCacheInuse, s.MCacheSys)
+ fmt.Fprintf(w, "# BuckHashSys = %d\n", s.BuckHashSys)
+
+ fmt.Fprintf(w, "# NextGC = %d\n", s.NextGC)
+ fmt.Fprintf(w, "# PauseNs = %d\n", s.PauseNs)
+ fmt.Fprintf(w, "# NumGC = %d\n", s.NumGC)
+ fmt.Fprintf(w, "# EnableGC = %v\n", s.EnableGC)
+ fmt.Fprintf(w, "# DebugGC = %v\n", s.DebugGC)
+ }
+
+ if tw != nil {
+ tw.Flush()
}
return b.Flush()
}
-// WriteThreadProfile writes a pprof-formatted thread creation profile to w.
-// If a write to w returns an error, WriteThreadProfile returns that error.
-// Otherwise, WriteThreadProfile returns nil.
-func WriteThreadProfile(w io.Writer) error {
- // Find out how many records there are (ThreadProfile(nil)),
+// countThreadCreate returns the size of the current ThreadCreateProfile.
+func countThreadCreate() int {
+ n, _ := runtime.ThreadCreateProfile(nil)
+ return n
+}
+
+// writeThreadCreate writes the current runtime ThreadCreateProfile to w.
+func writeThreadCreate(w io.Writer, debug int) error {
+ return writeRuntimeProfile(w, debug, "threadcreate", runtime.ThreadCreateProfile)
+}
+
+// countGoroutine returns the number of goroutines.
+func countGoroutine() int {
+ return runtime.NumGoroutine()
+}
+
+// writeGoroutine writes the current runtime GoroutineProfile to w.
+func writeGoroutine(w io.Writer, debug int) error {
+ if debug >= 2 {
+ return writeGoroutineStacks(w)
+ }
+ return writeRuntimeProfile(w, debug, "goroutine", runtime.GoroutineProfile)
+}
+
+func writeGoroutineStacks(w io.Writer) error {
+ // We don't know how big the buffer needs to be to collect
+ // all the goroutines. Start with 1 MB and try a few times, doubling each time.
+ // Give up and use a truncated trace if 64 MB is not enough.
+ buf := make([]byte, 1<<20)
+ for i := 0; ; i++ {
+ n := runtime.Stack(buf, true)
+ if n < len(buf) {
+ buf = buf[:n]
+ break
+ }
+ if len(buf) >= 64<<20 {
+ // Filled 64 MB - stop there.
+ break
+ }
+ buf = make([]byte, 2*len(buf))
+ }
+ _, err := w.Write(buf)
+ return err
+}
+
+func writeRuntimeProfile(w io.Writer, debug int, name string, fetch func([]runtime.StackRecord) (int, bool)) error {
+ // Find out how many records there are (fetch(nil)),
// allocate that many records, and get the data.
- // There's a race—more records (threads) might be added between
+ // There's a race—more records might be added between
// the two calls—so allocate a few extra records for safety
// and also try again if we're very unlucky.
// The loop should only execute one iteration in the common case.
- var p []runtime.ThreadProfileRecord
- n, ok := runtime.ThreadProfile(nil)
+ var p []runtime.StackRecord
+ n, ok := fetch(nil)
for {
// Allocate room for a slightly bigger profile,
// in case a few more entries have been added
// since the call to ThreadProfile.
- p = make([]runtime.ThreadProfileRecord, n+10)
- n, ok = runtime.ThreadProfile(p)
+ p = make([]runtime.StackRecord, n+10)
+ n, ok = fetch(p)
if ok {
p = p[0:n]
break
// Profile grew; try again.
}
- b := bufio.NewWriter(w)
- fmt.Fprintf(b, "thread creation profile: %d threads\n", n)
- for i := range p {
- r := &p[i]
- fmt.Fprintf(b, "@")
- for _, pc := range r.Stack() {
- fmt.Fprintf(b, " %#x", pc)
- }
- fmt.Fprintf(b, "\n")
- }
- return b.Flush()
+ return printCountProfile(w, debug, name, runtimeProfile(p))
}
+type runtimeProfile []runtime.StackRecord
+
+func (p runtimeProfile) Len() int { return len(p) }
+func (p runtimeProfile) Stack(i int) []uintptr { return p[i].Stack() }
+
var cpu struct {
sync.Mutex
profiling bool
+++ /dev/null
-// Copyright 2009 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 runtime_test
-
-import (
- "runtime"
- "sync/atomic"
- "testing"
-)
-
-func BenchmarkSemaUncontended(b *testing.B) {
- type PaddedSem struct {
- sem uint32
- pad [32]uint32
- }
- const CallsPerSched = 1000
- procs := runtime.GOMAXPROCS(-1)
- N := int32(b.N / CallsPerSched)
- c := make(chan bool, procs)
- for p := 0; p < procs; p++ {
- go func() {
- sem := new(PaddedSem)
- for atomic.AddInt32(&N, -1) >= 0 {
- runtime.Gosched()
- for g := 0; g < CallsPerSched; g++ {
- runtime.Semrelease(&sem.sem)
- runtime.Semacquire(&sem.sem)
- }
- }
- c <- true
- }()
- }
- for p := 0; p < procs; p++ {
- <-c
- }
-}
-
-func benchmarkSema(b *testing.B, block, work bool) {
- const CallsPerSched = 1000
- const LocalWork = 100
- procs := runtime.GOMAXPROCS(-1)
- N := int32(b.N / CallsPerSched)
- c := make(chan bool, procs)
- c2 := make(chan bool, procs/2)
- sem := uint32(0)
- if block {
- for p := 0; p < procs/2; p++ {
- go func() {
- runtime.Semacquire(&sem)
- c2 <- true
- }()
- }
- }
- for p := 0; p < procs; p++ {
- go func() {
- foo := 0
- for atomic.AddInt32(&N, -1) >= 0 {
- runtime.Gosched()
- for g := 0; g < CallsPerSched; g++ {
- runtime.Semrelease(&sem)
- if work {
- for i := 0; i < LocalWork; i++ {
- foo *= 2
- foo /= 2
- }
- }
- runtime.Semacquire(&sem)
- }
- }
- c <- foo == 42
- runtime.Semrelease(&sem)
- }()
- }
- if block {
- for p := 0; p < procs/2; p++ {
- <-c2
- }
- }
- for p := 0; p < procs; p++ {
- <-c
- }
-}
-
-func BenchmarkSemaSyntNonblock(b *testing.B) {
- benchmarkSema(b, false, false)
-}
-
-func BenchmarkSemaSyntBlock(b *testing.B) {
- benchmarkSema(b, true, false)
-}
-
-func BenchmarkSemaWorkNonblock(b *testing.B) {
- benchmarkSema(b, false, true)
-}
-
-func BenchmarkSemaWorkBlock(b *testing.B) {
- benchmarkSema(b, true, true)
-}
--- /dev/null
+// 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 sort_test
+
+import (
+ "fmt"
+ "sort"
+)
+
+type Grams int
+
+func (g Grams) String() string { return fmt.Sprintf("%dg", int(g)) }
+
+type Organ struct {
+ Name string
+ Weight Grams
+}
+
+type Organs []*Organ
+
+func (s Organs) Len() int { return len(s) }
+func (s Organs) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+
+// ByName implements sort.Interface by providing Less and using the Len and
+// Swap methods of the embedded Organs value.
+type ByName struct{ Organs }
+
+func (s ByName) Less(i, j int) bool { return s.Organs[i].Name < s.Organs[j].Name }
+
+// ByWeight implements sort.Interface by providing Less and using the Len and
+// Swap methods of the embedded Organs value.
+type ByWeight struct{ Organs }
+
+func (s ByWeight) Less(i, j int) bool { return s.Organs[i].Weight < s.Organs[j].Weight }
+
+func ExampleInterface() {
+ s := []*Organ{
+ {"brain", 1340},
+ {"heart", 290},
+ {"liver", 1494},
+ {"pancreas", 131},
+ {"prostate", 62},
+ {"spleen", 162},
+ }
+
+ sort.Sort(ByWeight{s})
+ fmt.Println("Organs by weight:")
+ printOrgans(s)
+
+ sort.Sort(ByName{s})
+ fmt.Println("Organs by name:")
+ printOrgans(s)
+
+ // Output:
+ // Organs by weight:
+ // prostate (62g)
+ // pancreas (131g)
+ // spleen (162g)
+ // heart (290g)
+ // brain (1340g)
+ // liver (1494g)
+ // Organs by name:
+ // brain (1340g)
+ // heart (290g)
+ // liver (1494g)
+ // pancreas (131g)
+ // prostate (62g)
+ // spleen (162g)
+}
+
+func printOrgans(s []*Organ) {
+ for _, o := range s {
+ fmt.Printf("%-8s (%v)\n", o.Name, o.Weight)
+ }
+}
--- /dev/null
+// 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 sort_test
+
+import (
+ "fmt"
+ "sort"
+)
+
+// Reverse embeds a sort.Interface value and implements a reverse sort over
+// that value.
+type Reverse struct {
+ // This embedded Interface permits Reverse to use the methods of
+ // another Interface implementation.
+ sort.Interface
+}
+
+// Less returns the opposite of the embedded implementation's Less method.
+func (r Reverse) Less(i, j int) bool {
+ return r.Interface.Less(j, i)
+}
+
+func ExampleInterface_reverse() {
+ s := []int{5, 2, 6, 3, 1, 4} // unsorted
+ sort.Sort(Reverse{sort.IntSlice(s)})
+ fmt.Println(s)
+ // Output: [6 5 4 3 2 1]
+}
"sort"
)
-// [1 2 3 4 5 6]
func ExampleInts() {
s := []int{5, 2, 6, 3, 1, 4} // unsorted
sort.Ints(s)
fmt.Println(s)
+ // Output: [1 2 3 4 5 6]
}
// 3) Multiply by 2^precision and round to get mantissa.
import "math"
+import "runtime"
var optimize = true // can change for testing
if d.nd > 15 {
return
}
+ // gccgo gets this wrong on 32-bit i386 when not using -msse.
+ // See TestRoundTrip in atof_test.go for a test case.
+ if runtime.GOARCH == "386" {
+ return
+ }
switch {
case d.dp == d.nd: // int
f := d.atof64int()
}
func numAllocations(f func()) int {
+ runtime.GC()
memstats := new(runtime.MemStats)
runtime.ReadMemStats(memstats)
n0 := memstats.Mallocs
"strings"
)
-// Fields are: ["foo" "bar" "baz"]
func ExampleFields() {
fmt.Printf("Fields are: %q", strings.Fields(" foo bar baz "))
+ // Output: Fields are: ["foo" "bar" "baz"]
}
-// true
-// false
-// true
-// true
func ExampleContains() {
fmt.Println(strings.Contains("seafood", "foo"))
fmt.Println(strings.Contains("seafood", "bar"))
fmt.Println(strings.Contains("seafood", ""))
fmt.Println(strings.Contains("", ""))
+ // Output:
+ // true
+ // false
+ // true
+ // true
}
-// false
-// true
-// false
-// false
func ExampleContainsAny() {
fmt.Println(strings.ContainsAny("team", "i"))
fmt.Println(strings.ContainsAny("failure", "u & i"))
fmt.Println(strings.ContainsAny("foo", ""))
fmt.Println(strings.ContainsAny("", ""))
-
+ // Output:
+ // false
+ // true
+ // false
+ // false
}
-// 3
-// 5
func ExampleCount() {
fmt.Println(strings.Count("cheese", "e"))
fmt.Println(strings.Count("five", "")) // before & after each rune
+ // Output:
+ // 3
+ // 5
}
-// true
func ExampleEqualFold() {
fmt.Println(strings.EqualFold("Go", "go"))
+ // Output: true
}
-// 4
-// -1
func ExampleIndex() {
fmt.Println(strings.Index("chicken", "ken"))
fmt.Println(strings.Index("chicken", "dmr"))
+ // Output:
+ // 4
+ // -1
}
-// 4
-// -1
func ExampleRune() {
fmt.Println(strings.IndexRune("chicken", 'k'))
fmt.Println(strings.IndexRune("chicken", 'd'))
+ // Output:
+ // 4
+ // -1
}
-// 0
-// 3
-// -1
func ExampleLastIndex() {
fmt.Println(strings.Index("go gopher", "go"))
fmt.Println(strings.LastIndex("go gopher", "go"))
fmt.Println(strings.LastIndex("go gopher", "rodent"))
+ // Output:
+ // 0
+ // 3
+ // -1
}
-// foo, bar, baz
func ExampleJoin() {
s := []string{"foo", "bar", "baz"}
fmt.Println(strings.Join(s, ", "))
+ // Output: foo, bar, baz
}
-// banana
func ExampleRepeat() {
fmt.Println("ba" + strings.Repeat("na", 2))
+ // Output: banana
}
-// oinky oinky oink
-// moo moo moo
func ExampleReplace() {
fmt.Println(strings.Replace("oink oink oink", "k", "ky", 2))
fmt.Println(strings.Replace("oink oink oink", "oink", "moo", -1))
+ // Output:
+ // oinky oinky oink
+ // moo moo moo
}
-// ["a" "b" "c"]
-// ["" "man " "plan " "canal panama"]
-// [" " "x" "y" "z" " "]
-// [""]
func ExampleSplit() {
fmt.Printf("%q\n", strings.Split("a,b,c", ","))
fmt.Printf("%q\n", strings.Split("a man a plan a canal panama", "a "))
fmt.Printf("%q\n", strings.Split(" xyz ", ""))
fmt.Printf("%q\n", strings.Split("", "Bernardo O'Higgins"))
+ // Output:
+ // ["a" "b" "c"]
+ // ["" "man " "plan " "canal panama"]
+ // [" " "x" "y" "z" " "]
+ // [""]
}
-// ["a" "b,c"]
-// [] (nil = true)
func ExampleSplitN() {
fmt.Printf("%q\n", strings.SplitN("a,b,c", ",", 2))
z := strings.SplitN("a,b,c", ",", 0)
fmt.Printf("%q (nil = %v)\n", z, z == nil)
+ // Output:
+ // ["a" "b,c"]
+ // [] (nil = true)
}
-// ["a," "b," "c"]
func ExampleSplitAfter() {
fmt.Printf("%q\n", strings.SplitAfter("a,b,c", ","))
+ // Output: ["a," "b," "c"]
}
-// ["a," "b,c"]
func ExampleSplitAfterN() {
fmt.Printf("%q\n", strings.SplitAfterN("a,b,c", ",", 2))
+ // Output: ["a," "b,c"]
}
-// Her Royal Highness
func ExampleTitle() {
fmt.Println(strings.Title("her royal highness"))
+ // Output: Her Royal Highness
}
-// LOUD NOISES
-// ХЛЕБ
func ExampleToTitle() {
fmt.Println(strings.ToTitle("loud noises"))
fmt.Println(strings.ToTitle("хлеб"))
+ // Output:
+ // LOUD NOISES
+ // ХЛЕБ
}
-// [Achtung]
func ExampleTrim() {
- fmt.Printf("[%s]", strings.Trim(" !!! Achtung !!! ", "! "))
+ fmt.Printf("[%q]", strings.Trim(" !!! Achtung !!! ", "! "))
+ // Output: ["Achtung"]
}
-// 'Gjnf oevyyvt naq gur fyvgul tbcure...
func ExampleMap() {
rot13 := func(r rune) rune {
switch {
return r
}
fmt.Println(strings.Map(rot13, "'Twas brillig and the slithy gopher..."))
+ // Output: 'Gjnf oevyyvt naq gur fyvgul tbcure...
}
-// a lone gopher
func ExampleTrimSpace() {
fmt.Println(strings.TrimSpace(" \t\n a lone gopher \n\t\r\n"))
+ // Output: a lone gopher
}
-// This is <b>HTML</b>!
func ExampleNewReplacer() {
r := strings.NewReplacer("<", "<", ">", ">")
fmt.Println(r.Replace("This is <b>HTML</b>!"))
+ // Output: This is <b>HTML</b>!
}
-// GOPHER
func ExampleToUpper() {
fmt.Println(strings.ToUpper("Gopher"))
+ // Output: GOPHER
}
-// gopher
func ExampleToLower() {
fmt.Println(strings.ToLower("Gopher"))
+ // Output: gopher
}
package sync
-import "runtime"
-
// Cond implements a condition variable, a rendezvous point
// for goroutines waiting for or announcing the occurrence
// of an event.
// Wait atomically unlocks c.L and suspends execution
// of the calling goroutine. After later resuming execution,
-// Wait locks c.L before returning.
+// Wait locks c.L before returning. Unlike in other systems,
+// Wait cannot return unless awoken by Broadcast or Signal.
//
-// Because L is not locked when Wait first resumes, the caller
+// Because c.L is not locked when Wait first resumes, the caller
// typically cannot assume that the condition is true when
// Wait returns. Instead, the caller should Wait in a loop:
//
c.newWaiters++
c.m.Unlock()
c.L.Unlock()
- runtime.Semacquire(s)
+ runtime_Semacquire(s)
c.L.Lock()
}
}
if c.oldWaiters > 0 {
c.oldWaiters--
- runtime.Semrelease(c.oldSema)
+ runtime_Semrelease(c.oldSema)
}
c.m.Unlock()
}
// Wake both generations.
if c.oldWaiters > 0 {
for i := 0; i < c.oldWaiters; i++ {
- runtime.Semrelease(c.oldSema)
+ runtime_Semrelease(c.oldSema)
}
c.oldWaiters = 0
}
if c.newWaiters > 0 {
for i := 0; i < c.newWaiters; i++ {
- runtime.Semrelease(c.newSema)
+ runtime_Semrelease(c.newSema)
}
c.newWaiters = 0
c.newSema = nil
--- /dev/null
+// 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.
+
+package sync_test
+
+import (
+ "net/http"
+ "sync"
+)
+
+// This example fetches several URLs concurrently,
+// using a WaitGroup to block until all the fetches are complete.
+func ExampleWaitGroup() {
+ var wg sync.WaitGroup
+ var urls = []string{
+ "http://www.golang.org/",
+ "http://www.google.com/",
+ "http://www.somestupidname.com/",
+ }
+ for _, url := range urls {
+ // Increment the WaitGroup counter.
+ wg.Add(1)
+ // Launch a goroutine to fetch the URL.
+ go func(url string) {
+ // Fetch the URL.
+ http.Get(url)
+ // Decrement the counter.
+ wg.Done()
+ }(url)
+ }
+ // Wait for all HTTP fetches to complete.
+ wg.Wait()
+}
--- /dev/null
+// 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.
+
+package sync
+
+// Export for testing.
+var Runtime_Semacquire = runtime_Semacquire
+var Runtime_Semrelease = runtime_Semrelease
// Values containing the types defined in this package should not be copied.
package sync
-import (
- "runtime"
- "sync/atomic"
-)
+import "sync/atomic"
// A Mutex is a mutual exclusion lock.
// Mutexes can be created as part of other structures;
if old&mutexLocked == 0 {
break
}
- runtime.Semacquire(&m.sema)
+ runtime_Semacquire(&m.sema)
awoke = true
}
}
// Grab the right to wake someone.
new = (old - 1<<mutexWaiterShift) | mutexWoken
if atomic.CompareAndSwapInt32(&m.state, old, new) {
- runtime.Semrelease(&m.sema)
+ runtime_Semrelease(&m.sema)
return
}
old = m.state
func HammerSemaphore(s *uint32, loops int, cdone chan bool) {
for i := 0; i < loops; i++ {
- runtime.Semacquire(s)
- runtime.Semrelease(s)
+ Runtime_Semacquire(s)
+ Runtime_Semrelease(s)
}
cdone <- true
}
--- /dev/null
+// 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.
+
+package sync
+
+// defined in package runtime
+
+// Semacquire waits until *s > 0 and then atomically decrements it.
+// It is intended as a simple sleep primitive for use by the synchronization
+// library and should not be used directly.
+func runtime_Semacquire(s *uint32)
+
+// Semrelease atomically increments *s and notifies a waiting goroutine
+// if one is blocked in Semacquire.
+// It is intended as a simple wakeup primitive for use by the synchronization
+// library and should not be used directly.
+func runtime_Semrelease(s *uint32)
--- /dev/null
+// Copyright 2009 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 sync_test
+
+import (
+ "runtime"
+ . "sync"
+ "sync/atomic"
+ "testing"
+)
+
+func BenchmarkSemaUncontended(b *testing.B) {
+ type PaddedSem struct {
+ sem uint32
+ pad [32]uint32
+ }
+ const CallsPerSched = 1000
+ procs := runtime.GOMAXPROCS(-1)
+ N := int32(b.N / CallsPerSched)
+ c := make(chan bool, procs)
+ for p := 0; p < procs; p++ {
+ go func() {
+ sem := new(PaddedSem)
+ for atomic.AddInt32(&N, -1) >= 0 {
+ runtime.Gosched()
+ for g := 0; g < CallsPerSched; g++ {
+ Runtime_Semrelease(&sem.sem)
+ Runtime_Semacquire(&sem.sem)
+ }
+ }
+ c <- true
+ }()
+ }
+ for p := 0; p < procs; p++ {
+ <-c
+ }
+}
+
+func benchmarkSema(b *testing.B, block, work bool) {
+ const CallsPerSched = 1000
+ const LocalWork = 100
+ procs := runtime.GOMAXPROCS(-1)
+ N := int32(b.N / CallsPerSched)
+ c := make(chan bool, procs)
+ c2 := make(chan bool, procs/2)
+ sem := uint32(0)
+ if block {
+ for p := 0; p < procs/2; p++ {
+ go func() {
+ Runtime_Semacquire(&sem)
+ c2 <- true
+ }()
+ }
+ }
+ for p := 0; p < procs; p++ {
+ go func() {
+ foo := 0
+ for atomic.AddInt32(&N, -1) >= 0 {
+ runtime.Gosched()
+ for g := 0; g < CallsPerSched; g++ {
+ Runtime_Semrelease(&sem)
+ if work {
+ for i := 0; i < LocalWork; i++ {
+ foo *= 2
+ foo /= 2
+ }
+ }
+ Runtime_Semacquire(&sem)
+ }
+ }
+ c <- foo == 42
+ Runtime_Semrelease(&sem)
+ }()
+ }
+ if block {
+ for p := 0; p < procs/2; p++ {
+ <-c2
+ }
+ }
+ for p := 0; p < procs; p++ {
+ <-c
+ }
+}
+
+func BenchmarkSemaSyntNonblock(b *testing.B) {
+ benchmarkSema(b, false, false)
+}
+
+func BenchmarkSemaSyntBlock(b *testing.B) {
+ benchmarkSema(b, true, false)
+}
+
+func BenchmarkSemaWorkNonblock(b *testing.B) {
+ benchmarkSema(b, false, true)
+}
+
+func BenchmarkSemaWorkBlock(b *testing.B) {
+ benchmarkSema(b, true, true)
+}
package sync
-import (
- "runtime"
- "sync/atomic"
-)
+import "sync/atomic"
// An RWMutex is a reader/writer mutual exclusion lock.
// The lock can be held by an arbitrary number of readers
func (rw *RWMutex) RLock() {
if atomic.AddInt32(&rw.readerCount, 1) < 0 {
// A writer is pending, wait for it.
- runtime.Semacquire(&rw.readerSem)
+ runtime_Semacquire(&rw.readerSem)
}
}
// A writer is pending.
if atomic.AddInt32(&rw.readerWait, -1) == 0 {
// The last reader unblocks the writer.
- runtime.Semrelease(&rw.writerSem)
+ runtime_Semrelease(&rw.writerSem)
}
}
}
r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders
// Wait for active readers.
if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 {
- runtime.Semacquire(&rw.writerSem)
+ runtime_Semacquire(&rw.writerSem)
}
}
r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders)
// Unblock blocked readers, if any.
for i := 0; i < int(r); i++ {
- runtime.Semrelease(&rw.readerSem)
+ runtime_Semrelease(&rw.readerSem)
}
// Allow other writers to proceed.
rw.w.Unlock()
package sync
-import (
- "runtime"
- "sync/atomic"
-)
+import "sync/atomic"
// A WaitGroup waits for a collection of goroutines to finish.
// The main goroutine calls Add to set the number of
}
wg.m.Lock()
for i := int32(0); i < wg.waiters; i++ {
- runtime.Semrelease(wg.sema)
+ runtime_Semrelease(wg.sema)
}
wg.waiters = 0
wg.sema = nil
}
s := wg.sema
wg.m.Unlock()
- runtime.Semacquire(s)
+ runtime_Semacquire(s)
}
// }
// }
//
-// The package also runs and verifies example code. Example functions
-// include an introductory comment that is compared with the standard output
-// of the function when the tests are run, as in this example of an example:
+// The package also runs and verifies example code. Example functions may
+// include a concluding comment that begins with "Output:" and is compared with
+// the standard output of the function when the tests are run, as in these
+// examples of an example:
//
-// // hello
// func ExampleHello() {
// fmt.Println("hello")
+// // Output: hello
// }
//
-// Example functions without comments are compiled but not executed.
+// func ExampleSalutations() {
+// fmt.Println("hello, and")
+// fmt.Println("goodbye")
+// // Output:
+// // hello, and
+// // goodbye
+// }
+//
+// Example functions without output comments are compiled but not executed.
//
// The naming convention to declare examples for a function F, a type T and
// method M on type T are:
--- /dev/null
+// 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.
+
+package tabwriter_test
+
+import (
+ "fmt"
+ "os"
+ "text/tabwriter"
+)
+
+func ExampleWriter_Init() {
+ w := new(tabwriter.Writer)
+
+ // Format in tab-separated columns with a tab stop of 8.
+ w.Init(os.Stdout, 0, 8, 0, '\t', 0)
+ fmt.Fprintln(w, "a\tb\tc\td\t.")
+ fmt.Fprintln(w, "123\t12345\t1234567\t123456789\t.")
+ fmt.Fprintln(w)
+ w.Flush()
+
+ // Format right-aligned in space-separated columns of minimal width 5
+ // and at least one blank of padding (so wider column entries do not
+ // touch each other).
+ w.Init(os.Stdout, 5, 0, 1, ' ', tabwriter.AlignRight)
+ fmt.Fprintln(w, "a\tb\tc\td\t.")
+ fmt.Fprintln(w, "123\t12345\t1234567\t123456789\t.")
+ fmt.Fprintln(w)
+ w.Flush()
+
+ // output:
+ // a b c d .
+ // 123 12345 1234567 123456789 .
+ //
+ // a b c d.
+ // 123 12345 1234567 123456789.
+}
// to the tab width in the viewer displaying the result)
// flags formatting control
//
-// To format in tab-separated columns with a tab stop of 8:
-// b.Init(w, 8, 1, 8, '\t', 0);
-//
-// To format in space-separated columns with at least 4 spaces between columns:
-// b.Init(w, 0, 4, 8, ' ', 0);
-//
func (b *Writer) Init(output io.Writer, minwidth, tabwidth, padding int, padchar byte, flags uint) *Writer {
if minwidth < 0 || tabwidth < 0 || padding < 0 {
panic("negative minwidth, tabwidth, or padding")
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package tabwriter
+package tabwriter_test
import (
"io"
"testing"
+ . "text/tabwriter"
)
type buffer struct {
Once constructed, a template may be executed safely in parallel.
+Here is a trivial example that prints "17 items are made of wool".
+
+ type Inventory struct {
+ Material string
+ Count uint
+ }
+ sweaters := Inventory{"wool", 17}
+ tmpl, err := template.New("test").Parse("{{.Count}} items are made of {{.Material}}")
+ if err != nil { panic(err) }
+ err = tmpl.Execute(os.Stdout, sweaters)
+ if err != nil { panic(err) }
+
+More intricate examples appear below.
+
Actions
Here is the list of actions. "Arguments" and "pipelines" are evaluations of
.Field1.Key1.Method1.Field2.Key2.Method2
Methods can also be evaluated on variables, including chaining:
$x.Method1.Field
+ - The name of a niladic function-valued struct field of the data,
+ preceded by a period, such as
+ .Function
+ Function-valued fields behave like methods (of structs) but do not
+ pass a receiver.
- The name of a niladic function, such as
fun
The result is the value of invoking the function, fun(). The return
The result is the value of calling the method with the
arguments:
dot.Method(Argument1, etc.)
+ .Function [Argument...]
+ A function-valued field of a struct works like a method but does
+ not pass the receiver.
functionName [Argument...]
The result is the value of calling the function associated
with the name:
By construction, a template may reside in only one association. If it's
necessary to have a template addressable from multiple associations, the
template definition must be parsed multiple times to create distinct *Template
-values.
+values, or must be copied with the Clone or AddParseTree method.
Parse may be called multiple times to assemble the various associated templates;
see the ParseFiles and ParseGlob functions and methods for simple ways to parse
tField, ok := receiver.Type().FieldByName(fieldName)
if ok {
field := receiver.FieldByIndex(tField.Index)
- if hasArgs {
- s.errorf("%s is not a method but has arguments", fieldName)
- }
if tField.PkgPath == "" { // field is exported
+ // If it's a function, we must call it.
+ if field.Type().Kind() == reflect.Func {
+ return s.evalCall(dot, field, fieldName, args, final)
+ }
+ if hasArgs {
+ s.errorf("%s is not a method or function but has arguments", fieldName)
+ }
return field
}
}
PI *int
PSI *[]int
NIL *int
+ // Function (not method)
+ Func func(...string) string
// Template to test evaluation of templates.
Tmpl *Template
}
Err: errors.New("erroozle"),
PI: newInt(23),
PSI: newIntSlice(21, 22, 23),
+ Func: func(s ...string) string { return fmt.Sprint("<", strings.Join(s, "+"), ">") },
Tmpl: Must(New("x").Parse("test template")), // "x" is the value of .X
}
"{{with $x := .}}{{with .SI}}{{$.GetU.TrueFalse $.True}}{{end}}{{end}}",
"true", tVal, true},
+ // Function call
+ {".Func", "-{{.Func}}-", "-<>-", tVal, true},
+ {".Func2", "-{{.Func `he` `llo`}}-", "-<he+llo>-", tVal, true},
+
// Pipelines.
{"pipeline", "-{{.Method0 | .Method2 .U16}}-", "-Method2: 16 M0-", tVal, true},
+ {"pipeline func", "-{{.Func `llo` | .Func `he` }}-", "-<he+<llo>>-", tVal, true},
// If.
{"if true", "{{if true}}TRUE{{end}}", "TRUE", tVal, true},
if err != nil {
t.Fatal(err)
}
- clone := root.Clone()
+ clone := Must(root.Clone())
// Add variants to both.
_, err = root.Parse(cloneText3)
if err != nil {
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Package parse builds parse trees for templates. The grammar is defined
-// in the documents for the template package.
+// Package parse builds parse trees for templates as defined by text/template
+// and html/template. Clients should use those packages to construct templates
+// rather than this one, which provides shared internal data structures not
+// intended for general use.
package parse
import (
// templates. The actual representation is not copied, but the name space of
// associated templates is, so further calls to Parse in the copy will add
// templates to the copy but not to the original. Clone can be used to prepare
-// common templates and use them with variant definitions for other templates by
-// adding the variants after the clone is made.
-func (t *Template) Clone() *Template {
+// common templates and use them with variant definitions for other templates
+// by adding the variants after the clone is made.
+func (t *Template) Clone() (*Template, error) {
nt := t.copy(nil)
nt.init()
nt.tmpl[t.name] = nt
for k, v := range t.execFuncs {
nt.execFuncs[k] = v
}
- return nt
+ return nt, nil
}
// copy returns a shallow copy of t, with common set to the argument.
}
}
-// Go launched at 2009-11-10 15:00:00 -0800 PST
func ExampleDate() {
t := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
fmt.Printf("Go launched at %s\n", t.Local())
+ // Output: Go launched at 2009-11-10 15:00:00 -0800 PST
}
package time
-import "syscall"
+import (
+ "errors"
+ "syscall"
+)
// for testing: whatever interrupts a sleep
func interrupt() {
}
return ret, err
}
+
+func open(name string) (uintptr, error) {
+ fd, err := syscall.Open(name, syscall.O_RDONLY)
+ if err != nil {
+ return 0, err
+ }
+ return uintptr(fd), nil
+}
+
+func closefd(fd uintptr) {
+ syscall.Close(int(fd))
+}
+
+func preadn(fd uintptr, buf []byte, off int) error {
+ whence := 0
+ if off < 0 {
+ whence = 2
+ }
+ if _, err := syscall.Seek(int(fd), int64(off), whence); err != nil {
+ return err
+ }
+ for len(buf) > 0 {
+ m, err := syscall.Read(int(fd), buf)
+ if m <= 0 {
+ if err == nil {
+ return errors.New("short read")
+ }
+ return err
+ }
+ buf = buf[m:]
+ }
+ return nil
+}
package time
-import "syscall"
+import (
+ "errors"
+ "syscall"
+)
// for testing: whatever interrupts a sleep
func interrupt() {
}
return ret, err
}
+
+func open(name string) (uintptr, error) {
+ fd, err := syscall.Open(name, syscall.O_RDONLY, 0)
+ if err != nil {
+ return 0, err
+ }
+ return uintptr(fd), nil
+}
+
+func closefd(fd uintptr) {
+ syscall.Close(int(fd))
+}
+
+func preadn(fd uintptr, buf []byte, off int) error {
+ whence := 0
+ if off < 0 {
+ whence = 2
+ }
+ if _, err := syscall.Seek(int(fd), int64(off), whence); err != nil {
+ return err
+ }
+ for len(buf) > 0 {
+ m, err := syscall.Read(int(fd), buf)
+ if m <= 0 {
+ if err == nil {
+ return errors.New("short read")
+ }
+ return err
+ }
+ buf = buf[m:]
+ }
+ return nil
+}
package time
+import (
+ "errors"
+ "syscall"
+)
+
// for testing: whatever interrupts a sleep
func interrupt() {
}
+
+// readFile reads and returns the content of the named file.
+// It is a trivial implementation of ioutil.ReadFile, reimplemented
+// here to avoid depending on io/ioutil or os.
+func readFile(name string) ([]byte, error) {
+ f, err := syscall.Open(name, syscall.O_RDONLY, 0)
+ if err != nil {
+ return nil, err
+ }
+ defer syscall.Close(f)
+ var (
+ buf [4096]byte
+ ret []byte
+ n int
+ )
+ for {
+ n, err = syscall.Read(f, buf[:])
+ if n > 0 {
+ ret = append(ret, buf[:n]...)
+ }
+ if n == 0 || err != nil {
+ break
+ }
+ }
+ return ret, err
+}
+
+func open(name string) (uintptr, error) {
+ fd, err := syscall.Open(name, syscall.O_RDONLY, 0)
+ if err != nil {
+ return 0, err
+ }
+ return uintptr(fd), nil
+}
+
+func closefd(fd uintptr) {
+ syscall.Close(syscall.Handle(fd))
+}
+
+func preadn(fd uintptr, buf []byte, off int) error {
+ whence := 0
+ if off < 0 {
+ whence = 2
+ }
+ if _, err := syscall.Seek(syscall.Handle(fd), int64(off), whence); err != nil {
+ return err
+ }
+ for len(buf) > 0 {
+ m, err := syscall.Read(syscall.Handle(fd), buf)
+ if m <= 0 {
+ if err == nil {
+ return errors.New("short read")
+ }
+ return err
+ }
+ buf = buf[m:]
+ }
+ return nil
+}
func TestTicker(t *testing.T) {
const Count = 10
Delta := 100 * Millisecond
- if testing.Short() {
- Delta = 10 * Millisecond
- }
ticker := NewTicker(Delta)
t0 := Now()
for i := 0; i < Count; i++ {
package time
-import "sync"
+import (
+ "sync"
+ "syscall"
+)
// A Location maps time instants to the zone in use at that time.
// Typically, the Location represents the collection of time offsets
// NOTE(rsc): Eventually we will need to accept the POSIX TZ environment
// syntax too, but I don't feel like implementing it today.
-// NOTE(rsc): Using the IANA names below means ensuring we have access
-// to the database. Probably we will ship the files in $GOROOT/lib/zoneinfo/
-// and only look there if there are no system files available (such as on Windows).
-// The files total 200 kB.
+var zoneinfo, _ = syscall.Getenv("ZONEINFO")
// LoadLocation returns the Location with the given name.
//
//
// Otherwise, the name is taken to be a location name corresponding to a file
// in the IANA Time Zone database, such as "America/New_York".
+//
+// The time zone database needed by LoadLocation may not be
+// present on all systems, especially non-Unix systems.
+// LoadLocation looks in the directory or uncompressed zip file
+// named by the ZONEINFO environment variable, if any, then looks in
+// known installation locations on Unix systems,
+// and finally looks in $GOROOT/lib/time/zoneinfo.zip.
func LoadLocation(name string) (*Location, error) {
if name == "" || name == "UTC" {
return UTC, nil
if name == "Local" {
return Local, nil
}
+ if zoneinfo != "" {
+ if z, err := loadZoneFile(zoneinfo, name); err == nil {
+ z.name = name
+ return z, nil
+ }
+ }
return loadLocation(name)
}
import (
"errors"
+ "runtime"
"syscall"
)
-var badData = errors.New("malformed time zone information")
-
func isSpace(r rune) bool {
return r == ' ' || r == '\t' || r == '\n'
}
return a
}
-func loadZoneData(s string) (l *Location, err error) {
+func loadZoneDataPlan9(s string) (l *Location, err error) {
f := fields(s)
if len(f) < 4 {
if len(f) == 2 && f[0] == "GMT" {
return l, nil
}
-func loadZoneFile(name string) (*Location, error) {
+func loadZoneFilePlan9(name string) (*Location, error) {
b, err := readFile(name)
if err != nil {
return nil, err
}
- return loadZoneData(string(b))
+ return loadZoneDataPlan9(string(b))
}
func initTestingZone() {
- if z, err := loadZoneFile("/adm/timezone/US_Pacific"); err == nil {
- localLoc = *z
- return
+ z, err := loadLocation("America/Los_Angeles")
+ if err != nil {
+ panic("cannot load America/Los_Angeles for testing: " + err.Error())
}
-
- // Fall back to UTC.
- localLoc.name = "UTC"
+ z.name = "Local"
+ localLoc = *z
}
func initLocal() {
t, ok := syscall.Getenv("timezone")
if ok {
- if z, err := loadZoneData(t); err == nil {
+ if z, err := loadZoneDataPlan9(t); err == nil {
localLoc = *z
return
}
} else {
- if z, err := loadZoneFile("/adm/timezone/local"); err == nil {
+ if z, err := loadZoneFilePlan9("/adm/timezone/local"); err == nil {
localLoc = *z
localLoc.name = "Local"
return
}
func loadLocation(name string) (*Location, error) {
- if z, err := loadZoneFile("/adm/timezone/" + name); err == nil {
+ if z, err := loadZoneFile(runtime.GOROOT()+"/lib/time/zoneinfo.zip", name); err == nil {
+ z.name = name
return z, nil
}
return nil, errors.New("unknown time zone " + name)
--- /dev/null
+// Copyright 2009 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.
+
+// Parse "zoneinfo" time zone file.
+// This is a fairly standard file format used on OS X, Linux, BSD, Sun, and others.
+// See tzfile(5), http://en.wikipedia.org/wiki/Zoneinfo,
+// and ftp://munnari.oz.au/pub/oldtz/
+
+package time
+
+import "errors"
+
+const (
+ headerSize = 4 + 16 + 4*7
+)
+
+// Simple I/O interface to binary blob of data.
+type data struct {
+ p []byte
+ error bool
+}
+
+func (d *data) read(n int) []byte {
+ if len(d.p) < n {
+ d.p = nil
+ d.error = true
+ return nil
+ }
+ p := d.p[0:n]
+ d.p = d.p[n:]
+ return p
+}
+
+func (d *data) big4() (n uint32, ok bool) {
+ p := d.read(4)
+ if len(p) < 4 {
+ d.error = true
+ return 0, false
+ }
+ return uint32(p[0])<<24 | uint32(p[1])<<16 | uint32(p[2])<<8 | uint32(p[3]), true
+}
+
+func (d *data) byte() (n byte, ok bool) {
+ p := d.read(1)
+ if len(p) < 1 {
+ d.error = true
+ return 0, false
+ }
+ return p[0], true
+}
+
+// Make a string by stopping at the first NUL
+func byteString(p []byte) string {
+ for i := 0; i < len(p); i++ {
+ if p[i] == 0 {
+ return string(p[0:i])
+ }
+ }
+ return string(p)
+}
+
+var badData = errors.New("malformed time zone information")
+
+func loadZoneData(bytes []byte) (l *Location, err error) {
+ d := data{bytes, false}
+
+ // 4-byte magic "TZif"
+ if magic := d.read(4); string(magic) != "TZif" {
+ return nil, badData
+ }
+
+ // 1-byte version, then 15 bytes of padding
+ var p []byte
+ if p = d.read(16); len(p) != 16 || p[0] != 0 && p[0] != '2' {
+ return nil, badData
+ }
+
+ // six big-endian 32-bit integers:
+ // number of UTC/local indicators
+ // number of standard/wall indicators
+ // number of leap seconds
+ // number of transition times
+ // number of local time zones
+ // number of characters of time zone abbrev strings
+ const (
+ NUTCLocal = iota
+ NStdWall
+ NLeap
+ NTime
+ NZone
+ NChar
+ )
+ var n [6]int
+ for i := 0; i < 6; i++ {
+ nn, ok := d.big4()
+ if !ok {
+ return nil, badData
+ }
+ n[i] = int(nn)
+ }
+
+ // Transition times.
+ txtimes := data{d.read(n[NTime] * 4), false}
+
+ // Time zone indices for transition times.
+ txzones := d.read(n[NTime])
+
+ // Zone info structures
+ zonedata := data{d.read(n[NZone] * 6), false}
+
+ // Time zone abbreviations.
+ abbrev := d.read(n[NChar])
+
+ // Leap-second time pairs
+ d.read(n[NLeap] * 8)
+
+ // Whether tx times associated with local time types
+ // are specified as standard time or wall time.
+ isstd := d.read(n[NStdWall])
+
+ // Whether tx times associated with local time types
+ // are specified as UTC or local time.
+ isutc := d.read(n[NUTCLocal])
+
+ if d.error { // ran out of data
+ return nil, badData
+ }
+
+ // If version == 2, the entire file repeats, this time using
+ // 8-byte ints for txtimes and leap seconds.
+ // We won't need those until 2106.
+
+ // Now we can build up a useful data structure.
+ // First the zone information.
+ // utcoff[4] isdst[1] nameindex[1]
+ zone := make([]zone, n[NZone])
+ for i := range zone {
+ var ok bool
+ var n uint32
+ if n, ok = zonedata.big4(); !ok {
+ return nil, badData
+ }
+ zone[i].offset = int(n)
+ var b byte
+ if b, ok = zonedata.byte(); !ok {
+ return nil, badData
+ }
+ zone[i].isDST = b != 0
+ if b, ok = zonedata.byte(); !ok || int(b) >= len(abbrev) {
+ return nil, badData
+ }
+ zone[i].name = byteString(abbrev[b:])
+ }
+
+ // Now the transition time info.
+ tx := make([]zoneTrans, n[NTime])
+ for i := range tx {
+ var ok bool
+ var n uint32
+ if n, ok = txtimes.big4(); !ok {
+ return nil, badData
+ }
+ tx[i].when = int64(int32(n))
+ if int(txzones[i]) >= len(zone) {
+ return nil, badData
+ }
+ tx[i].index = txzones[i]
+ if i < len(isstd) {
+ tx[i].isstd = isstd[i] != 0
+ }
+ if i < len(isutc) {
+ tx[i].isutc = isutc[i] != 0
+ }
+ }
+
+ // Commited to succeed.
+ l = &Location{zone: zone, tx: tx}
+
+ // Fill in the cache with information about right now,
+ // since that will be the most common lookup.
+ sec, _ := now()
+ for i := range tx {
+ if tx[i].when <= sec && (i+1 == len(tx) || sec < tx[i+1].when) {
+ l.cacheStart = tx[i].when
+ l.cacheEnd = 1<<63 - 1
+ if i+1 < len(tx) {
+ l.cacheEnd = tx[i+1].when
+ }
+ l.cacheZone = &l.zone[tx[i].index]
+ }
+ }
+
+ return l, nil
+}
+
+func loadZoneFile(dir, name string) (l *Location, err error) {
+ if len(dir) > 4 && dir[len(dir)-4:] == ".zip" {
+ return loadZoneZip(dir, name)
+ }
+ if dir != "" {
+ name = dir + "/" + name
+ }
+ buf, err := readFile(name)
+ if err != nil {
+ return
+ }
+ return loadZoneData(buf)
+}
+
+// There are 500+ zoneinfo files. Rather than distribute them all
+// individually, we ship them in an uncompressed zip file.
+// Used this way, the zip file format serves as a commonly readable
+// container for the individual small files. We choose zip over tar
+// because zip files have a contiguous table of contents, making
+// individual file lookups faster, and because the per-file overhead
+// in a zip file is considerably less than tar's 512 bytes.
+
+// get4 returns the little-endian 32-bit value in b.
+func get4(b []byte) int {
+ if len(b) < 4 {
+ return 0
+ }
+ return int(b[0]) | int(b[1])<<8 | int(b[2])<<16 | int(b[3])<<24
+}
+
+// get2 returns the little-endian 16-bit value in b.
+func get2(b []byte) int {
+ if len(b) < 2 {
+ return 0
+ }
+ return int(b[0]) | int(b[1])<<8
+}
+
+func loadZoneZip(zipfile, name string) (l *Location, err error) {
+ fd, err := open(zipfile)
+ if err != nil {
+ return nil, errors.New("open " + zipfile + ": " + err.Error())
+ }
+ defer closefd(fd)
+
+ const (
+ zecheader = 0x06054b50
+ zcheader = 0x02014b50
+ ztailsize = 22
+
+ zheadersize = 30
+ zheader = 0x04034b50
+ )
+
+ buf := make([]byte, ztailsize)
+ if err := preadn(fd, buf, -ztailsize); err != nil || get4(buf) != zecheader {
+ return nil, errors.New("corrupt zip file " + zipfile)
+ }
+ n := get2(buf[10:])
+ size := get4(buf[12:])
+ off := get4(buf[16:])
+
+ buf = make([]byte, size)
+ if err := preadn(fd, buf, off); err != nil {
+ return nil, errors.New("corrupt zip file " + zipfile)
+ }
+
+ for i := 0; i < n; i++ {
+ // zip entry layout:
+ // 0 magic[4]
+ // 4 madevers[1]
+ // 5 madeos[1]
+ // 6 extvers[1]
+ // 7 extos[1]
+ // 8 flags[2]
+ // 10 meth[2]
+ // 12 modtime[2]
+ // 14 moddate[2]
+ // 16 crc[4]
+ // 20 csize[4]
+ // 24 uncsize[4]
+ // 28 namelen[2]
+ // 30 xlen[2]
+ // 32 fclen[2]
+ // 34 disknum[2]
+ // 36 iattr[2]
+ // 38 eattr[4]
+ // 42 off[4]
+ // 46 name[namelen]
+ // 46+namelen+xlen+fclen - next header
+ //
+ if get4(buf) != zcheader {
+ break
+ }
+ meth := get2(buf[10:])
+ size := get4(buf[24:])
+ namelen := get2(buf[28:])
+ xlen := get2(buf[30:])
+ fclen := get2(buf[32:])
+ off := get4(buf[42:])
+ zname := buf[46 : 46+namelen]
+ buf = buf[46+namelen+xlen+fclen:]
+ if string(zname) != name {
+ continue
+ }
+ if meth != 0 {
+ return nil, errors.New("unsupported compression for " + name + " in " + zipfile)
+ }
+
+ // zip per-file header layout:
+ // 0 magic[4]
+ // 4 extvers[1]
+ // 5 extos[1]
+ // 6 flags[2]
+ // 8 meth[2]
+ // 10 modtime[2]
+ // 12 moddate[2]
+ // 14 crc[4]
+ // 18 csize[4]
+ // 22 uncsize[4]
+ // 26 namelen[2]
+ // 28 xlen[2]
+ // 30 name[namelen]
+ // 30+namelen+xlen - file data
+ //
+ buf = make([]byte, zheadersize+namelen)
+ if err := preadn(fd, buf, off); err != nil ||
+ get4(buf) != zheader ||
+ get2(buf[8:]) != meth ||
+ get2(buf[26:]) != namelen ||
+ string(buf[30:30+namelen]) != name {
+ return nil, errors.New("corrupt zip file " + zipfile)
+ }
+ xlen = get2(buf[28:])
+
+ buf = make([]byte, size)
+ if err := preadn(fd, buf, off+30+namelen+xlen); err != nil {
+ return nil, errors.New("corrupt zip file " + zipfile)
+ }
+
+ return loadZoneData(buf)
+ }
+
+ return nil, errors.New("cannot find " + name + " in zip file " + zipfile)
+}
import (
"errors"
+ "runtime"
"syscall"
)
-const (
- headerSize = 4 + 16 + 4*7
-)
-
-// Simple I/O interface to binary blob of data.
-type data struct {
- p []byte
- error bool
-}
-
-func (d *data) read(n int) []byte {
- if len(d.p) < n {
- d.p = nil
- d.error = true
- return nil
- }
- p := d.p[0:n]
- d.p = d.p[n:]
- return p
-}
-
-func (d *data) big4() (n uint32, ok bool) {
- p := d.read(4)
- if len(p) < 4 {
- d.error = true
- return 0, false
- }
- return uint32(p[0])<<24 | uint32(p[1])<<16 | uint32(p[2])<<8 | uint32(p[3]), true
-}
-
-func (d *data) byte() (n byte, ok bool) {
- p := d.read(1)
- if len(p) < 1 {
- d.error = true
- return 0, false
- }
- return p[0], true
-}
-
-// Make a string by stopping at the first NUL
-func byteString(p []byte) string {
- for i := 0; i < len(p); i++ {
- if p[i] == 0 {
- return string(p[0:i])
- }
- }
- return string(p)
-}
-
-var badData = errors.New("malformed time zone information")
-
-func loadZoneData(bytes []byte) (l *Location, err error) {
- d := data{bytes, false}
-
- // 4-byte magic "TZif"
- if magic := d.read(4); string(magic) != "TZif" {
- return nil, badData
- }
-
- // 1-byte version, then 15 bytes of padding
- var p []byte
- if p = d.read(16); len(p) != 16 || p[0] != 0 && p[0] != '2' {
- return nil, badData
- }
-
- // six big-endian 32-bit integers:
- // number of UTC/local indicators
- // number of standard/wall indicators
- // number of leap seconds
- // number of transition times
- // number of local time zones
- // number of characters of time zone abbrev strings
- const (
- NUTCLocal = iota
- NStdWall
- NLeap
- NTime
- NZone
- NChar
- )
- var n [6]int
- for i := 0; i < 6; i++ {
- nn, ok := d.big4()
- if !ok {
- return nil, badData
- }
- n[i] = int(nn)
- }
-
- // Transition times.
- txtimes := data{d.read(n[NTime] * 4), false}
-
- // Time zone indices for transition times.
- txzones := d.read(n[NTime])
-
- // Zone info structures
- zonedata := data{d.read(n[NZone] * 6), false}
-
- // Time zone abbreviations.
- abbrev := d.read(n[NChar])
-
- // Leap-second time pairs
- d.read(n[NLeap] * 8)
-
- // Whether tx times associated with local time types
- // are specified as standard time or wall time.
- isstd := d.read(n[NStdWall])
-
- // Whether tx times associated with local time types
- // are specified as UTC or local time.
- isutc := d.read(n[NUTCLocal])
-
- if d.error { // ran out of data
- return nil, badData
- }
-
- // If version == 2, the entire file repeats, this time using
- // 8-byte ints for txtimes and leap seconds.
- // We won't need those until 2106.
-
- // Now we can build up a useful data structure.
- // First the zone information.
- // utcoff[4] isdst[1] nameindex[1]
- zone := make([]zone, n[NZone])
- for i := range zone {
- var ok bool
- var n uint32
- if n, ok = zonedata.big4(); !ok {
- return nil, badData
- }
- zone[i].offset = int(n)
- var b byte
- if b, ok = zonedata.byte(); !ok {
- return nil, badData
- }
- zone[i].isDST = b != 0
- if b, ok = zonedata.byte(); !ok || int(b) >= len(abbrev) {
- return nil, badData
- }
- zone[i].name = byteString(abbrev[b:])
- }
-
- // Now the transition time info.
- tx := make([]zoneTrans, n[NTime])
- for i := range tx {
- var ok bool
- var n uint32
- if n, ok = txtimes.big4(); !ok {
- return nil, badData
- }
- tx[i].when = int64(int32(n))
- if int(txzones[i]) >= len(zone) {
- return nil, badData
- }
- tx[i].index = txzones[i]
- if i < len(isstd) {
- tx[i].isstd = isstd[i] != 0
- }
- if i < len(isutc) {
- tx[i].isutc = isutc[i] != 0
- }
- }
-
- // Commited to succeed.
- l = &Location{zone: zone, tx: tx}
-
- // Fill in the cache with information about right now,
- // since that will be the most common lookup.
- sec, _ := now()
- for i := range tx {
- if tx[i].when <= sec && (i+1 == len(tx) || sec < tx[i+1].when) {
- l.cacheStart = tx[i].when
- l.cacheEnd = 1<<63 - 1
- if i+1 < len(tx) {
- l.cacheEnd = tx[i+1].when
- }
- l.cacheZone = &l.zone[tx[i].index]
- }
- }
-
- return l, nil
-}
-
-func loadZoneFile(name string) (l *Location, err error) {
- buf, err := readFile(name)
- if err != nil {
- return
- }
- return loadZoneData(buf)
-}
-
func initTestingZone() {
syscall.Setenv("TZ", "America/Los_Angeles")
initLocal()
"/usr/share/zoneinfo/",
"/usr/share/lib/zoneinfo/",
"/usr/lib/locale/TZ/",
+ runtime.GOROOT() + "/lib/time/zoneinfo/",
}
func initLocal() {
tz, ok := syscall.Getenv("TZ")
switch {
case !ok:
- z, err := loadZoneFile("/etc/localtime")
+ z, err := loadZoneFile("", "/etc/localtime")
if err == nil {
localLoc = *z
localLoc.name = "Local"
func loadLocation(name string) (*Location, error) {
for _, zoneDir := range zoneDirs {
- if z, err := loadZoneFile(zoneDir + name); err == nil {
+ if z, err := loadZoneFile(zoneDir, name); err == nil {
z.name = name
return z, nil
}
import (
"errors"
+ "runtime"
"syscall"
)
initLocalFromTZI(&i)
}
-// TODO(rsc): Implement.
func loadLocation(name string) (*Location, error) {
+ if z, err := loadZoneFile(runtime.GOROOT()+`\lib\time\zoneinfo.zip`, name); err == nil {
+ z.name = name
+ return z, nil
+ }
return nil, errors.New("unknown time zone " + name)
}
#include "go-type.h"
MHeap runtime_mheap;
+
extern MStats mstats; // defined in extern.go
extern volatile int32 runtime_MemProfileRate
ret = runtime_mallocgc(typ->__size, flag, 1, 1);
}
-func Alloc(n uintptr) (p *byte) {
- p = runtime_malloc(n);
-}
-
-func Free(p *byte) {
- runtime_free(p);
-}
-
-func Lookup(p *byte) (base *byte, size uintptr) {
- runtime_mlookup(p, &base, &size, nil);
-}
-
func GC() {
runtime_gc(1);
}
uint64 heap_sys; // bytes obtained from system
uint64 heap_idle; // bytes in idle spans
uint64 heap_inuse; // bytes in non-idle spans
+ uint64 heap_released; // bytes released to the OS
uint64 heap_objects; // total number of allocated objects
// Statistics about allocation of low-level fixed-size structures.
// Statistics about garbage collector.
// Protected by stopping the world during GC.
uint64 next_gc; // next GC (in heap_alloc time)
+ uint64 last_gc; // last GC (in absolute time)
uint64 pause_total_ns;
uint64 pause_ns[256];
uint32 numgc;
{
MSpan *next; // in a span linked list
MSpan *prev; // in a span linked list
- MSpan *allnext; // in the list of all spans
+ MSpan *allnext; // in the list of all spans
PageID start; // starting page number
uintptr npages; // number of pages in span
MLink *freelist; // list of free objects
uint32 ref; // number of allocated objects in this span
uint32 sizeclass; // size class
uint32 state; // MSpanInUse etc
- byte *limit; // end of data in span
+ int64 unusedsince; // First time spotted by GC in MSpanFree state
+ uintptr npreleased; // number of pages released to the OS
+ byte *limit; // end of data in span
};
void runtime_MSpan_Init(MSpan *span, PageID start, uintptr npages);
void runtime_MGetSizeClassInfo(int32 sizeclass, uintptr *size, int32 *npages, int32 *nobj);
void* runtime_MHeap_SysAlloc(MHeap *h, uintptr n);
void runtime_MHeap_MapBits(MHeap *h);
+void runtime_MHeap_Scavenger(void*);
void* runtime_mallocgc(uintptr size, uint32 flag, int32 dogc, int32 zeroed);
int32 runtime_mlookup(void *v, byte **base, uintptr *size, MSpan **s);
void runtime_MProf_Malloc(void*, uintptr);
void runtime_MProf_Free(void*, uintptr);
+void runtime_MProf_GC(void);
void runtime_MProf_Mark(void (*scan)(byte *, int64));
int32 runtime_helpgc(bool*);
void runtime_gchelper(void);
-// Malloc profiling settings.
-// Must match definition in extern.go.
-enum {
- MProf_None = 0,
- MProf_Sample = 1,
- MProf_All = 2,
-};
-extern int32 runtime_malloc_profile;
-
struct __go_func_type;
bool runtime_getfinalizer(void *p, bool del, void (**fn)(void*), const struct __go_func_type **ft);
void runtime_walkfintab(void (*fn)(void*), void (*scan)(byte *, int64));
#define bitMask (bitBlockBoundary | bitAllocated | bitMarked | bitSpecial)
+// Holding worldsema grants an M the right to try to stop the world.
+// The procedure is:
+//
+// runtime_semacquire(&runtime_worldsema);
+// m->gcing = 1;
+// runtime_stoptheworld();
+//
+// ... do stuff ...
+//
+// m->gcing = 0;
+// runtime_semrelease(&runtime_worldsema);
+// runtime_starttheworld();
+//
+uint32 runtime_worldsema = 1;
+
// TODO: Make these per-M.
static uint64 nhandoff;
Finalizer fin[1];
};
-
static G *fing;
static FinBlock *finq; // list of finalizers that are to be executed
static FinBlock *finc; // cache of free blocks
byte *p;
MCache *c;
byte *arena_start;
+ int64 now;
m = runtime_m();
arena_start = runtime_mheap.arena_start;
+ now = runtime_nanotime();
for(;;) {
s = work.spans;
if(!runtime_casp(&work.spans, s, s->allnext))
continue;
+ // Stamp newly unused spans. The scavenger will use that
+ // info to potentially give back some pages to the OS.
+ if(s->state == MSpanFree && s->unusedsince == 0)
+ s->unusedsince = now;
+
if(s->state != MSpanInUse)
continue;
runtime_notewakeup(&work.alldone);
}
-// Semaphore, not Lock, so that the goroutine
-// reschedules when there is contention rather
-// than spinning.
-static uint32 gcsema = 1;
-
// Initialized from $GOGC. GOGC=off means no gc.
//
// Next gc is after we've allocated an extra amount of
if(gcpercent < 0)
return;
- runtime_semacquire(&gcsema);
+ runtime_semacquire(&runtime_worldsema);
if(!force && mstats.heap_alloc < mstats.next_gc) {
- runtime_semrelease(&gcsema);
+ runtime_semrelease(&runtime_worldsema);
return;
}
obj1 = mstats.nmalloc - mstats.nfree;
t3 = runtime_nanotime();
+ mstats.last_gc = t3;
mstats.pause_ns[mstats.numgc%nelem(mstats.pause_ns)] = t3 - t0;
mstats.pause_total_ns += t3 - t0;
mstats.numgc++;
(unsigned long long) mstats.nmalloc, (unsigned long long)mstats.nfree,
(unsigned long long) nhandoff);
}
-
- runtime_semrelease(&gcsema);
+
+ runtime_MProf_GC();
+ runtime_semrelease(&runtime_worldsema);
// If we could have used another helper proc, start one now,
// in the hope that it will be available next time.
{
M *m;
- // Have to acquire gcsema to stop the world,
+ // Have to acquire worldsema to stop the world,
// because stoptheworld can only be used by
// one goroutine at a time, and there might be
// a pending garbage collection already calling it.
- runtime_semacquire(&gcsema);
+ runtime_semacquire(&runtime_worldsema);
m = runtime_m();
m->gcing = 1;
runtime_stoptheworld();
cachestats();
*stats = mstats;
m->gcing = 0;
- runtime_semrelease(&gcsema);
+ runtime_semrelease(&runtime_worldsema);
runtime_starttheworld(false);
}
runtime_MSpanList_Remove(s);
s->state = MSpanInUse;
mstats.heap_idle -= s->npages<<PageShift;
+ mstats.heap_released -= s->npreleased<<PageShift;
+ s->npreleased = 0;
if(s->npages > npage) {
// Trim extra and put it back in the heap.
}
mstats.heap_idle += s->npages<<PageShift;
s->state = MSpanFree;
+ s->unusedsince = 0;
+ s->npreleased = 0;
runtime_MSpanList_Remove(s);
sp = (uintptr*)(s->start<<PageShift);
*tp |= *sp; // propagate "needs zeroing" mark
s->start = t->start;
s->npages += t->npages;
+ s->npreleased = t->npreleased; // absorb released pages
p -= t->npages;
h->map[p] = s;
runtime_MSpanList_Remove(t);
tp = (uintptr*)(t->start<<PageShift);
*sp |= *tp; // propagate "needs zeroing" mark
s->npages += t->npages;
+ s->npreleased += t->npreleased;
h->map[p + s->npages - 1] = s;
runtime_MSpanList_Remove(t);
t->state = MSpanDead;
runtime_MSpanList_Insert(&h->free[s->npages], s);
else
runtime_MSpanList_Insert(&h->large, s);
+}
- // TODO(rsc): IncrementalScavenge() to return memory to OS.
+// Release (part of) unused memory to OS.
+// Goroutine created in runtime_schedinit.
+// Loop forever.
+void
+runtime_MHeap_Scavenger(void* dummy)
+{
+ MHeap *h;
+ MSpan *s, *list;
+ uint64 tick, now, forcegc, limit;
+ uint32 k, i;
+ uintptr released, sumreleased;
+ const byte *env;
+ bool trace;
+ Note note;
+
+ USED(dummy);
+
+ // If we go two minutes without a garbage collection, force one to run.
+ forcegc = 2*60*1e9;
+ // If a span goes unused for 5 minutes after a garbage collection,
+ // we hand it back to the operating system.
+ limit = 5*60*1e9;
+ // Make wake-up period small enough for the sampling to be correct.
+ if(forcegc < limit)
+ tick = forcegc/2;
+ else
+ tick = limit/2;
+
+ trace = false;
+ env = runtime_getenv("GOGCTRACE");
+ if(env != nil)
+ trace = runtime_atoi(env) > 0;
+
+ h = &runtime_mheap;
+ for(k=0;; k++) {
+ runtime_noteclear(¬e);
+ runtime_entersyscall();
+ runtime_notetsleep(¬e, tick);
+ runtime_exitsyscall();
+
+ runtime_lock(h);
+ now = runtime_nanotime();
+ if(now - mstats.last_gc > forcegc) {
+ runtime_unlock(h);
+ runtime_gc(1);
+ runtime_lock(h);
+ now = runtime_nanotime();
+ if (trace)
+ runtime_printf("scvg%d: GC forced\n", k);
+ }
+ sumreleased = 0;
+ for(i=0; i < nelem(h->free)+1; i++) {
+ if(i < nelem(h->free))
+ list = &h->free[i];
+ else
+ list = &h->large;
+ if(runtime_MSpanList_IsEmpty(list))
+ continue;
+ for(s=list->next; s != list; s=s->next) {
+ if(s->unusedsince != 0 && (now - s->unusedsince) > limit) {
+ released = (s->npages - s->npreleased) << PageShift;
+ mstats.heap_released += released;
+ sumreleased += released;
+ s->npreleased = s->npages;
+ runtime_SysUnused((void*)(s->start << PageShift), s->npages << PageShift);
+ }
+ }
+ }
+ runtime_unlock(h);
+
+ if(trace) {
+ if(sumreleased > 0)
+ runtime_printf("scvg%d: %p MB released\n", k, (void*)(sumreleased>>20));
+ runtime_printf("scvg%d: inuse: %lld, idle: %lld, sys: %lld, released: %lld, consumed: %lld (MB)\n",
+ k, (long long)(mstats.heap_inuse>>20), (long long)(mstats.heap_idle>>20), (long long)(mstats.heap_sys>>20),
+ (long long)(mstats.heap_released>>20), (long long)((mstats.heap_sys - mstats.heap_released)>>20));
+ }
+ }
}
// Initialize a new span with the given start and npages.
span->ref = 0;
span->sizeclass = 0;
span->state = 0;
+ span->unusedsince = 0;
+ span->npreleased = 0;
}
// Initialize an empty doubly-linked list.
uintptr frees;
uintptr alloc_bytes;
uintptr free_bytes;
+ uintptr recent_allocs; // since last gc
+ uintptr recent_frees;
+ uintptr recent_alloc_bytes;
+ uintptr recent_free_bytes;
uintptr hash;
uintptr nstk;
uintptr stk[1];
// Return the bucket for stk[0:nstk], allocating new bucket if needed.
static Bucket*
-stkbucket(uintptr *stk, int32 nstk)
+stkbucket(uintptr *stk, int32 nstk, bool alloc)
{
int32 i;
uintptr h;
runtime_mcmp((byte*)b->stk, (byte*)stk, nstk*sizeof stk[0]) == 0)
return b;
+ if(!alloc)
+ return nil;
+
b = runtime_mallocgc(sizeof *b + nstk*sizeof stk[0], FlagNoProfiling, 0, 1);
bucketmem += sizeof *b + nstk*sizeof stk[0];
runtime_memmove(b->stk, stk, nstk*sizeof stk[0]);
return b;
}
+// Record that a gc just happened: all the 'recent' statistics are now real.
+void
+runtime_MProf_GC(void)
+{
+ Bucket *b;
+
+ runtime_lock(&proflock);
+ for(b=buckets; b; b=b->allnext) {
+ b->allocs += b->recent_allocs;
+ b->frees += b->recent_frees;
+ b->alloc_bytes += b->recent_alloc_bytes;
+ b->free_bytes += b->recent_free_bytes;
+ b->recent_allocs = 0;
+ b->recent_frees = 0;
+ b->recent_alloc_bytes = 0;
+ b->recent_free_bytes = 0;
+ }
+ runtime_unlock(&proflock);
+}
+
// Map from pointer to Bucket* that allocated it.
// Three levels:
// Linked-list hash table for top N-20 bits.
nstk = 0;
#endif
runtime_lock(&proflock);
- b = stkbucket(stk, nstk);
- b->allocs++;
- b->alloc_bytes += size;
+ b = stkbucket(stk, nstk, true);
+ b->recent_allocs++;
+ b->recent_alloc_bytes += size;
setaddrbucket((uintptr)p, b);
runtime_unlock(&proflock);
m = runtime_m();
runtime_lock(&proflock);
b = getaddrbucket((uintptr)p);
if(b != nil) {
- b->frees++;
- b->free_bytes += size;
+ b->recent_frees++;
+ b->recent_free_bytes += size;
}
runtime_unlock(&proflock);
m = runtime_m();
scan((byte*)&addrfree, sizeof addrfree);
}
-// Must match ThreadProfileRecord in debug.go.
+// Must match StackRecord in debug.go.
typedef struct TRecord TRecord;
struct TRecord {
uintptr stk[32];
};
-func ThreadProfile(p Slice) (n int32, ok bool) {
+func ThreadCreateProfile(p Slice) (n int32, ok bool) {
TRecord *r;
M *first, *m;
}
}
}
+
+func Stack(b Slice, all bool) (n int32) {
+ byte *pc, *sp;
+
+ sp = runtime_getcallersp(&b);
+ pc = runtime_getcallerpc(&b);
+
+ if(all) {
+ runtime_semacquire(&runtime_worldsema);
+ runtime_m()->gcing = 1;
+ runtime_stoptheworld();
+ }
+
+ if(b.__count == 0)
+ n = 0;
+ else{
+ G* g = runtime_g();
+ g->writebuf = (byte*)b.__values;
+ g->writenbuf = b.__count;
+ USED(pc);
+ USED(sp);
+ // runtime_goroutineheader(g);
+ // runtime_traceback(pc, sp, 0, g);
+ // if(all)
+ // runtime_tracebackothers(g);
+ n = b.__count - g->writenbuf;
+ g->writebuf = nil;
+ g->writenbuf = 0;
+ }
+
+ if(all) {
+ runtime_m()->gcing = 0;
+ runtime_semrelease(&runtime_worldsema);
+ runtime_starttheworld(false);
+ }
+}
+
+static void
+saveg(byte *pc, byte *sp, G *g, TRecord *r)
+{
+ int32 n;
+
+ USED(pc);
+ USED(sp);
+ USED(g);
+ // n = runtime_gentraceback(pc, sp, 0, g, 0, r->stk, nelem(r->stk));
+ n = 0;
+ if((size_t)n < nelem(r->stk))
+ r->stk[n] = 0;
+}
+
+func GoroutineProfile(b Slice) (n int32, ok bool) {
+ byte *pc, *sp;
+ TRecord *r;
+ G *gp;
+
+ sp = runtime_getcallersp(&b);
+ pc = runtime_getcallerpc(&b);
+
+ ok = false;
+ n = runtime_gcount();
+ if(n <= b.__count) {
+ runtime_semacquire(&runtime_worldsema);
+ runtime_m()->gcing = 1;
+ runtime_stoptheworld();
+
+ n = runtime_gcount();
+ if(n <= b.__count) {
+ G* g = runtime_g();
+ ok = true;
+ r = (TRecord*)b.__values;
+ saveg(pc, sp, g, r++);
+ for(gp = runtime_allg; gp != nil; gp = gp->alllink) {
+ if(gp == g || gp->status == Gdead)
+ continue;
+ //saveg(gp->sched.pc, gp->sched.sp, gp, r++);
+ r++;
+ }
+ }
+
+ runtime_m()->gcing = 0;
+ runtime_semrelease(&runtime_worldsema);
+ runtime_starttheworld(false);
+ }
+}
+
}
}
+// Keep trace of scavenger's goroutine for deadlock detection.
+static G *scvg;
+
// The bootstrap sequence is:
//
// call osinit
// Can not enable GC until all roots are registered.
// mstats.enablegc = 1;
m->nomemprof--;
+
+ scvg = __go_go(runtime_MHeap_Scavenger, nil);
}
extern void main_init(void) __asm__ ("__go_init_main");
// Add to runtime_allm so garbage collector doesn't free m
// when it is just in a register or thread-local storage.
m->alllink = runtime_allm;
- // runtime_Cgocalls() iterates over allm w/o schedlock,
+ // runtime_NumCgoCall() iterates over allm w/o schedlock,
// so we need to publish it safely.
runtime_atomicstorep(&runtime_allm, m);
}
mput(m);
}
- v = runtime_atomicload(&runtime_sched.atomic);
- if(runtime_sched.grunning == 0)
- runtime_throw("all goroutines are asleep - deadlock!");
+ // Look for deadlock situation: one single active g which happens to be scvg.
+ if(runtime_sched.grunning == 1 && runtime_sched.gwait == 0) {
+ if(scvg->status == Grunning || scvg->status == Gsyscall)
+ runtime_throw("all goroutines are asleep - deadlock!");
+ }
+
m->nextg = nil;
m->waitnextg = 1;
runtime_noteclear(&m->havenextg);
// Entersyscall might have decremented mcpu too, but if so
// it will see the waitstop and take the slow path.
// Exitsyscall never increments mcpu beyond mcpumax.
+ v = runtime_atomicload(&runtime_sched.atomic);
if(atomic_waitstop(v) && atomic_mcpu(v) <= atomic_mcpumax(v)) {
// set waitstop = 0 (known to be 1)
runtime_xadd(&runtime_sched.atomic, -1<<waitstopShift);
return m->id;
}
-int32 runtime_Goroutines (void)
- __asm__ ("libgo_runtime.runtime.Goroutines");
+int32 runtime_NumGoroutine (void)
+ __asm__ ("libgo_runtime.runtime.NumGoroutine");
+
+int32
+runtime_NumGoroutine()
+{
+ return runtime_sched.gcount;
+}
int32
-runtime_Goroutines()
+runtime_gcount(void)
{
return runtime_sched.gcount;
}
M* lockedm;
M* idlem;
// int32 sig;
+ int32 writenbuf;
+ byte* writebuf;
// uintptr sigcode0;
// uintptr sigcode1;
// uintptr sigpc;
enum
{
SigNotify = 1<<0, // let signal.Notify have signal, even if from kernel
- SigKill = 1<<1, // if signal.Notify doesn't take it, exit quietly
- SigThrow = 1<<2, // if signal.Notify doesn't take it, exit loudly
- SigPanic = 1<<3, // if the signal is from the kernel, panic
+ SigKill = 1<<1, // if signal.Notify doesn't take it, exit quietly
+ SigThrow = 1<<2, // if signal.Notify doesn't take it, exit loudly
+ SigPanic = 1<<3, // if the signal is from the kernel, panic
SigDefault = 1<<4, // if the signal isn't explicitly requested, don't monitor it
};
void* runtime_mal(uintptr);
void runtime_schedinit(void);
void runtime_initsig(void);
+void runtime_sigenable(uint32 sig);
String runtime_gostringnocopy(const byte*);
void* runtime_mstart(void*);
G* runtime_malg(int32, byte**, size_t*);
void runtime_stoptheworld(void);
void runtime_starttheworld(bool);
+extern uint32 runtime_worldsema;
G* __go_go(void (*pfn)(void*), void*);
/*
#define runtime_munmap munmap
#define runtime_madvise madvise
#define runtime_memclr(buf, size) __builtin_memset((buf), 0, (size))
+#define runtime_getcallerpc(p) __builtin_return_address(0)
#ifdef __rtems__
void __wrap_rtems_task_variable_add(void **);
#define runtime_exit(s) exit(s)
MCache* runtime_allocmcache(void);
void free(void *v);
-struct __go_func_type;
-bool runtime_addfinalizer(void*, void(*fn)(void*), const struct __go_func_type *);
#define runtime_cas(pval, old, new) __sync_bool_compare_and_swap (pval, old, new)
#define runtime_casp(pval, old, new) __sync_bool_compare_and_swap (pval, old, new)
#define runtime_xadd(p, v) __sync_add_and_fetch (p, v)
#define runtime_atomicloadp(p) __atomic_load_n (p, __ATOMIC_SEQ_CST)
#define runtime_atomicstorep(p, v) __atomic_store_n (p, v, __ATOMIC_SEQ_CST)
+struct __go_func_type;
+bool runtime_addfinalizer(void*, void(*fn)(void*), const struct __go_func_type *);
+#define runtime_getcallersp(p) __builtin_frame_address(1)
+int32 runtime_mcount(void);
+int32 runtime_gcount(void);
void runtime_dopanic(int32) __attribute__ ((noreturn));
void runtime_startpanic(void);
void runtime_ready(G*);
// See Mullender and Cox, ``Semaphores in Plan 9,''
// http://swtch.com/semaphore.pdf
-package runtime
+package sync
#include "runtime.h"
#include "arch.h"
runtime_ready(s->g);
}
-func Semacquire(addr *uint32) {
+func runtime_Semacquire(addr *uint32) {
runtime_semacquire(addr);
}
-func Semrelease(addr *uint32) {
+func runtime_Semrelease(addr *uint32) {
runtime_semrelease(addr);
}
// Special case: want everything.
for(i=0; (size_t)i<nelem(sig.wanted); i++)
sig.wanted[i] = ~(uint32)0;
+ runtime_sigenable(s);
return;
}
if(s >= nelem(sig.wanted)*32)
return;
sig.wanted[s/32] |= 1U<<(s&31);
+ runtime_sigenable(s);
}