libgo: Update to weekly.2012-02-22 release.
authorIan Lance Taylor <ian@gcc.gnu.org>
Fri, 2 Mar 2012 20:01:37 +0000 (20:01 +0000)
committerIan Lance Taylor <ian@gcc.gnu.org>
Fri, 2 Mar 2012 20:01:37 +0000 (20:01 +0000)
From-SVN: r184819

215 files changed:
gcc/testsuite/go.test/test/env.go
gcc/testsuite/go.test/test/fixedbugs/bug262.go
gcc/testsuite/go.test/test/malloc1.go [deleted file]
gcc/testsuite/go.test/test/mallocrand.go [deleted file]
gcc/testsuite/go.test/test/mallocrep.go [deleted file]
gcc/testsuite/go.test/test/mallocrep1.go [deleted file]
libgo/MERGE
libgo/Makefile.am
libgo/Makefile.in
libgo/go/bufio/bufio.go
libgo/go/bufio/bufio_test.go
libgo/go/bytes/bytes.go
libgo/go/bytes/bytes_test.go
libgo/go/bytes/example_test.go
libgo/go/compress/flate/deflate_test.go
libgo/go/container/heap/example_test.go
libgo/go/crypto/dsa/dsa.go
libgo/go/crypto/md5/md5_test.go
libgo/go/crypto/sha1/sha1_test.go
libgo/go/crypto/tls/handshake_client.go
libgo/go/crypto/tls/handshake_server.go
libgo/go/database/sql/convert.go
libgo/go/database/sql/driver/driver.go
libgo/go/database/sql/driver/types.go
libgo/go/database/sql/fakedb_test.go
libgo/go/database/sql/sql.go
libgo/go/debug/dwarf/open.go
libgo/go/debug/dwarf/testdata/typedef.c
libgo/go/debug/dwarf/testdata/typedef.elf
libgo/go/debug/dwarf/testdata/typedef.macho
libgo/go/debug/dwarf/type.go
libgo/go/debug/dwarf/type_test.go
libgo/go/debug/gosym/pclntab_test.go
libgo/go/encoding/gob/decode.go
libgo/go/encoding/gob/encoder_test.go
libgo/go/encoding/gob/type.go
libgo/go/encoding/json/decode.go
libgo/go/encoding/json/decode_test.go
libgo/go/encoding/json/encode.go
libgo/go/encoding/xml/marshal.go
libgo/go/encoding/xml/read.go
libgo/go/errors/errors_test.go
libgo/go/exp/inotify/inotify_linux_test.go
libgo/go/exp/norm/composition.go
libgo/go/exp/norm/composition_test.go
libgo/go/exp/norm/input.go
libgo/go/exp/norm/iter.go [new file with mode: 0644]
libgo/go/exp/norm/iter_test.go [new file with mode: 0644]
libgo/go/exp/norm/normalize.go
libgo/go/exp/norm/normalize_test.go
libgo/go/exp/norm/normregtest.go
libgo/go/exp/proxy/socks5.go
libgo/go/exp/terminal/terminal.go
libgo/go/exp/winfsnotify/winfsnotify_test.go
libgo/go/go/doc/example.go
libgo/go/go/doc/reader.go
libgo/go/go/doc/synopsis.go [new file with mode: 0644]
libgo/go/go/doc/synopsis_test.go [new file with mode: 0644]
libgo/go/go/doc/testdata/benchmark.go
libgo/go/go/doc/testdata/testing.1.golden
libgo/go/go/doc/testdata/testing.go
libgo/go/go/parser/parser.go
libgo/go/go/printer/nodes.go
libgo/go/go/printer/printer.go
libgo/go/go/printer/printer_test.go
libgo/go/go/printer/testdata/comments.golden
libgo/go/go/printer/testdata/comments.input
libgo/go/go/printer/testdata/expressions.golden
libgo/go/go/printer/testdata/expressions.raw
libgo/go/go/scanner/scanner.go
libgo/go/html/template/clone.go [deleted file]
libgo/go/html/template/clone_test.go
libgo/go/html/template/content.go
libgo/go/html/template/content_test.go
libgo/go/html/template/doc.go
libgo/go/html/template/escape.go
libgo/go/html/template/template.go
libgo/go/image/decode_example_test.go [new file with mode: 0644]
libgo/go/image/ycbcr_test.go
libgo/go/io/ioutil/tempfile.go
libgo/go/log/syslog/syslog.go
libgo/go/log/syslog/syslog_test.go
libgo/go/log/syslog/syslog_unix.go
libgo/go/math/big/nat_test.go
libgo/go/math/rand/rand.go
libgo/go/math/rand/rand_test.go
libgo/go/net/dialgoogle_test.go
libgo/go/net/fd.go
libgo/go/net/fd_windows.go
libgo/go/net/file.go
libgo/go/net/file_plan9.go
libgo/go/net/hosts_test.go
libgo/go/net/http/cookie_test.go
libgo/go/net/http/example_test.go [new file with mode: 0644]
libgo/go/net/http/fs_test.go
libgo/go/net/http/httputil/persist.go
libgo/go/net/http/pprof/pprof.go
libgo/go/net/http/request.go
libgo/go/net/http/serve_test.go
libgo/go/net/http/server.go
libgo/go/net/http/transport_test.go
libgo/go/net/interface_linux.go
libgo/go/net/interface_test.go
libgo/go/net/ipraw_test.go
libgo/go/net/iprawsock_plan9.go
libgo/go/net/iprawsock_posix.go
libgo/go/net/ipsock_plan9.go
libgo/go/net/ipsock_posix.go
libgo/go/net/lookup_plan9.go
libgo/go/net/lookup_test.go
libgo/go/net/multicast_test.go
libgo/go/net/net.go
libgo/go/net/parse.go
libgo/go/net/rpc/server_test.go
libgo/go/net/server_test.go
libgo/go/net/smtp/smtp.go
libgo/go/net/smtp/smtp_test.go
libgo/go/net/sock.go
libgo/go/net/tcpsock_plan9.go
libgo/go/net/tcpsock_posix.go
libgo/go/net/testdata/hosts [new file with mode: 0644]
libgo/go/net/testdata/igmp [new file with mode: 0644]
libgo/go/net/testdata/igmp6 [new file with mode: 0644]
libgo/go/net/textproto/reader.go
libgo/go/net/textproto/reader_test.go
libgo/go/net/textproto/textproto.go
libgo/go/net/udpsock_plan9.go
libgo/go/net/udpsock_posix.go
libgo/go/net/unixsock_plan9.go
libgo/go/net/unixsock_posix.go
libgo/go/net/url/url.go
libgo/go/net/url/url_test.go
libgo/go/old/netchan/netchan_test.go
libgo/go/os/dir_plan9.go
libgo/go/os/env.go
libgo/go/os/error.go
libgo/go/os/error_plan9.go
libgo/go/os/error_posix.go
libgo/go/os/exec/exec.go
libgo/go/os/exec/lp_plan9.go
libgo/go/os/exec/lp_unix.go
libgo/go/os/exec/lp_windows.go
libgo/go/os/exec_plan9.go
libgo/go/os/exec_posix.go
libgo/go/os/exec_unix.go
libgo/go/os/exec_windows.go
libgo/go/os/file.go
libgo/go/os/file_plan9.go
libgo/go/os/file_posix.go
libgo/go/os/file_unix.go
libgo/go/os/getwd.go
libgo/go/os/os_test.go
libgo/go/os/path.go
libgo/go/os/path_test.go
libgo/go/os/signal/signal_stub.go [new file with mode: 0644]
libgo/go/os/stat_plan9.go
libgo/go/path/example_test.go
libgo/go/path/filepath/match.go
libgo/go/path/filepath/path.go
libgo/go/path/filepath/path_test.go
libgo/go/path/match.go
libgo/go/path/path.go
libgo/go/runtime/debug.go
libgo/go/runtime/extern.go
libgo/go/runtime/malloc1.go [new file with mode: 0644]
libgo/go/runtime/mallocrand.go [new file with mode: 0644]
libgo/go/runtime/mallocrep.go [new file with mode: 0644]
libgo/go/runtime/mallocrep1.go [new file with mode: 0644]
libgo/go/runtime/mem.go
libgo/go/runtime/pprof/pprof.go
libgo/go/runtime/sema_test.go [deleted file]
libgo/go/sort/example_interface_test.go [new file with mode: 0644]
libgo/go/sort/example_reverse_test.go [new file with mode: 0644]
libgo/go/sort/example_test.go
libgo/go/strconv/atof.go
libgo/go/strconv/itoa_test.go
libgo/go/strings/example_test.go
libgo/go/sync/cond.go
libgo/go/sync/example_test.go [new file with mode: 0644]
libgo/go/sync/export_test.go [new file with mode: 0644]
libgo/go/sync/mutex.go
libgo/go/sync/mutex_test.go
libgo/go/sync/runtime.go [new file with mode: 0644]
libgo/go/sync/runtime_sema_test.go [new file with mode: 0644]
libgo/go/sync/rwmutex.go
libgo/go/sync/waitgroup.go
libgo/go/testing/testing.go
libgo/go/text/tabwriter/example_test.go [new file with mode: 0644]
libgo/go/text/tabwriter/tabwriter.go
libgo/go/text/tabwriter/tabwriter_test.go
libgo/go/text/template/doc.go
libgo/go/text/template/exec.go
libgo/go/text/template/exec_test.go
libgo/go/text/template/multi_test.go
libgo/go/text/template/parse/parse.go
libgo/go/text/template/template.go
libgo/go/time/example_test.go
libgo/go/time/sys_plan9.go
libgo/go/time/sys_unix.go
libgo/go/time/sys_windows.go
libgo/go/time/tick_test.go
libgo/go/time/zoneinfo.go
libgo/go/time/zoneinfo_plan9.go
libgo/go/time/zoneinfo_read.go [new file with mode: 0644]
libgo/go/time/zoneinfo_unix.go
libgo/go/time/zoneinfo_windows.go
libgo/runtime/malloc.goc
libgo/runtime/malloc.h
libgo/runtime/mgc0.c
libgo/runtime/mheap.c
libgo/runtime/mprof.goc
libgo/runtime/proc.c
libgo/runtime/runtime.h
libgo/runtime/sema.goc
libgo/runtime/sigqueue.goc

index a4b9d05d87dce7025a7445d4a04a43e8988e744c..3c8e4232838e017faaf17d2c6a0f613cb9907369 100644 (file)
@@ -4,6 +4,9 @@
 // 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 (
@@ -12,18 +15,14 @@ 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)
        }
 }
index f5f2c35532222c56525a178e647a54abc01c51c2..ebca7905f9ad3f4167c10d1826b0153546bc3e65 100644 (file)
@@ -7,7 +7,7 @@
 package main
 
 import (
-       "os"
+       "errors"
        "strconv"
 )
 
@@ -44,7 +44,7 @@ func main() {
        }
        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)
diff --git a/gcc/testsuite/go.test/test/malloc1.go b/gcc/testsuite/go.test/test/malloc1.go
deleted file mode 100644 (file)
index 0f7f0b2..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-// $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))
-       }
-}
diff --git a/gcc/testsuite/go.test/test/mallocrand.go b/gcc/testsuite/go.test/test/mallocrand.go
deleted file mode 100644 (file)
index 69d07ce..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-// $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()
-       }
-}
diff --git a/gcc/testsuite/go.test/test/mallocrep.go b/gcc/testsuite/go.test/test/mallocrep.go
deleted file mode 100644 (file)
index 4188da9..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-// $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
-               }
-       }
-}
diff --git a/gcc/testsuite/go.test/test/mallocrep1.go b/gcc/testsuite/go.test/test/mallocrep1.go
deleted file mode 100644 (file)
index f9d7286..0000000
+++ /dev/null
@@ -1,143 +0,0 @@
-// $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
-                       }
-               }
-       }
-}
index a760ae9c8de8289262e86aa074fa837a65c896fb..9605a8a9040b295ee925be1680d303790adcdc6b 100644 (file)
@@ -1,4 +1,4 @@
-43cf9b39b647
+96bd78e7d35e
 
 The first line of this file holds the Mercurial revision number of the
 last merge done from the master library sources.
index 1167a3f7ed0414366c61b93f8e257454a3090bef..eb764df0eab88e16d24128bd0af4dbe27bae6ef9 100644 (file)
@@ -504,7 +504,7 @@ runtime1.c: $(srcdir)/runtime/runtime1.goc goc2c
        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
@@ -847,6 +847,7 @@ go_sync_files = \
        go/sync/cond.go \
        go/sync/mutex.go \
        go/sync/once.go \
+       go/sync/runtime.go \
        go/sync/rwmutex.go \
        go/sync/waitgroup.go
 
@@ -878,6 +879,7 @@ go_time_files = \
        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 = \
@@ -1091,6 +1093,7 @@ go_exp_norm_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 \
@@ -1132,7 +1135,8 @@ go_go_doc_files = \
        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
@@ -1159,7 +1163,6 @@ go_hash_fnv_files = \
 
 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 \
index b1d1d4c561ee74c6bb598a9d2fa3fa8bd3e5cfff..4604e560c9b6caf128a48b8b4d7e44a9d35e668b 100644 (file)
@@ -1157,6 +1157,7 @@ go_sync_files = \
        go/sync/cond.go \
        go/sync/mutex.go \
        go/sync/once.go \
+       go/sync/runtime.go \
        go/sync/rwmutex.go \
        go/sync/waitgroup.go
 
@@ -1182,6 +1183,7 @@ go_time_files = \
        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 = \
@@ -1427,6 +1429,7 @@ go_exp_norm_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 \
@@ -1474,7 +1477,8 @@ go_go_doc_files = \
        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 \
@@ -1508,7 +1512,6 @@ go_hash_fnv_files = \
 
 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 \
@@ -4318,7 +4321,7 @@ runtime1.c: $(srcdir)/runtime/runtime1.goc goc2c
        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
index 156dddfcf071f37d527b929ad33a202a44e4de3f..6f3b1eec9713a200f42517f0d79e34c4e1b13f29 100644 (file)
@@ -106,9 +106,12 @@ func (b *Reader) Peek(n int) ([]byte, error) {
        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
 }
index 9aec61ec426c9a65715f5d7f3943b355be686fd9..a43cbd23a64d0eae2fd5e2c0d2efaf18b80e8800 100644 (file)
@@ -539,6 +539,27 @@ func TestPeek(t *testing.T) {
        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) {
index e94a0ec5c4f950feec36cce66e66cdc362bbbf0f..7d1426fb4174d8197b7370933a4947e5dc456115 100644 (file)
@@ -13,6 +13,7 @@ import (
 
 // 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) {
@@ -37,6 +38,7 @@ func Compare(a, b []byte) int {
 }
 
 // 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 {
index 2a1d41b910e3f222b091ccefe2eeac734f9edefa..000f235176df8d7a01f42dba4f095dd6cfa9ff4e 100644 (file)
@@ -46,32 +46,39 @@ type BinOpTest struct {
        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)
                }
index 0234a012a4e8187e612dac0c260ea2eebbaaa5de..6fe8cd5a90cc3736eeb7ade9cd648cdf26ca9cef 100644 (file)
@@ -11,18 +11,18 @@ import (
        "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!
 }
index a76e2d930f60694adf9dc745f3a34dc3c627d125..543c5950586ce3e31a49bdf6b1cc4fab8499e605 100644 (file)
@@ -306,6 +306,9 @@ func TestDeflateInflateString(t *testing.T) {
                        t.Error(err)
                }
                testToFromWithLimit(t, gold, test.label, test.limit)
+               if testing.Short() {
+                       break
+               }
        }
 }
 
@@ -363,6 +366,10 @@ func TestWriterDict(t *testing.T) {
 
 // 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)
index c3b8d94cb2acd304c62b5f76e0b5abf7d0140908..2050bc835915d1d3dab107af03354637d0fb37ce 100644 (file)
@@ -57,11 +57,26 @@ func (pq *PriorityQueue) Pop() interface{} {
        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{
@@ -85,21 +100,6 @@ func ExampleInterface() {
                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
 }
index f7c47831790d9d059e53a90ceb89a154b8074117..05766a2f13684411e927eef28dd48ee28b11fd80 100644 (file)
@@ -2,7 +2,7 @@
 // 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 (
index b15e4668c3219eb0e8ded352956afb2a71675cf3..aae875464f92b486f81ca9463f7cffc537933a2a 100644 (file)
@@ -2,9 +2,10 @@
 // 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"
@@ -52,7 +53,7 @@ var golden = []md5Test{
 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)
@@ -69,3 +70,11 @@ func TestGolden(t *testing.T) {
                }
        }
 }
+
+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
+}
index c23df6c41e9de1794d555fe9c5cf90c1987049a2..2dc14ac986807b058922cd6b1f71178238f0d4c6 100644 (file)
@@ -4,9 +4,10 @@
 
 // SHA1 hash algorithm.  See RFC 3174.
 
-package sha1
+package sha1_test
 
 import (
+       "crypto/sha1"
        "fmt"
        "io"
        "testing"
@@ -54,7 +55,7 @@ var golden = []sha1Test{
 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)
@@ -71,3 +72,10 @@ func TestGolden(t *testing.T) {
                }
        }
 }
+
+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
+}
index 687e5ef11b462baab95cf01ce36f1ce74e1fe10d..0d7b806ff5b17da7b69d7b7eedf2c538e76a8a03 100644 (file)
@@ -273,7 +273,7 @@ func (c *Conn) clientHandshake() error {
        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})
@@ -294,7 +294,7 @@ func (c *Conn) clientHandshake() error {
        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)
index fb53767f3e08c50e6cb857e3a6a60387a30a4a3d..23ec5587235b5d2eebe2aa961fffc7924c99e5ee 100644 (file)
@@ -295,7 +295,7 @@ FindCipherSuite:
        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)
@@ -333,7 +333,7 @@ FindCipherSuite:
 
        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})
index 4afa2bef753a217d2edeb9d0fa0ddadc5796befd..bfcb03ccf8d42ac627f6d207d0e0f773f78353e2 100644 (file)
@@ -17,8 +17,8 @@ import (
 // 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)
index b930077605047591d3cc40ab3ee4c1ac435fa73e..7f986b80f2cb99e99c73102ea8b71f7da94731d4 100644 (file)
@@ -6,21 +6,20 @@
 // 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.
@@ -50,11 +49,9 @@ var ErrSkip = errors.New("driver: skip fast-path; continue as if unimplemented")
 // 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
@@ -127,18 +124,17 @@ type Stmt interface {
        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
@@ -162,12 +158,12 @@ type Rows interface {
        // 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.
@@ -190,18 +186,19 @@ func (v RowsAffected) RowsAffected() (int64, error) {
        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")
 }
index ce3c943ead27170b7105f260e97cd0cf407b1647..3305354dfd0d66183d96545bbae100d8c3d52450 100644 (file)
@@ -17,28 +17,28 @@ import (
 // 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.
@@ -59,7 +59,7 @@ var _ ValueConverter = boolType{}
 
 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
@@ -104,7 +104,7 @@ type int32Type struct{}
 
 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:
@@ -137,7 +137,7 @@ var String stringType
 
 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
@@ -151,7 +151,7 @@ type Null struct {
        Converter ValueConverter
 }
 
-func (n Null) ConvertValue(v interface{}) (interface{}, error) {
+func (n Null) ConvertValue(v interface{}) (Value, error) {
        if v == nil {
                return nil, nil
        }
@@ -164,28 +164,17 @@ type NotNull struct {
        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 {
@@ -194,18 +183,9 @@ func IsParameterSubsetType(v interface{}) bool {
        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
        }
@@ -221,7 +201,7 @@ func IsScanSubsetType(v interface{}) bool {
 // 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
@@ -230,18 +210,18 @@ type defaultConverter struct{}
 
 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
        }
index 889e2a25232290562e9373bab72588c0a00ad6c3..fc63f03740ac0a4e68310f0935231040e5dd4d36 100644 (file)
@@ -217,7 +217,7 @@ func (c *fakeConn) Close() error {
        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:
@@ -228,7 +228,7 @@ func checkSubsetTypes(args []interface{}) error {
        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
@@ -379,7 +379,7 @@ func (s *fakeStmt) Close() error {
 
 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
        }
@@ -392,12 +392,12 @@ func (s *fakeStmt) Exec(args []interface{}) (driver.Result, error) {
        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)
        }
@@ -405,7 +405,7 @@ func (s *fakeStmt) Exec(args []interface{}) (driver.Result, error) {
        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")
@@ -441,7 +441,7 @@ func (s *fakeStmt) execInsert(args []interface{}) (driver.Result, error) {
        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
        }
@@ -548,7 +548,7 @@ func (rc *rowsCursor) Columns() []string {
        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")
        }
index f14a98c3cf29eb4a484a50b0094df500f93315fc..62b551d89b537ae098ce9f6723df850d660a85b4 100644 (file)
@@ -62,8 +62,8 @@ func (ns *NullString) Scan(value interface{}) error {
        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
        }
@@ -88,8 +88,8 @@ func (n *NullInt64) Scan(value interface{}) error {
        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
        }
@@ -114,8 +114,8 @@ func (n *NullFloat64) Scan(value interface{}) error {
        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
        }
@@ -140,8 +140,8 @@ func (n *NullBool) Scan(value interface{}) error {
        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
        }
@@ -523,8 +523,13 @@ func (tx *Tx) Exec(query string, args ...interface{}) (Result, error) {
        }
        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
                }
@@ -539,11 +544,6 @@ func (tx *Tx) Exec(query string, args ...interface{}) (Result, error) {
        }
        defer sti.Close()
 
-       sargs, err := subsetTypeArgs(args)
-       if err != nil {
-               return nil, err
-       }
-
        resi, err := sti.Exec(sargs)
        if err != nil {
                return nil, err
@@ -618,19 +618,21 @@ func (s *Stmt) Exec(args ...interface{}) (Result, error) {
                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
                        }
@@ -642,25 +644,25 @@ func (s *Stmt) Exec(args ...interface{}) (Result, error) {
                        // 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
        }
@@ -829,7 +831,7 @@ type Rows struct {
        rowsi       driver.Rows
 
        closed    bool
-       lastcols  []interface{}
+       lastcols  []driver.Value
        lasterr   error
        closeStmt *Stmt // if non-nil, statement to Close on close
 }
@@ -846,7 +848,7 @@ func (rs *Rows) Next() bool {
                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 {
index 9543297e189cd42a2bda67602df4cbd814914f05..37a518b6d376969e5371b88f72aad1779e4957f7 100644 (file)
@@ -31,8 +31,9 @@ type Data struct {
 }
 
 // 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
index 664d021ced5e6b884a2b5e7cbda08cfe7f5afcf0..f05f01564ff8c5f090b8c1bad1ce638bf2f475ef 100644 (file)
@@ -28,8 +28,13 @@ typedef struct my_struct {
        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;
@@ -65,7 +70,8 @@ t_func_void_of_char *a9;
 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;
index 44df8da9bc7c4683d557a34bee8715ab754e2d34..b2062d2c4bb828dcb229f12869f77fd5d9522278 100755 (executable)
Binary files a/libgo/go/debug/dwarf/testdata/typedef.elf and b/libgo/go/debug/dwarf/testdata/typedef.elf differ
index 41019c1e1461536738788a750b49abb0a4fd626b..f75afcccbfc852d284a33c01a49eb1035b6dae9c 100644 (file)
Binary files a/libgo/go/debug/dwarf/testdata/typedef.macho and b/libgo/go/debug/dwarf/testdata/typedef.macho differ
index 9be66658fe9fa0242e69fca787284bbd6c255f14..4502355022d60aafa499b1ac9ee6a1fd7b090cf1 100644 (file)
@@ -426,6 +426,8 @@ func (d *Data) Type(off Offset) (Type, error) {
                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)
@@ -444,11 +446,32 @@ func (d *Data) Type(off Offset) (Type, error) {
                                                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)
                        }
                }
 
@@ -579,3 +602,14 @@ Error:
        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
+       }
+}
index b9470a4fcb4dad4dc27421906aa1ffd10f8ba923..b5b255f6f4a600846b0e44c2ff452982df544fb3 100644 (file)
@@ -25,13 +25,22 @@ var typedefTests = map[string]string{
        "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 {
@@ -58,13 +67,13 @@ func machoData(t *testing.T, name string) *Data {
        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 {
@@ -93,7 +102,7 @@ func testTypedefs(t *testing.T, d *Data) {
                                        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)
                                }
                        }
index b90181bdc64103ba86d4c1961a6acc412067fbb8..b2400bb3ba7b99799998c3e321615b8670da3103 100644 (file)
@@ -6,15 +6,37 @@ package gosym
 
 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 {
@@ -149,7 +171,7 @@ func TestPCLine(t *testing.T) {
                return
        }
 
-       f, tab := crack("_test/pclinetest", t)
+       f, tab := crack(pclinetestBinary, t)
        text := f.Section(".text")
        textdat, err := text.Data()
        if err != nil {
@@ -163,10 +185,13 @@ func TestPCLine(t *testing.T) {
                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)
                }
        }
 
index 750d623cde20c4ed71094a11cd3a53a7787e4fad..a0bb985300f3b32adc784254f12bf7a250eb71ab 100644 (file)
@@ -464,7 +464,7 @@ func allocate(rtyp reflect.Type, p uintptr, indir int) uintptr {
 // 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())
@@ -473,7 +473,7 @@ func (dec *Decoder) decodeSingle(engine *decEngine, ut *userTypeInfo, basep uint
        }
        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 {
@@ -481,10 +481,9 @@ func (dec *Decoder) decodeSingle(engine *decEngine, ut *userTypeInfo, basep uint
        }
        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
@@ -839,11 +838,10 @@ func (dec *Decoder) decOpFor(wireId typeId, rt reflect.Type, name string, inProg
                        }
 
                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)
@@ -1151,7 +1149,7 @@ func (dec *Decoder) compileDec(remoteId typeId, ut *userTypeInfo) (engine *decEn
 
 // 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)
index 9a62cf9c2ade63cf7aa658fe4c20facd5c8d4394..3bfae30f39a88b8fafe2364602c563c784d2d896 100644 (file)
@@ -685,3 +685,54 @@ func TestSliceIncompatibility(t *testing.T) {
                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)
+       }
+}
index 39006efdb2d596a553899fa778df42cb1e91cd84..0dd7a0a770ea137956683f9e69c4ced123d1f2d3 100644 (file)
@@ -152,6 +152,10 @@ var idToType = make(map[typeId]gobType)
 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
@@ -346,6 +350,11 @@ func newSliceType(name string) *sliceType {
 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()
 }
 
@@ -503,6 +512,13 @@ func newTypeObject(name string, ut *userTypeInfo, rt reflect.Type) (gobType, err
                        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
index 87076b53dc06578d4b21b24293a957f1eb8d9566..110c6fd62386e5af5ad5871c9b31067c4175c569 100644 (file)
@@ -496,6 +496,12 @@ func (d *decodeState) object(v reflect.Value) {
                                        // 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 {
@@ -963,3 +969,11 @@ func unquoteBytes(s []byte) (t []byte, ok bool) {
        }
        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.
index 775becfa7c9be2699e127a088f1cc3f5feeaefa6..0eec586a9bb73eee8fbe37ccb5d7529fc3116a75 100644 (file)
@@ -619,3 +619,32 @@ func TestRefUnmarshal(t *testing.T) {
                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")
+       }
+}
index 83e73c09cb41c8d3bb80b5dae92e5c29a6487cb4..8a794b79bd5bb5c659466c70b75cf4886063351c 100644 (file)
@@ -538,6 +538,11 @@ func encodeFields(t reflect.Type) []encodeField {
                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
index a96c523d55316e2f0530b077f49fb2dfbaf49a69..6c3170bdda3d42074b9721b9ac21ebc401e7f82d 100644 (file)
@@ -57,35 +57,14 @@ const (
 //       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) {
@@ -96,6 +75,22 @@ 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
@@ -103,7 +98,7 @@ type Encoder struct {
 
 // 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.
@@ -118,8 +113,14 @@ func (enc *Encoder) Encode(v interface{}) error {
 
 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
@@ -177,6 +178,7 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo) error {
                }
        }
 
+       p.writeIndent(1)
        p.WriteByte('<')
        p.WriteString(name)
 
@@ -216,6 +218,7 @@ func (p *printer) marshalValue(val reflect.Value, finfo *fieldInfo) error {
                return err
        }
 
+       p.writeIndent(-1)
        p.WriteByte('<')
        p.WriteByte('/')
        p.WriteString(name)
@@ -294,6 +297,7 @@ func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error {
                        if vf.Len() == 0 {
                                continue
                        }
+                       p.writeIndent(0)
                        p.WriteString("<!--")
                        dashDash := false
                        dashLast := false
@@ -352,6 +356,33 @@ func (p *printer) marshalStruct(tinfo *typeInfo, val reflect.Value) error {
        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
@@ -367,20 +398,20 @@ func (s *parentStack) trim(parents []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('>')
        }
index b5a3426a328e27fdc4a5dce742017482bcdc05bd..c2168242091a493f3c7af28e182c6da2aa32048c 100644 (file)
@@ -25,58 +25,6 @@ import (
 // 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
@@ -133,6 +81,9 @@ import (
 //      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
index c537eeb625169e3876a9f4ec72cf669bd3395391..63c05d7185b8ef8c6e525b3c81c3c564849353ea 100644 (file)
@@ -5,29 +5,49 @@
 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
+}
index c2160fc65375d42198940fa55d9285848c3b435f..d41d66bfacd8d457ff4c7fa351c48baf55c0b160 100644 (file)
@@ -7,6 +7,7 @@
 package inotify
 
 import (
+       "io/ioutil"
        "os"
        "testing"
        "time"
@@ -16,16 +17,19 @@ func TestInotifyEvents(t *testing.T) {
        // 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
@@ -35,7 +39,7 @@ func TestInotifyEvents(t *testing.T) {
                }
        }()
 
-       const testFile string = "_test/TestInotifyEvents.testfile"
+       testFile := dir + "/TestInotifyEvents.testfile"
 
        // Receive events on the event channel on a separate goroutine
        eventstream := watcher.Event
@@ -58,7 +62,7 @@ func TestInotifyEvents(t *testing.T) {
        // 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
@@ -95,7 +99,7 @@ func TestInotifyClose(t *testing.T) {
                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")
        }
index ccff4670602179e9d1ae6bf792234aaf166686f8..2cbe1ac730e43a24f6de10c137c8353bb6eaf69c 100644 (file)
@@ -66,6 +66,18 @@ func (rb *reorderBuffer) flush(out []byte) []byte {
        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.
@@ -96,32 +108,41 @@ func (rb *reorderBuffer) insertOrdered(info runeInfo) bool {
 // 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
 }
 
@@ -182,8 +203,12 @@ const (
        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
@@ -202,8 +227,10 @@ func isHangul(b []byte) bool {
        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
@@ -234,6 +261,22 @@ func isHangulWithoutJamoT(b []byte) bool {
        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.
index e32380d7afac6f822bcccfd4e12f4a3fc167da03..9de9eacfd653f308cc1032a25ad6daa06c4e96f5 100644 (file)
@@ -47,14 +47,14 @@ func runTests(t *testing.T, name string, fm Form, f insertFunc, tests []TestCase
        }
 }
 
-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!") {
@@ -65,16 +65,32 @@ func TestFlush(t *testing.T) {
        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}},
index 5c0968ba58cd5d7611fd0fa8a6188d36c41d5c82..9c564d67718b5f7a6be2b374cce59cae2b415c75 100644 (file)
@@ -7,7 +7,7 @@ package norm
 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)
@@ -18,8 +18,8 @@ type input interface {
 
 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
 }
@@ -59,8 +59,8 @@ func (s inputString) hangul(p int) rune {
 
 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
 }
diff --git a/libgo/go/exp/norm/iter.go b/libgo/go/exp/norm/iter.go
new file mode 100644 (file)
index 0000000..761ba90
--- /dev/null
@@ -0,0 +1,286 @@
+// 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
+}
diff --git a/libgo/go/exp/norm/iter_test.go b/libgo/go/exp/norm/iter_test.go
new file mode 100644 (file)
index 0000000..f6e8d81
--- /dev/null
@@ -0,0 +1,186 @@
+// 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))
+                       }
+               }
+       }
+}
index 030d900918ed6873d1877c50f55654a5d203788d..b5cd44abfa0dc7af46366d1ff5e18bba7382236d 100644 (file)
@@ -243,7 +243,7 @@ func quickSpan(rb *reorderBuffer, i int) int {
        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
@@ -448,11 +448,16 @@ func decomposeToLastBoundary(rb *reorderBuffer, buf []byte) []byte {
                }
                // 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++
index c7d5e08fca0a6321d7155a8f9fa057c39655d91a..8b970598b4d1faed388067d6bda157eb1ec5afb4 100644 (file)
@@ -5,6 +5,7 @@
 package norm
 
 import (
+       "bytes"
        "strings"
        "testing"
 )
@@ -495,15 +496,40 @@ func TestAppend(t *testing.T) {
        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()
+               }
        }
 }
 
@@ -549,17 +575,21 @@ func BenchmarkNormalizeHangulNFD2NFD(b *testing.B) {
        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()
+               }
        }
 }
 
@@ -584,6 +614,11 @@ func BenchmarkJapanese(b *testing.B) {
 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
index c2ab25bc99dab9a7836b8ea2a040dafc7e637afc..507de1ae8340bed21f5aeb91285a569bb2189276 100644 (file)
@@ -220,6 +220,17 @@ func cmpIsNormal(t *Test, name string, f norm.Form, test string, result, want bo
 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))
index 466e135eb10981ca66f0e0b3b858a2561ac680de..62fa5c9296dc7672710d1a2a1da814ef938776b7 100644 (file)
@@ -98,9 +98,9 @@ func (s *socks5) Dial(network, addr string) (net.Conn, error) {
 
        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 {
@@ -139,7 +139,7 @@ func (s *socks5) Dial(network, addr string) (net.Conn, error) {
        }
 
        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 {
index c3ba5bde2ee6e69b01b7f9e315c1d17bdef7f3ea..c1ed0c0c4437d69f51d28cecc32f58f1fd3d354e 100644 (file)
@@ -389,12 +389,12 @@ func (t *Terminal) Write(buf []byte) (n int, err error) {
 
        // 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()
        }
index 59ac1624a265c81b319f6eccb8a7cd420be11914..4a1929a839715c01bd865124f3fc9b3a6a47ca68 100644 (file)
@@ -7,6 +7,7 @@
 package winfsnotify
 
 import (
+       "io/ioutil"
        "os"
        "testing"
        "time"
@@ -115,7 +116,13 @@ func TestNotifyClose(t *testing.T) {
                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")
        }
index 1c23b0d95c3928edc3d7b5ba8de4a911cdf57d13..a7e0e250a2e9d74a6e82d1f397e6f831794f8d35 100644 (file)
@@ -2,28 +2,31 @@
 // 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
@@ -45,26 +48,54 @@ func Examples(pkg *ast.Package) []*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.)
@@ -78,3 +109,9 @@ func isTest(name, prefix string) bool {
        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 }
index bdfb294adb001bc3f3072b96e4b6d8e318c2d07c..3558892ebd0c2a411cf840ce2a290d37fc76ef2d 100644 (file)
@@ -439,8 +439,10 @@ func (r *reader) readFile(src *ast.File) {
                                                // 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)
                                        }
@@ -460,7 +462,7 @@ func (r *reader) readFile(src *ast.File) {
                                // 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())
                        }
                }
        }
@@ -530,7 +532,7 @@ func customizeRecv(f *Func, recvTypeName string, embeddedIsPtr bool, level int)
        _, 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
 
diff --git a/libgo/go/go/doc/synopsis.go b/libgo/go/go/doc/synopsis.go
new file mode 100644 (file)
index 0000000..2192d78
--- /dev/null
@@ -0,0 +1,52 @@
+// 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)
+}
diff --git a/libgo/go/go/doc/synopsis_test.go b/libgo/go/go/doc/synopsis_test.go
new file mode 100644 (file)
index 0000000..dfc6598
--- /dev/null
@@ -0,0 +1,44 @@
+// 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)
+               }
+       }
+}
index 0bf567b7c4d898fa6e159a15e1c198a6a7a12e64..0aded5bb4c74cf263b8b691e545675ba72df9806 100644 (file)
@@ -16,7 +16,7 @@ var matchBenchmarks = flag.String("test.bench", "", "regular expression to selec
 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)
@@ -213,7 +213,7 @@ func (r BenchmarkResult) String() string {
 }
 
 // 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 {
@@ -281,7 +281,7 @@ func (b *B) trimOutput() {
 }
 
 // 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{
index 1f92f8fe3e1102589fcfe5cae5d54673a592eaf7..d26a4685ca03ea04f8e0d45f9c10de1c364eba52 100644 (file)
@@ -27,7 +27,7 @@ VARIABLES
                // 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")
        
index cfe212dc1d78ff16e7fdc03c3b2afb97111cda21..71c1d1eaf0ea832e262eccd70d62386e3603a1a4 100644 (file)
@@ -3,7 +3,7 @@
 // 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
@@ -12,7 +12,7 @@
 //
 // 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:
@@ -53,7 +53,7 @@ var (
        // 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")
 
@@ -205,7 +205,7 @@ func (t *T) Parallel() {
 }
 
 // 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)
@@ -227,7 +227,7 @@ func tRunner(t *T, test *InternalTest) {
 }
 
 // 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()
index 6a0b61eb48cf3630396888b753d141a75561e4fa..c1e6190448641e9c50822d66b6c6604597b0a478 100644 (file)
@@ -249,7 +249,7 @@ func (p *parser) consumeComment() (comment *ast.Comment, endline int) {
                }
        }
 
-       comment = &ast.Comment{p.pos, p.lit}
+       comment = &ast.Comment{Slash: p.pos, Text: p.lit}
        p.next0()
 
        return
@@ -270,7 +270,7 @@ func (p *parser) consumeCommentGroup() (comments *ast.CommentGroup, endline int)
        }
 
        // add comment group to the comments list
-       comments = &ast.CommentGroup{list}
+       comments = &ast.CommentGroup{List: list}
        p.comments = append(p.comments, comments)
 
        return
@@ -391,7 +391,7 @@ func (p *parser) parseIdent() *ast.Ident {
        } 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) {
@@ -469,7 +469,7 @@ func (p *parser) parseType() ast.Expr {
                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
@@ -489,7 +489,7 @@ func (p *parser) parseTypeName() ast.Expr {
                p.next()
                p.resolve(ident)
                sel := p.parseIdent()
-               return &ast.SelectorExpr{ident, sel}
+               return &ast.SelectorExpr{X: ident, Sel: sel}
        }
 
        return ident
@@ -503,7 +503,7 @@ func (p *parser) parseArrayType(ellipsisOk bool) ast.Expr {
        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()
@@ -511,7 +511,7 @@ func (p *parser) parseArrayType(ellipsisOk bool) ast.Expr {
        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 {
@@ -521,7 +521,7 @@ 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
        }
@@ -541,7 +541,7 @@ func (p *parser) parseFieldDecl(scope *ast.Scope) *ast.Field {
        // 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()
        }
 
@@ -557,13 +557,13 @@ func (p *parser) parseFieldDecl(scope *ast.Scope) *ast.Field {
                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
@@ -586,7 +586,14 @@ func (p *parser) parseStructType() *ast.StructType {
        }
        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 {
@@ -597,7 +604,7 @@ 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 {
@@ -607,9 +614,9 @@ 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)
 }
@@ -620,7 +627,7 @@ func (p *parser) parseVarType(isParam bool) ast.Expr {
                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
 }
@@ -661,7 +668,7 @@ func (p *parser) parseParameterList(scope *ast.Scope, ellipsisOk bool) (params [
        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.
@@ -673,7 +680,7 @@ func (p *parser) parseParameterList(scope *ast.Scope, ellipsisOk bool) (params [
                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.
@@ -708,7 +715,7 @@ func (p *parser) parseParameters(scope *ast.Scope, ellipsisOk bool) *ast.FieldLi
        }
        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 {
@@ -750,7 +757,7 @@ func (p *parser) parseFuncType() (*ast.FuncType, *ast.Scope) {
        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 {
@@ -767,7 +774,7 @@ 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
@@ -775,7 +782,7 @@ func (p *parser) parseMethodSpec(scope *ast.Scope) *ast.Field {
        }
        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
@@ -795,7 +802,14 @@ func (p *parser) parseInterfaceType() *ast.InterfaceType {
        }
        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 {
@@ -809,7 +823,7 @@ 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 {
@@ -832,7 +846,7 @@ 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.
@@ -860,7 +874,7 @@ func (p *parser) tryIdentOrType(ellipsisOk bool) ast.Expr {
                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
@@ -903,7 +917,7 @@ func (p *parser) parseBody(scope *ast.Scope) *ast.BlockStmt {
        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 {
@@ -917,7 +931,7 @@ 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}
 }
 
 // ----------------------------------------------------------------------------
@@ -938,7 +952,7 @@ func (p *parser) parseFuncTypeOrLit() ast.Expr {
        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
@@ -959,7 +973,7 @@ func (p *parser) parseOperand(lhs bool) ast.Expr {
                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
 
@@ -970,7 +984,7 @@ func (p *parser) parseOperand(lhs bool) ast.Expr {
                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()
@@ -987,7 +1001,7 @@ func (p *parser) parseOperand(lhs bool) ast.Expr {
        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 {
@@ -997,7 +1011,7 @@ 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 {
@@ -1015,7 +1029,7 @@ 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 {
@@ -1041,9 +1055,9 @@ 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 {
@@ -1069,7 +1083,7 @@ 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 {
@@ -1086,7 +1100,7 @@ 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
        }
@@ -1123,7 +1137,7 @@ func (p *parser) parseLiteralValue(typ ast.Expr) ast.Expr {
        }
        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).
@@ -1152,7 +1166,7 @@ func (p *parser) checkExpr(x ast.Expr) ast.Expr {
        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
 }
@@ -1215,7 +1229,7 @@ func (p *parser) checkExprOrType(x ast.Expr) ast.Expr {
        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()}
                }
        }
 
@@ -1247,7 +1261,7 @@ L:
                                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 {
@@ -1288,7 +1302,7 @@ func (p *parser) parseUnaryExpr(lhs bool) ast.Expr {
                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
@@ -1297,18 +1311,18 @@ func (p *parser) parseUnaryExpr(lhs bool) ast.Expr {
                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)
@@ -1330,7 +1344,7 @@ func (p *parser) parseBinaryExpr(lhs bool, prec1 int) ast.Expr {
                                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)}
                }
        }
 
@@ -1392,12 +1406,12 @@ func (p *parser) parseSimpleStmt(mode int) (ast.Stmt, bool) {
                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)
                }
@@ -1418,7 +1432,7 @@ func (p *parser) parseSimpleStmt(mode int) (ast.Stmt, bool) {
                        // 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
                }
@@ -1429,24 +1443,24 @@ func (p *parser) parseSimpleStmt(mode int) (ast.Stmt, bool) {
                // 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 {
@@ -1467,10 +1481,10 @@ func (p *parser) parseGoStmt() ast.Stmt {
        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 {
@@ -1482,10 +1496,10 @@ 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 {
@@ -1501,7 +1515,7 @@ 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 {
@@ -1519,7 +1533,7 @@ 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 {
@@ -1530,7 +1544,7 @@ 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 {
@@ -1572,7 +1586,7 @@ 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) {
@@ -1612,7 +1626,7 @@ func (p *parser) parseCaseClause(typeSwitch bool) *ast.CaseClause {
        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 {
@@ -1681,13 +1695,13 @@ func (p *parser) parseSwitchStmt() ast.Stmt {
        }
        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 {
@@ -1710,7 +1724,7 @@ 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 {
@@ -1723,7 +1737,7 @@ func (p *parser) parseCommClause() *ast.CommClause {
                                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)
                                }
@@ -1734,7 +1748,7 @@ func (p *parser) parseCommClause() *ast.CommClause {
                                        p.errorExpected(lhs[0].Pos(), "1 expression")
                                        // continue with first expression
                                }
-                               comm = &ast.ExprStmt{lhs[0]}
+                               comm = &ast.ExprStmt{X: lhs[0]}
                        }
                }
        } else {
@@ -1745,7 +1759,7 @@ func (p *parser) parseCommClause() *ast.CommClause {
        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 {
@@ -1761,9 +1775,9 @@ 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 {
@@ -1812,16 +1826,30 @@ 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) {
@@ -1831,12 +1859,12 @@ 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
@@ -1864,17 +1892,17 @@ func (p *parser) parseStmt() (s ast.Stmt) {
        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
@@ -1893,7 +1921,7 @@ func parseImportSpec(p *parser, doc *ast.CommentGroup, _ int) ast.Spec {
        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()
@@ -1901,7 +1929,7 @@ func parseImportSpec(p *parser, doc *ast.CommentGroup, _ int) ast.Spec {
 
        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
@@ -1909,7 +1937,12 @@ func parseImportSpec(p *parser, doc *ast.CommentGroup, _ int) ast.Spec {
        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
@@ -1933,7 +1966,13 @@ func parseConstSpec(p *parser, doc *ast.CommentGroup, iota int) ast.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
@@ -1950,7 +1989,7 @@ func parseTypeSpec(p *parser, doc *ast.CommentGroup, _ int) ast.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()
@@ -1978,7 +2017,13 @@ func parseVarSpec(p *parser, doc *ast.CommentGroup, _ int) ast.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, nil, p.topScope, ast.Var, idents...)
 
        return spec
@@ -2005,7 +2050,14 @@ func (p *parser) parseGenDecl(keyword token.Token, f parseSpecFunction) *ast.Gen
                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 {
@@ -2018,7 +2070,7 @@ 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
        }
 
@@ -2027,7 +2079,7 @@ func (p *parser) parseReceiver(scope *ast.Scope) *ast.FieldList {
        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
@@ -2057,7 +2109,17 @@ func (p *parser) parseFuncDecl() *ast.FuncDecl {
        }
        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
@@ -2096,7 +2158,7 @@ func (p *parser) parseDecl() ast.Decl {
                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
        }
 
@@ -2155,5 +2217,14 @@ func (p *parser) parseFile() *ast.File {
                }
        }
 
-       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,
+       }
 }
index 25935fb42bb8b369be3791b28c2cb83224f0b5c0..cd5e075c16c9a0d84fc39fa19dd2843b29fbacc7 100644 (file)
@@ -87,7 +87,6 @@ const (
        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.
@@ -133,7 +132,9 @@ func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exp
                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)
                        }
@@ -213,14 +214,18 @@ func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exp
                }
 
                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
@@ -287,11 +292,18 @@ func (p *printer) parameters(fields *ast.FieldList, multiLine *bool) {
                                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
@@ -316,7 +328,7 @@ func (p *printer) parameters(fields *ast.FieldList, multiLine *bool) {
                // 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
@@ -374,7 +386,7 @@ func (p *printer) isOneLineFieldList(list []*ast.Field) bool {
 }
 
 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) {
@@ -397,6 +409,7 @@ 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)
@@ -668,63 +681,6 @@ func isBinary(expr ast.Expr) bool {
        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())
@@ -798,8 +754,14 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int, multiLine *bool) {
                }
 
        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)
@@ -1180,7 +1142,9 @@ func (p *printer) stmt(stmt ast.Stmt, nextIsRBrace bool, multiLine *bool) {
                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)
index c9949205e8a141a05ccd3fb87d5f4895842ea0ad..72f65a1d8526637fd1c56dcfcaf1d9e4a02f005c 100644 (file)
@@ -686,9 +686,11 @@ func (p *printer) intersperseComments(next token.Position, tok token.Token) (wro
        }
 
        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,
index fa133cd35f04f4fbcf02ffffa5f22b9ef7a43b07..2d4f61356c19f6d67f3d0ad6b7a3dcdc40129886 100644 (file)
@@ -283,10 +283,10 @@ func fibo(n int) {
                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
index e5826eecefeefadab2b0b0da4b2841b72f6559c0..4c6f1ab82740d985859f8938989021ef09a403b1 100644 (file)
@@ -405,16 +405,17 @@ func _() {
 }
 
 // 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 */
 }
@@ -425,7 +426,7 @@ func _() {
 }
 
 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:
@@ -448,8 +449,9 @@ func _() {
        _ = 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
@@ -479,6 +481,35 @@ func _() {
        }
 }
 
+// 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.
index 55f6b61f21f388f2f1c2a54c21c347c9904a3917..c0f8cca3a92babd7a89d03e624c384352d8c938f 100644 (file)
@@ -411,6 +411,7 @@ func _() {
 
 
 // Some interesting interspersed comments.
+// See below for more common cases.
 func _(/* this */x/* is *//* an */ int) {
 }
 
@@ -453,8 +454,9 @@ func _() {
        _ = 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
@@ -486,6 +488,31 @@ func _() {
        }
 }
 
+// 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.
 
index d0cf24ad6f6d8bd968f4d51837d41a2a311eaa95..95fdd95ffbba598e94b986fda2684286ba42eeb6 100644 (file)
@@ -545,7 +545,7 @@ func _() {
        // handle multiline argument list correctly
        _ = new(T).
                foo(
-                       1).
+               1).
                foo(2)
 
        _ = new(T).foo(
@@ -587,12 +587,12 @@ func _() {
        _ = new(T).
                Field.
                Array[3+
-                       4].
+               4].
                Table["foo"].
                Blob.(*Type).
                Slices[1:4].
                Method(1, 2,
-                       3).
+               3).
                Thingy
 
        _ = a.b.c
index d7819a3baadcff99f25bfe2f4e468c78a37fbb5f..3442ba9b9501da9e83b2c900ce03def1bfecbe74 100644 (file)
@@ -545,7 +545,7 @@ func _() {
        // handle multiline argument list correctly
        _ = new(T).
                foo(
-                       1).
+               1).
                foo(2)
 
        _ = new(T).foo(
@@ -587,12 +587,12 @@ func _() {
        _ = new(T).
                Field.
                Array[3+
-                       4].
+               4].
                Table["foo"].
                Blob.(*Type).
                Slices[1:4].
                Method(1, 2,
-                       3).
+               3).
                Thingy
 
        _ = a.b.c
index 458e1f9f37a8a7b69ba07499922dd79aedb65ae2..2395363b0ec88afb2b07b827113dd623f0ad02ba 100644 (file)
@@ -2,21 +2,9 @@
 // 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
 
diff --git a/libgo/go/html/template/clone.go b/libgo/go/html/template/clone.go
deleted file mode 100644 (file)
index d0d8ea4..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-// 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),
-       }
-}
index 39788173b999019022e57bf6333de7aaf0dc3c39..c612775d4f01fba832c3f195eebcc045b02809df 100644 (file)
@@ -7,86 +7,109 @@ package template
 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&gt;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, &lt;World&gt;!",
-               },
-               {
-                       `Hello, {{if false}}{{.X}}{{else}}{{"<World>"}}{{end}}!`,
-                       "Hello, <World>!",
-                       "Hello, &lt;World&gt;!",
-               },
-               {
-                       `Hello, {{with "<World>"}}{{.}}{{end}}!`,
-                       "Hello, <World>!",
-                       "Hello, &lt;World&gt;!",
-               },
-               {
-                       `{{range .}}<p>{{.}}</p>{{end}}`,
-                       "<p>foo</p><p><bar></p><p>baz</p>",
-                       "<p>foo</p><p>&lt;bar&gt;</p><p>baz</p>",
-               },
-               {
-                       `Hello, {{"<World>" | html}}!`,
-                       "Hello, &lt;World&gt;!",
-                       "Hello, &lt;World&gt;!",
-               },
-               {
-                       `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: &#34;\u003ci\u003e*/&#34; "></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(), ` ( &lt;i&gt;*/ ) `; 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)
        }
 }
index 4de7ccde9127c1247b3067692a1ea8236387c13c..539664f9729a0dc6ac982d8e84bfa1e75624fac8 100644 (file)
@@ -85,6 +85,22 @@ func indirect(a interface{}) interface{} {
        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) {
@@ -107,7 +123,7 @@ func stringify(args ...interface{}) (string, contentType) {
                }
        }
        for i, arg := range args {
-               args[i] = indirect(arg)
+               args[i] = indirectToStringerOrError(arg)
        }
        return fmt.Sprint(args...), contentTypePlain
 }
index c96a521a59c47e9e5ad9be547a11332f01213e47..3c32e5e89cfac3e524e70d5dbcac75f6bf993030 100644 (file)
@@ -6,6 +6,7 @@ package template
 
 import (
        "bytes"
+       "fmt"
        "strings"
        "testing"
 )
@@ -219,3 +220,42 @@ func TestTypedContent(t *testing.T) {
                }
        }
 }
+
+// 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())
+       }
+}
index 6fe507abea4c8ddca94ed0d3c59746602b39768e..7f60f3b9680fb537f42c3162db4e3dcaf5a0e3b1 100644 (file)
@@ -17,11 +17,11 @@ Introduction
 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
@@ -172,18 +172,18 @@ This package assumes that template authors are trusted, that Execute's data
 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."
index c6f723ae4a47baff517340b075fca9286d090005..02fa3eaad6bdb3ff447c0670d87ce9056e199d91 100644 (file)
@@ -46,30 +46,30 @@ func escapeTemplates(tmpl *Template, names ...string) error {
 
 // 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
@@ -147,17 +147,17 @@ func (e *escaper) escapeAction(c context, n *parse.ActionNode) context {
        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,
@@ -167,27 +167,27 @@ func (e *escaper) escapeAction(c context, n *parse.ActionNode) context {
                        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())
                }
@@ -196,9 +196,9 @@ func (e *escaper) escapeAction(c context, n *parse.ActionNode) context {
        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
@@ -260,22 +260,22 @@ func ensurePipelineContains(p *parse.PipeNode, s []string) {
 // 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,
        },
 }
 
@@ -505,7 +505,7 @@ func (e *escaper) escapeTree(c context, name string, line int) (context, string)
                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
index 9ffe41413a8ecab61a9b2eeba26c48b57fa7b72b..b0bae7a54fb35e4d47e0ced052c7832bbf60f9d2 100644 (file)
@@ -50,7 +50,7 @@ func (t *Template) Execute(wr io.Writer, data interface{}) (err error) {
 // 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
        }
@@ -60,7 +60,7 @@ func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{})
 // 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]
@@ -106,14 +106,71 @@ func (t *Template) Parse(src string) (*Template, error) {
        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.
diff --git a/libgo/go/image/decode_example_test.go b/libgo/go/image/decode_example_test.go
new file mode 100644 (file)
index 0000000..aa5a841
--- /dev/null
@@ -0,0 +1,79 @@
+// 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
+}
index eb8b1950bfef6165367b732ad637af5e97cb48c2..b2373f79ba3aeaa52cf339ea15f58ff291d9b5af 100644 (file)
@@ -50,6 +50,9 @@ func TestYCbCr(t *testing.T) {
                                testYCbCr(t, r, subsampleRatio, delta)
                        }
                }
+               if testing.Short() {
+                       break
+               }
        }
 }
 
index 645eed6abb855489554cd833dd21b45b849b22d4..42d2e6758698186a0724f262199b4de5d3d2925f 100644 (file)
@@ -49,7 +49,7 @@ func TempFile(dir, prefix string) (f *os.File, err error) {
        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()
                        }
@@ -76,7 +76,7 @@ func TempDir(dir, prefix string) (name string, err error) {
        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()
                        }
index 3eb5353e9a9b8ae73854d883ac0f73787942267e..f53310cb0a1fd63c03958026ff10a50e5e1bc668 100644 (file)
@@ -2,6 +2,8 @@
 // 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.
index 7f509b3666e81574aea51f5ba70b032e486326c1..0fd6239059a3d0645eaf0376007ddc09612a8125 100644 (file)
@@ -1,6 +1,9 @@
 // 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 (
index b1c929ad2fe0cfa79ff59d2da1a52bdfd49796f9..46a164dd5773b07b4cb53c7a9d325a8f78966e63 100644 (file)
@@ -2,6 +2,8 @@
 // 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 (
index 25e39273c0c8b48313cd5b394b9984645cc49183..7f3f76dc36f6ce97bbe98fde928a9edf17d59188 100644 (file)
@@ -512,6 +512,9 @@ func TestStringPowers(t *testing.T) {
                                t.Errorf("failed at %d ** %d in base %d: %s != %s", b, p, b, xs, xs2)
                        }
                }
+               if b >= 3 && testing.Short() {
+                       break
+               }
        }
 }
 
index 8955219203075efc02465776f8548d2a3bd82f60..94f84a85fbe14a0f2e1f8d62a6ca1d3fc546dc93 100644 (file)
@@ -49,9 +49,10 @@ func (r *Rand) Int() int {
 }
 
 // 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()
@@ -62,9 +63,10 @@ func (r *Rand) Int63n(n int64) int64 {
 }
 
 // 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()
@@ -75,7 +77,11 @@ func (r *Rand) Int31n(n int32) int32 {
 }
 
 // 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)))
        }
@@ -125,12 +131,15 @@ func Int31() int32 { return globalRand.Int31() }
 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).
index 0ba8f98c4965ba493511c213783eff819d4687fe..bbd44e3f8b10657bdb8e6e4a54f009e14ada5901 100644 (file)
@@ -141,6 +141,9 @@ func TestNonStandardNormalValues(t *testing.T) {
                for m := 0.5; m < mmax; m *= 2 {
                        for _, seed := range testSeeds {
                                testNormalDistribution(t, numTestSamples, m, sd, seed)
+                               if testing.Short() {
+                                       break
+                               }
                        }
                }
        }
@@ -191,6 +194,9 @@ func TestNonStandardExponentialValues(t *testing.T) {
        for rate := 0.05; rate < 10; rate *= 2 {
                for _, seed := range testSeeds {
                        testExponentialDistribution(t, numTestSamples, rate, seed)
+                       if testing.Short() {
+                               break
+                       }
                }
        }
 }
index 81750a3d73967b05f29889b9a507d16bc7dfc5a4..14356da4ce368fe1d0e5f654e05e5a0c43b0cbf0 100644 (file)
@@ -14,7 +14,7 @@ import (
 )
 
 // 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.
@@ -130,7 +130,7 @@ func TestDialGoogleIPv6(t *testing.T) {
                return
        }
        // Only run tcp6 if the kernel will take it.
-       if !*ipv6 || !supportsIPv6 {
+       if !*testIPv6 || !supportsIPv6 {
                return
        }
 
index bf0a387775de494f0852f3ba9af2f6c85870101e..ae1bf2614a28b6f358f4a49049e265b952bded09 100644 (file)
@@ -252,7 +252,9 @@ func (s *pollServer) Run() {
                } 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)
@@ -506,7 +508,7 @@ func (fd *netFD) Write(p []byte) (int, error) {
        }
        defer fd.decref()
        if fd.sysfile == nil {
-               return 0, os.EINVAL
+               return 0, syscall.EINVAL
        }
 
        var err error
index efd846e5d8c8aefb16ab1316ba8df280e7886eb0..45f5c2d882f136dd3a48ba50836c6b7c4aeb008c 100644 (file)
@@ -335,7 +335,7 @@ func (fd *netFD) Close() 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 {
@@ -369,7 +369,7 @@ func (o *readOp) Name() string {
 
 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()
@@ -378,7 +378,7 @@ func (fd *netFD) Read(buf []byte) (int, error) {
        }
        defer fd.decref()
        if fd.sysfd == syscall.InvalidHandle {
-               return 0, os.EINVAL
+               return 0, syscall.EINVAL
        }
        var o readOp
        o.Init(fd, buf, 'r')
@@ -408,7 +408,7 @@ func (o *readFromOp) Name() string {
 
 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
@@ -447,7 +447,7 @@ func (o *writeOp) Name() string {
 
 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()
@@ -478,7 +478,7 @@ func (o *writeToOp) Name() string {
 
 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
@@ -490,7 +490,7 @@ func (fd *netFD) WriteTo(buf []byte, sa syscall.Sockaddr) (int, error) {
        }
        defer fd.decref()
        if fd.sysfd == syscall.InvalidHandle {
-               return 0, os.EINVAL
+               return 0, syscall.EINVAL
        }
        var o writeToOp
        o.Init(fd, buf, 'w')
@@ -578,10 +578,12 @@ func (fd *netFD) dup() (*os.File, error) {
        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
 }
index f9546dc930db4a07fbafb3a238f3632663c497d8..c95d16d64e796325db1cad4fa62daf623f95e6b5 100644 (file)
@@ -28,7 +28,7 @@ func newFileFD(f *os.File) (*netFD, error) {
        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 {
@@ -84,7 +84,7 @@ func FileConn(f *os.File) (c Conn, err error) {
                return newIPConn(fd), nil
        }
        fd.Close()
-       return nil, os.EINVAL
+       return nil, syscall.EINVAL
 }
 
 // FileListener returns a copy of the network listener corresponding
@@ -103,7 +103,7 @@ func FileListener(f *os.File) (l Listener, err error) {
                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
@@ -122,5 +122,5 @@ func FilePacketConn(f *os.File) (c PacketConn, err error) {
                return newUnixConn(fd), nil
        }
        fd.Close()
-       return nil, os.EINVAL
+       return nil, syscall.EINVAL
 }
index 06d7cc89846fbca5c81e99cf5e9ce6600000abaa..04f7ee0401b7f86a7fe0e6b71b00705aa00dffea 100644 (file)
@@ -6,6 +6,7 @@ package net
 
 import (
        "os"
+       "syscall"
 )
 
 // FileConn returns a copy of the network connection corresponding to
@@ -13,7 +14,7 @@ import (
 // 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
@@ -21,7 +22,7 @@ func FileConn(f *os.File) (c Conn, err error) {
 // 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
@@ -29,5 +30,5 @@ func FileListener(f *os.File) (l Listener, err error) {
 // 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
 }
index 1bd00541c6df7038bf9a323d282524471651bbca..064e7e432827f0c5c0c80ca9ed54cf736acc8c02 100644 (file)
@@ -34,7 +34,7 @@ var hosttests = []hostTest{
 
 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)
index 712350dfcef7b00885f6672e0cfbca9922fd6e15..1e9186a0581443dbca3080209a16ebfce177c4a3 100644 (file)
@@ -128,6 +128,34 @@ var readSetCookiesTests = []struct {
                        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 {
diff --git a/libgo/go/net/http/example_test.go b/libgo/go/net/http/example_test.go
new file mode 100644 (file)
index 0000000..2584afc
--- /dev/null
@@ -0,0 +1,51 @@
+// 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)
+}
index 143617e95fc62c22a33e5a36b8b2075cc74cd077..0409008b6755bb0aaa97121a4f4424075d346488 100644 (file)
@@ -6,6 +6,7 @@ package http_test
 
 import (
        "bytes"
+       "errors"
        "fmt"
        "io"
        "io/ioutil"
@@ -131,7 +132,7 @@ func TestFileServerCleans(t *testing.T) {
        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
@@ -398,11 +399,15 @@ func TestLinuxSendfile(t *testing.T) {
                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()))
index c065ccfb499a4d4d8f78836cf18431de437080be..32f4662cc0e7f18f2b1a0301a8e4956a05d78bfa 100644 (file)
@@ -13,12 +13,12 @@ import (
        "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"}
 )
 
@@ -191,7 +191,7 @@ func (sc *ServerConn) Write(req *http.Request, resp *http.Response) 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 {
index 0fe41b7d31b0dbea4ff505daaa6bbddf52f6c352..06fcde1447fb6148bfa6a4b8c8ab323d89bb668f 100644 (file)
@@ -22,9 +22,9 @@
 //
 //     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
 //
@@ -36,7 +36,9 @@ import (
        "bufio"
        "bytes"
        "fmt"
+       "html/template"
        "io"
+       "log"
        "net/http"
        "os"
        "runtime"
@@ -47,11 +49,10 @@ import (
 )
 
 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
@@ -62,20 +63,6 @@ func Cmdline(w http.ResponseWriter, r *http.Request) {
        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) {
@@ -147,3 +134,61 @@ func Symbol(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>
+`))
index 0bbec53be71d50d3bc233d3e1785cd47a3068566..5277657805d1df6615f76a2511b31ac0166a28e6 100644 (file)
@@ -186,7 +186,7 @@ func (r *Request) Cookies() []*Cookie {
        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.
@@ -486,7 +486,7 @@ func ReadRequest(b *bufio.Reader) (req *Request, err error) {
                rawurl = "http://" + rawurl
        }
 
-       if req.URL, err = url.ParseRequest(rawurl); err != nil {
+       if req.URL, err = url.ParseRequestURI(rawurl); err != nil {
                return nil, err
        }
 
index e2860c3edcf55fbd9eaa0cf80637716fa8517fd3..b6a6b4c77d159eac5361e5eff6ad80c8950f31e0 100644 (file)
@@ -245,8 +245,7 @@ func TestServerTimeouts(t *testing.T) {
                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)
@@ -277,7 +276,7 @@ func TestServerTimeouts(t *testing.T) {
        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)
        }
 
index e715c73cb6e9586d1757f4f84e8f97546185130a..fa0df54a236c2dfef8b9ddc99638e1e1b8fcac30 100644 (file)
@@ -12,7 +12,6 @@ package http
 import (
        "bufio"
        "bytes"
-       "crypto/rand"
        "crypto/tls"
        "errors"
        "fmt"
@@ -985,6 +984,7 @@ type Server struct {
        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
@@ -1121,9 +1121,12 @@ func (srv *Server) ListenAndServeTLS(certFile, keyFile string) error {
        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
index a36571a44468ce7c08ce9d58201e95bc939d6f14..1a629c1727c5282bb888c2eee998caf2ac20d1e2 100644 (file)
@@ -648,7 +648,7 @@ func TestTransportPersistConnLeak(t *testing.T) {
        tr := &Transport{}
        c := &Client{Transport: tr}
 
-       n0 := runtime.Goroutines()
+       n0 := runtime.NumGoroutine()
 
        const numReq = 25
        didReqCh := make(chan bool)
@@ -669,7 +669,7 @@ func TestTransportPersistConnLeak(t *testing.T) {
                <-gotReqCh
        }
 
-       nhigh := runtime.Goroutines()
+       nhigh := runtime.NumGoroutine()
 
        // Tell all handlers to unblock and reply.
        for i := 0; i < numReq; i++ {
@@ -685,7 +685,7 @@ func TestTransportPersistConnLeak(t *testing.T) {
        time.Sleep(100 * time.Millisecond)
        runtime.GC()
        runtime.GC() // even more.
-       nfinal := runtime.Goroutines()
+       nfinal := runtime.NumGoroutine()
 
        growth := nfinal - n0
 
index 21038c629b13e42e1e1e52bbdf87e78eee1f89ec..15c2f3781b1d8ef1a9dbadd4c3ef4873b6fc6fca 100644 (file)
@@ -166,13 +166,13 @@ func interfaceMulticastAddrTable(ifindex int) ([]Addr, error) {
                        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
        }
@@ -185,23 +185,26 @@ func parseProcNetIGMP(ifi *Interface) []Addr {
        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
        }
@@ -210,7 +213,10 @@ func parseProcNetIGMP6(ifi *Interface) []Addr {
        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]}}
index 4ce01dc906189598ac0be0052760d3f707c568bb..769414e0eef8c78ff0be6ee9ced7095d138240a8 100644 (file)
@@ -31,17 +31,17 @@ func TestInterfaces(t *testing.T) {
        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())
index f9401c1104e37c2b658e79367ddb1b71ff08e3e9..6136202727cfc70a4e5e4d1eea79564f1b670c1c 100644 (file)
@@ -7,6 +7,7 @@ package net
 import (
        "bytes"
        "os"
+       "syscall"
        "testing"
        "time"
 )
@@ -15,7 +16,7 @@ var icmpTests = []struct {
        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},
@@ -34,15 +35,15 @@ func TestICMP(t *testing.T) {
                }
                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))
@@ -50,12 +51,12 @@ func exchangeICMPEcho(t *testing.T, net, laddr, raddr string, ipv6 bool, echo []
 
        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)
@@ -71,11 +72,15 @@ func exchangeICMPEcho(t *testing.T, net, laddr, raddr string, ipv6 bool, echo []
                        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)
@@ -87,11 +92,11 @@ func exchangeICMPEcho(t *testing.T, net, laddr, raddr string, ipv6 bool, echo []
        }
 }
 
-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))
@@ -106,18 +111,23 @@ func icmpEchoTransponder(t *testing.T, net, raddr string, ipv6 bool, waitForRead
                        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
        }
 
@@ -135,11 +145,15 @@ const (
        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 {
index 382a4402770c29ed561400885087114eb9e4429b..43719fc99cd0ccb252c8cb5d5a3314ce212ef9c9 100644 (file)
@@ -7,7 +7,7 @@
 package net
 
 import (
-       "os"
+       "syscall"
        "time"
 )
 
@@ -17,34 +17,34 @@ type IPConn bool
 
 // 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.
@@ -67,12 +67,12 @@ func (c *IPConn) RemoteAddr() Addr {
 // 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.
@@ -82,18 +82,18 @@ func (c *IPConn) ReadFrom(b []byte) (int, Addr, error) {
 // 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
@@ -101,5 +101,5 @@ func DialIP(netProto string, laddr, raddr *IPAddr) (*IPConn, error) {
 // 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
 }
index c34ffeb121df042295cddcf586234ed03bf7276f..9caa86985a55a7205e5fc9b893d4ab386f433b11 100644 (file)
@@ -66,7 +66,7 @@ func (c *IPConn) Read(b []byte) (int, error) {
 // 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)
 }
@@ -74,7 +74,7 @@ func (c *IPConn) Write(b []byte) (int, error) {
 // 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
@@ -100,7 +100,7 @@ func (c *IPConn) RemoteAddr() Addr {
 // 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)
 }
@@ -108,7 +108,7 @@ func (c *IPConn) SetDeadline(t time.Time) error {
 // 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)
 }
@@ -116,7 +116,7 @@ func (c *IPConn) SetReadDeadline(t time.Time) error {
 // 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)
 }
@@ -125,7 +125,7 @@ func (c *IPConn) SetWriteDeadline(t time.Time) error {
 // 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)
 }
@@ -134,7 +134,7 @@ func (c *IPConn) SetReadBuffer(bytes int) error {
 // 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)
 }
@@ -150,7 +150,7 @@ func (c *IPConn) SetWriteBuffer(bytes int) error {
 // 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
@@ -173,7 +173,7 @@ func (c *IPConn) ReadFromIP(b []byte) (int, *IPAddr, error) {
 // 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
@@ -187,7 +187,7 @@ func (c *IPConn) ReadFrom(b []byte) (int, Addr, error) {
 // 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 {
@@ -199,11 +199,11 @@ func (c *IPConn) WriteToIP(b []byte, addr *IPAddr) (int, error) {
 // 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)
 }
index 597b1277544791d1e9ab80a3ff6dac4366029f23..eab0bf3e899b5e291a9931884278ff80af25d787 100644 (file)
@@ -10,6 +10,7 @@ import (
        "errors"
        "io"
        "os"
+       "syscall"
        "time"
 )
 
@@ -83,7 +84,7 @@ func (c *plan9Conn) ok() bool { return c != nil && c.ctl != nil }
 // 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)
@@ -102,7 +103,7 @@ func (c *plan9Conn) Read(b []byte) (n int, err error) {
 // 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)
@@ -116,7 +117,7 @@ func (c *plan9Conn) Write(b []byte) (n int, err error) {
 // 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 {
@@ -148,17 +149,17 @@ func (c *plan9Conn) RemoteAddr() Addr {
 
 // 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) {
@@ -280,7 +281,7 @@ func (l *plan9Listener) Accept() (c Conn, err error) {
 
 func (l *plan9Listener) Close() error {
        if l == nil || l.ctl == nil {
-               return os.EINVAL
+               return syscall.EINVAL
        }
        return l.ctl.Close()
 }
index 4da18a5061ac3d3d156bd408a3cfef9fa12fd65a..4841057d6beca367b1cb23ead15db27671431456 100644 (file)
@@ -105,21 +105,20 @@ type sockaddr interface {
 }
 
 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
@@ -129,7 +128,7 @@ Error:
        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) {
index c0bb9225a7dc862502be3ff00674e431807c86bb..b08a9fb98e042a50a0af31a1f714eec496bad0fa 100644 (file)
@@ -7,6 +7,7 @@ package net
 import (
        "errors"
        "os"
+       "syscall"
 )
 
 func query(filename, query string, bufSize int) (res []string, err error) {
@@ -71,7 +72,7 @@ func queryDNS(addr string, typ string) (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) {
index 9a39ca8a1eb46e4757c4042516c3ad643e1fcf10..7b9ea844cd4f9a39f839d12ae5c9faa6d0d5f2c3 100644 (file)
@@ -8,14 +8,14 @@
 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
        }
@@ -38,7 +38,7 @@ func TestGoogleSRV(t *testing.T) {
 }
 
 func TestGmailMX(t *testing.T) {
-       if testing.Short() || avoidMacFirewall {
+       if testing.Short() || !*testExternal {
                t.Logf("skipping test to avoid external network")
                return
        }
@@ -52,7 +52,7 @@ func TestGmailMX(t *testing.T) {
 }
 
 func TestGmailTXT(t *testing.T) {
-       if testing.Short() || avoidMacFirewall {
+       if testing.Short() || !*testExternal {
                t.Logf("skipping test to avoid external network")
                return
        }
@@ -66,7 +66,7 @@ func TestGmailTXT(t *testing.T) {
 }
 
 func TestGoogleDNSAddr(t *testing.T) {
-       if testing.Short() || avoidMacFirewall {
+       if testing.Short() || !*testExternal {
                t.Logf("skipping test to avoid external network")
                return
        }
index f62580de66e9e8e7259209e5d1df40caecdfcee9..1d760c21051ff5e0999ed2e7f0b49440bd95e8fb 100644 (file)
@@ -5,30 +5,46 @@
 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
@@ -38,112 +54,142 @@ func TestListenMulticastUDP(t *testing.T) {
                }
        }
 
-       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)
@@ -151,31 +197,28 @@ func testIPv4MulticastSocketOptions(t *testing.T, fd *netFD, ifi *Interface) {
 }
 
 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)
index 79d36a2a8136d00a2d5f6290e95e18cc8e9e5e28..bf242ff8dd65419a8713201890484ee0ec3aa2c0 100644 (file)
@@ -2,8 +2,41 @@
 // 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):
@@ -42,21 +75,28 @@ type Conn interface {
        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
 }
 
index 4c4200a49b7277f9b90aa73b851c6b422e8e58a3..7c87b42f6d9edbc3ddef05a105d282e7f724a9e1 100644 (file)
@@ -67,7 +67,7 @@ func open(name string) (*file, 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 {
index 8cfa033ccc3be0adf8a62e712a86115a9f41371e..62c7b1e60048d7dc63ac2f8eedf71268c5b33a1d 100644 (file)
@@ -387,12 +387,12 @@ func (WriteFailCodec) WriteRequest(*Request, interface{}) error {
 }
 
 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")
 }
 
index 55691493aa9dc2596530f088cb7e28503116ec4e..b9862168153f96b6905594b6eb046ecb4fcf7d30 100644 (file)
@@ -95,7 +95,7 @@ func doTest(t *testing.T, network, listenaddr, dialaddr string) {
        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
                }
index f600cc8648212182d99520f6464a2c2e7e248b51..59f6449f0ab07d257e77464cb771d1d3f2f73fef 100644 (file)
@@ -50,15 +50,14 @@ func Dial(addr string) (*Client, error) {
 // 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
index ce88782053172cae38fdb5a7b9843f4a74e08a85..c315d185c9df0ed188503e99faf71059b9057f86 100644 (file)
@@ -8,9 +8,11 @@ import (
        "bufio"
        "bytes"
        "io"
+       "net"
        "net/textproto"
        "strings"
        "testing"
+       "time"
 )
 
 type authTest struct {
@@ -59,9 +61,12 @@ type faker 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")
@@ -180,3 +185,87 @@ Goodbye.
 .
 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
+`
index 70064c307ef36b5319d3a341c5829ecf318325b7..dc139f04a259e34fc99a4e8246a2df0c2443b309 100644 (file)
@@ -33,13 +33,14 @@ func socket(net string, f, t, p int, la, ra syscall.Sockaddr, toAddr func(syscal
                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
@@ -61,7 +62,12 @@ func socket(net string, f, t, p int, la, ra syscall.Sockaddr, toAddr func(syscal
        }
 
        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)
 
index 128766144ddc3ba42151f6af29696971e9928bf4..35f56966eae66336bcd84d01735cf0906d60626b 100644 (file)
@@ -7,7 +7,7 @@
 package net
 
 import (
-       "os"
+       "syscall"
        "time"
 )
 
@@ -19,35 +19,35 @@ type TCPConn struct {
 
 // 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,
index 200ce91566c7b456e780167a89dee583f0c49fdd..e05bc10170e711d39b416733198c3c4222314355 100644 (file)
@@ -9,6 +9,7 @@
 package net
 
 import (
+       "fmt"
        "io"
        "os"
        "syscall"
@@ -26,6 +27,11 @@ func sockaddrToTCP(sa syscall.Sockaddr) Addr {
                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
 }
@@ -70,7 +76,7 @@ func (c *TCPConn) ok() bool { return c != nil && c.fd != 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)
 }
@@ -86,7 +92,7 @@ func (c *TCPConn) ReadFrom(r io.Reader) (int64, error) {
 // 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)
 }
@@ -94,7 +100,7 @@ func (c *TCPConn) Write(b []byte) (n int, err error) {
 // 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
@@ -105,7 +111,7 @@ func (c *TCPConn) Close() error {
 // Most callers should just use Close.
 func (c *TCPConn) CloseRead() error {
        if !c.ok() {
-               return os.EINVAL
+               return syscall.EINVAL
        }
        return c.fd.CloseRead()
 }
@@ -114,7 +120,7 @@ func (c *TCPConn) CloseRead() error {
 // Most callers should just use Close.
 func (c *TCPConn) CloseWrite() error {
        if !c.ok() {
-               return os.EINVAL
+               return syscall.EINVAL
        }
        return c.fd.CloseWrite()
 }
@@ -138,7 +144,7 @@ func (c *TCPConn) RemoteAddr() Addr {
 // 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)
 }
@@ -146,7 +152,7 @@ func (c *TCPConn) SetDeadline(t time.Time) error {
 // 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)
 }
@@ -154,7 +160,7 @@ func (c *TCPConn) SetReadDeadline(t time.Time) error {
 // 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)
 }
@@ -163,7 +169,7 @@ func (c *TCPConn) SetWriteDeadline(t time.Time) error {
 // 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)
 }
@@ -172,7 +178,7 @@ func (c *TCPConn) SetReadBuffer(bytes int) error {
 // 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)
 }
@@ -190,7 +196,7 @@ func (c *TCPConn) SetWriteBuffer(bytes int) error {
 // 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)
 }
@@ -199,7 +205,7 @@ func (c *TCPConn) SetLinger(sec int) error {
 // 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)
 }
@@ -210,7 +216,7 @@ func (c *TCPConn) SetKeepAlive(keepalive bool) error {
 // 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)
 }
@@ -259,6 +265,17 @@ func DialTCP(net string, laddr, raddr *TCPAddr) (*TCPConn, error) {
 }
 
 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)
@@ -294,7 +311,7 @@ func ListenTCP(net string, laddr *TCPAddr) (*TCPListener, error) {
 // 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 {
@@ -317,7 +334,7 @@ func (l *TCPListener) Accept() (c Conn, err error) {
 // 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()
 }
@@ -329,7 +346,7 @@ func (l *TCPListener) Addr() Addr { return l.fd.laddr }
 // 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)
 }
diff --git a/libgo/go/net/testdata/hosts b/libgo/go/net/testdata/hosts
new file mode 100644 (file)
index 0000000..b601763
--- /dev/null
@@ -0,0 +1,12 @@
+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
diff --git a/libgo/go/net/testdata/igmp b/libgo/go/net/testdata/igmp
new file mode 100644 (file)
index 0000000..5f380a2
--- /dev/null
@@ -0,0 +1,24 @@
+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
diff --git a/libgo/go/net/testdata/igmp6 b/libgo/go/net/testdata/igmp6
new file mode 100644 (file)
index 0000000..6cd5a2d
--- /dev/null
@@ -0,0 +1,18 @@
+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
index 862cd536c467e237ec6f56d56195ea5fa255444c..125feb3e885c39a986dda30e6a0450a5e845ba3c 100644 (file)
@@ -454,10 +454,14 @@ func (r *Reader) ReadMIMEHeader() (MIMEHeader, error) {
 
                // 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
@@ -503,6 +507,11 @@ MustRewrite:
        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'
                }
index 4d036914801f9b8fb30fe51fa16be01fead1dbaa..7c5d16227ff95559362a91660852c2ae0e3f1d2f 100644 (file)
@@ -164,6 +164,29 @@ func TestLargeReadMIMEHeader(t *testing.T) {
        }
 }
 
+// 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
index 317ec72b0cc1d31d92073c25197270e084706108..ad5840cf7dafe882bdbeb365a6332d3f507e6ffd 100644 (file)
@@ -20,6 +20,9 @@
 //
 // 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 (
index f90a5fe9ab853c24b4b94df09babb322a9b5d7db..4f298a42f878737bbfc0b6d4c694aeb08cf144fd 100644 (file)
@@ -9,6 +9,7 @@ package net
 import (
        "errors"
        "os"
+       "syscall"
        "time"
 )
 
@@ -20,17 +21,17 @@ type UDPConn struct {
 
 // 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.
@@ -43,7 +44,7 @@ func (c *UDPConn) SetWriteDeadline(t time.Time) error {
 // 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)
@@ -69,7 +70,7 @@ func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err error) {
 // 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)
 }
@@ -82,7 +83,7 @@ func (c *UDPConn) ReadFrom(b []byte) (n int, addr Addr, err error) {
 // 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)
@@ -106,11 +107,11 @@ func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (n int, err error) {
 // 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)
 }
@@ -191,5 +192,5 @@ func ListenUDP(net string, laddr *UDPAddr) (c *UDPConn, err error) {
 // 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
 }
index 6108373568a9d2f0abd245223f2b97303a20f2bb..1f99dc53867826cfc29b61fd12569a7b5b507a3e 100644 (file)
@@ -63,7 +63,7 @@ func (c *UDPConn) ok() bool { return c != nil && c.fd != nil }
 // 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)
 }
@@ -71,7 +71,7 @@ func (c *UDPConn) Read(b []byte) (int, error) {
 // 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)
 }
@@ -79,7 +79,7 @@ func (c *UDPConn) Write(b []byte) (int, error) {
 // 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
@@ -105,7 +105,7 @@ func (c *UDPConn) RemoteAddr() Addr {
 // 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)
 }
@@ -113,7 +113,7 @@ func (c *UDPConn) SetDeadline(t time.Time) error {
 // 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)
 }
@@ -121,7 +121,7 @@ func (c *UDPConn) SetReadDeadline(t time.Time) error {
 // 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)
 }
@@ -130,7 +130,7 @@ func (c *UDPConn) SetWriteDeadline(t time.Time) error {
 // 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)
 }
@@ -139,7 +139,7 @@ func (c *UDPConn) SetReadBuffer(bytes int) error {
 // 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)
 }
@@ -154,7 +154,7 @@ func (c *UDPConn) SetWriteBuffer(bytes int) error {
 // 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) {
@@ -169,7 +169,7 @@ func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err error) {
 // 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
@@ -183,7 +183,7 @@ func (c *UDPConn) ReadFrom(b []byte) (int, Addr, error) {
 // 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}
@@ -198,11 +198,11 @@ func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (int, error) {
 // 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)
 }
@@ -262,7 +262,7 @@ func ListenMulticastUDP(net string, ifi *Interface, gaddr *UDPAddr) (*UDPConn, e
                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 {
index 1d9d7578f4fff0ff3692dd5164831775258f4562..7b4ae6bd11619e673c49337996a8c643d4946531 100644 (file)
@@ -7,7 +7,7 @@
 package net
 
 import (
-       "os"
+       "syscall"
        "time"
 )
 
@@ -19,17 +19,17 @@ type UnixConn bool
 
 // 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.
@@ -47,28 +47,28 @@ func (c *UnixConn) RemoteAddr() Addr {
 
 // 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
 }
 
@@ -76,7 +76,7 @@ func (c *UnixConn) WriteTo(b []byte, addr Addr) (n int, err error) {
 // 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.
@@ -87,19 +87,19 @@ type UnixListener bool
 // 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.
index 10b7966851155045b8ab988af7fcfd906f885f25..3a94cf5c5adb57cc33cc50e4dd4d0a99202d2d1a 100644 (file)
@@ -59,8 +59,8 @@ func unixSocket(net string, laddr, raddr *UnixAddr, mode string) (fd *netFD, err
                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
@@ -70,7 +70,7 @@ Error:
        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 {
@@ -123,7 +123,7 @@ func (c *UnixConn) ok() bool { return c != nil && c.fd != nil }
 // 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)
 }
@@ -131,7 +131,7 @@ func (c *UnixConn) Read(b []byte) (n int, err error) {
 // 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)
 }
@@ -139,7 +139,7 @@ func (c *UnixConn) Write(b []byte) (n int, err error) {
 // 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
@@ -168,7 +168,7 @@ func (c *UnixConn) RemoteAddr() Addr {
 // 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)
 }
@@ -176,7 +176,7 @@ func (c *UnixConn) SetDeadline(t time.Time) error {
 // 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)
 }
@@ -184,7 +184,7 @@ func (c *UnixConn) SetReadDeadline(t time.Time) error {
 // 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)
 }
@@ -193,7 +193,7 @@ func (c *UnixConn) SetWriteDeadline(t time.Time) error {
 // 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)
 }
@@ -202,7 +202,7 @@ func (c *UnixConn) SetReadBuffer(bytes int) error {
 // 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)
 }
@@ -216,7 +216,7 @@ func (c *UnixConn) SetWriteBuffer(bytes int) error {
 // 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) {
@@ -229,7 +229,7 @@ func (c *UnixConn) ReadFromUnix(b []byte) (n int, addr *UnixAddr, err error) {
 // 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
@@ -243,10 +243,10 @@ func (c *UnixConn) ReadFrom(b []byte) (n int, addr Addr, err error) {
 // 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)
@@ -255,18 +255,18 @@ func (c *UnixConn) WriteToUnix(b []byte, addr *UnixAddr) (n int, err error) {
 // 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) {
@@ -278,11 +278,11 @@ func (c *UnixConn) ReadMsgUnix(b, oob []byte) (n, oobn, flags int, addr *UnixAdd
 
 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)
@@ -339,7 +339,7 @@ func ListenUnix(net string, laddr *UnixAddr) (*UnixListener, error) {
 // 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 {
@@ -363,7 +363,7 @@ func (l *UnixListener) Accept() (c Conn, err error) {
 // 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
@@ -391,7 +391,7 @@ func (l *UnixListener) Addr() Addr { return l.fd.laddr }
 // 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)
 }
index a9ce3b31e24ecfb3c4c05e3653163d83cde0ba64..88ff7ebfef3d6ffc500568bc2f391000e5cd2163 100644 (file)
@@ -2,7 +2,7 @@
 // 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
 
@@ -321,19 +321,28 @@ func split(s string, c byte, cutc bool) (string, string) {
 }
 
 // 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)
 }
 
@@ -415,22 +424,6 @@ func parseAuthority(authority string) (user *Userinfo, host string, err error) {
        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
@@ -589,15 +582,15 @@ func (u *URL) IsAbs() bool {
        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
@@ -606,13 +599,13 @@ func (base *URL) Parse(ref string) (*URL, error) {
 // 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 != "" {
@@ -632,7 +625,7 @@ func (base *URL) ResolveReference(ref *URL) *URL {
                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
                }
index 9fe5ff886b7d8be48ef6e366e8a4347be9daf9cb..2d911ed505a18911cdaa9b43ece9e1f8aa5abc8f 100644 (file)
@@ -188,22 +188,6 @@ var urltests = []URLTest{
                },
                "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{
@@ -257,12 +241,6 @@ func DoTest(t *testing.T, parse func(string) (*URL, error), name string, tests [
 
 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"
@@ -281,16 +259,16 @@ var parseRequestUrlTests = []struct {
        {"../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)
        }
@@ -319,9 +297,6 @@ func DoTestString(t *testing.T, parse func(string) (*URL, error), name string, t
 
 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 {
@@ -538,7 +513,7 @@ var resolveReferenceTests = []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)
                }
@@ -589,7 +564,7 @@ func TestResolveReference(t *testing.T) {
 
 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)
                }
index 53f0f78776566958c71ab3994cf5124603cf8e6b..9a7c076d59c92303caa0001ba56288ef263ca7d8 100644 (file)
@@ -291,6 +291,10 @@ func exportLoopback(exp *Exporter, t *testing.T) {
 // 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)
@@ -378,6 +382,10 @@ const flowCount = 100
 
 // 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)
@@ -394,6 +402,10 @@ func TestExportFlowControl(t *testing.T) {
 
 // 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)
index f2dc15409dbe89edab922382eee8e8ca5157e8c1..7fa4c7f4449b88781dc940ff5b3853bbed068f01 100644 (file)
@@ -10,6 +10,9 @@ import (
        "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 {
@@ -35,7 +38,7 @@ func (file *File) readdir(n int) (fi []FileInfo, err error) {
                                break
                        }
                        if d.nbuf < syscall.STATFIXLEN {
-                               return result, &PathError{"readdir", file.name, Eshortstat}
+                               return result, &PathError{"readdir", file.name, errShortStat}
                        }
                }
 
@@ -43,7 +46,7 @@ func (file *File) readdir(n int) (fi []FileInfo, err error) {
                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 {
@@ -138,7 +141,7 @@ func UnmarshalDir(b []byte) (d *Dir, err error) {
        n, b = gbit16(b)
 
        if int(n) != len(b) {
-               return nil, Ebadstat
+               return nil, errBadStat
        }
 
        d = new(Dir)
@@ -155,7 +158,7 @@ func UnmarshalDir(b []byte) (d *Dir, err error) {
        d.Muid, b = gstring(b)
 
        if len(b) != 0 {
-               return nil, Ebadstat
+               return nil, errBadStat
        }
 
        return d, nil
index 59350510ccfc1254e4047349cf040b1b42caf8d0..eb265f24138f2337028a602e9e58c4a7c0e19793 100644 (file)
@@ -6,10 +6,7 @@
 
 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.
@@ -77,26 +74,10 @@ func getShellName(s string) (string, int) {
        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
 }
 
index 135cdae1f9b74e506bb39f140ead4eddd43ded85..e0b83b5c22cabf46a586d0235fa036f179b17ae9 100644 (file)
@@ -4,6 +4,18 @@
 
 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
index cc847e0774324939756425bb5361d2a9529a7114..159d685e7cdb845b09212cfa3ae96ea657c37fd8 100644 (file)
@@ -4,34 +4,38 @@
 
 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
+}
index 57c9b6f27862c374a7fa91ceee3bb5a5aafe8108..74b75d1121836500e47365219e6f55b78ce87b67 100644 (file)
@@ -8,44 +8,29 @@ package os
 
 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
+}
index fe254672169c66e890e20979009f11a724733e83..ebe92a9fba39344c24c2c4350bc5870da6ed9413 100644 (file)
@@ -79,9 +79,9 @@ type Cmd struct {
        // 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
@@ -266,11 +266,11 @@ func (c *Cmd) Start() error {
 
 // 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.
@@ -291,8 +291,8 @@ func (c *Cmd) Wait() error {
                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 {
@@ -307,8 +307,8 @@ func (c *Cmd) Wait() error {
 
        if err != nil {
                return err
-       } else if !msg.Exited() || msg.ExitStatus() != 0 {
-               return &ExitError{msg}
+       } else if !state.Success() {
+               return &ExitError{state}
        }
 
        return copyError
index d88cd0df959a0b2a024053c52aef231c4d3bab1e..0e229e03ee7045d0c20cab18db97946bdc9dda8d 100644 (file)
@@ -8,6 +8,7 @@ import (
        "errors"
        "os"
        "strings"
+       "syscall"
 )
 
 // ErrNotFound is the error resulting if a path search failed to find an executable file.
@@ -21,7 +22,7 @@ func findExecutable(file string) error {
        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
index 2d3a919dc6e85348f4a54646206dab6d6ee4a505..21632219972b365fd60af78674e2797dc38d3dec 100644 (file)
@@ -23,7 +23,7 @@ func findExecutable(file string) error {
        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
index b7efcd68b8030be006262c681d00128487b72667..d8351d7e6d39408f36406c4da367e561e89d22fa 100644 (file)
@@ -19,7 +19,7 @@ func chkStat(file string) error {
                return err
        }
        if d.IsDir() {
-               return os.EPERM
+               return os.ErrPermission
        }
        return nil
 }
@@ -39,7 +39,7 @@ func findExecutable(file string, exts []string) (string, error) {
                        return f, nil
                }
        }
-       return ``, os.ENOENT
+       return ``, os.ErrNotExist
 }
 
 // LookPath searches for an executable binary named file
index 08f16b86d54e8939a6b6ba2f4d50a0b4d66bb833..1c9e2b997f8d4192e70d980124987f90ea460cf7 100644 (file)
@@ -8,6 +8,7 @@ import (
        "errors"
        "runtime"
        "syscall"
+       "time"
 )
 
 // StartProcess starts a new process with the program, arguments and attributes
@@ -20,18 +21,10 @@ func StartProcess(name string, argv []string, attr *ProcAttr) (p *Process, err e
                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}
@@ -72,19 +65,13 @@ func (p *Process) Kill() error {
        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 {
@@ -100,21 +87,11 @@ func (p *Process) Wait(options int) (w *Waitmsg, err error) {
                }
        }
 
-       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.
@@ -131,9 +108,57 @@ func findProcess(pid int) (p *Process, err error) {
        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
 }
index 33a689eb045ba8f73b7855a951819bc9b63bc34c..4a75cb67fb5ef35cecd357aebb2144630405546b 100644 (file)
@@ -42,32 +42,41 @@ func (p *Process) Kill() error {
        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.
@@ -97,26 +106,26 @@ func itod(i int) 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
index 7fe7c2fe8cdeac3d5c5514bb2bdaab2cd6ed83b6..8d000e9ef15e6e1107e8477df59d70711af89afe 100644 (file)
@@ -10,46 +10,30 @@ import (
        "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.
@@ -80,3 +64,13 @@ func findProcess(pid int) (p *Process, err error) {
        // 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
+}
index f357a3034b1f335e2b615906a2f7d13bdf9fa686..dab0dc97571c7c185e34b8cae4eed96f1771f5d2 100644 (file)
@@ -8,12 +8,13 @@ import (
        "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:
@@ -29,7 +30,7 @@ func (p *Process) Wait(options int) (w *Waitmsg, err error) {
                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.
@@ -48,7 +49,7 @@ func (p *Process) Signal(sig Signal) error {
 // 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 {
@@ -83,3 +84,15 @@ func init() {
                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
+}
index 85f151e2840d8a1567d5ef989730a222458f6ed6..1c3d0172d3433c0021b0bfa246d969f340f13a97 100644 (file)
@@ -7,11 +7,33 @@
 // 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 (
@@ -50,12 +72,25 @@ const (
        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 {
@@ -76,7 +111,7 @@ func (f *File) Read(b []byte) (n int, err error) {
 // 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)
@@ -99,7 +134,7 @@ func (f *File) ReadAt(b []byte, off int64) (n int, err error) {
 // 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 {
@@ -119,7 +154,7 @@ func (f *File) Write(b []byte) (n int, err error) {
 // 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)
@@ -153,7 +188,7 @@ func (f *File) Seek(offset int64, whence int) (ret int64, err error) {
 // 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))
 }
index fed2b809175d7a628ba1bf98c8ae7e1768eacf5b..cb0e9ef9289871690388086c6f328fdbb3e21216 100644 (file)
@@ -5,11 +5,14 @@
 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
@@ -26,19 +29,20 @@ type file struct {
 }
 
 // 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
 }
@@ -128,7 +132,7 @@ func OpenFile(name string, flag int, perm FileMode) (file *File, err error) {
                }
        }
 
-       return NewFile(fd, name), nil
+       return NewFile(uintptr(fd), name), nil
 }
 
 // Close closes the File, rendering it unusable for I/O.
@@ -139,7 +143,7 @@ func (file *File) Close() error {
 
 func (file *file) close() error {
        if file == nil || file.fd < 0 {
-               return Ebadfd
+               return ErrInvalid
        }
        var err error
        syscall.ForkLock.RLock()
@@ -202,7 +206,7 @@ func (f *File) Chmod(mode FileMode) error {
 // of recently written data to disk.
 func (f *File) Sync() (err error) {
        if f == nil {
-               return EINVAL
+               return ErrInvalid
        }
 
        var d Dir
@@ -272,7 +276,6 @@ func Remove(name string) error {
 }
 
 // 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()
@@ -330,34 +333,37 @@ func Pipe() (r *File, w *File, err error) {
        }
        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.
index 8d3a00b6c5a9c5362dc3c0956aca5a5f338dccc5..073bd56a471d4bb21b1f7dc55620c11ac8565cf5 100644 (file)
@@ -24,20 +24,8 @@ func epipecheck(file *File, e error) {
        }
 }
 
-// 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 {
@@ -47,6 +35,7 @@ func Link(oldname, newname string) error {
 }
 
 // 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 {
@@ -160,7 +149,7 @@ func (f *File) Truncate(size int64) error {
 // 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)
index 3d659efe061a61d9dfa22b0198f54473c71fa5e4..a69680cb8c908291cfd24f6b753116f1e974eb38 100644 (file)
@@ -89,7 +89,7 @@ func (f *File) Close() error {
 
 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 {
index 56836434dbe3f94a7f92d8333897d77e98a87f1f..81d8fed926eeca8cd54d7972a4897386ba023894 100644 (file)
@@ -52,7 +52,7 @@ func Getwd() (pwd string, err error) {
        pwd = ""
        for parent := ".."; ; parent = "../" + parent {
                if len(parent) >= 1024 { // Sanity check
-                       return "", ENAMETOOLONG
+                       return "", syscall.ENAMETOOLONG
                }
                fd, err := Open(parent)
                if err != nil {
@@ -74,7 +74,7 @@ func Getwd() (pwd string, err error) {
                        }
                }
                fd.Close()
-               return "", ENOENT
+               return "", ErrNotExist
 
        Found:
                pd, err := fd.Stat()
index 27436dadcdaeed5beb1baeb21d280a01f463ab19..cff35fcef75ce7b23f1c59dffb90e92163e794a2 100644 (file)
@@ -13,6 +13,7 @@ import (
        "path/filepath"
        "runtime"
        "strings"
+       "syscall"
        "testing"
        "time"
 )
@@ -538,7 +539,7 @@ func exec(t *testing.T, dir, cmd string, args []string, expect string) {
                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) {
@@ -767,7 +768,7 @@ func TestSeek(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
@@ -787,17 +788,17 @@ var openErrorTests = []openErrorTest{
        {
                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,
        },
 }
 
@@ -850,7 +851,7 @@ func run(t *testing.T, cmd []string) string {
 
        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)
        }
@@ -983,32 +984,31 @@ func TestAppend(t *testing.T) {
 }
 
 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>")
        }
 }
index e962f3e397b39d4d82b50cbab57d212ba0b76611..02a77ec805171ff4eeef187664c7fb8a7e1f6ec7 100644 (file)
@@ -4,7 +4,10 @@
 
 package os
 
-import "io"
+import (
+       "io"
+       "syscall"
+)
 
 // MkdirAll creates a directory named path,
 // along with any necessary parents, and returns nil,
@@ -20,7 +23,7 @@ func MkdirAll(path string, perm FileMode) error {
                if dir.IsDir() {
                        return nil
                }
-               return &PathError{"mkdir", path, ENOTDIR}
+               return &PathError{"mkdir", path, syscall.ENOTDIR}
        }
 
        // Doesn't already exist; make sure parent does.
@@ -70,7 +73,7 @@ func RemoveAll(path string) error {
        // 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
index 18634ba410ede3d23f8f4707181e6b5a99eadb1e..c1e3fb35436b96199956e329330d78c5f9856cfa 100644 (file)
@@ -8,18 +8,18 @@ import (
        . "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)
@@ -63,7 +63,7 @@ func TestMkdirAll(t *testing.T) {
        }
 
        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)
@@ -72,8 +72,9 @@ func TestMkdirAll(t *testing.T) {
 }
 
 func TestRemoveAll(t *testing.T) {
+       tmpDir := TempDir()
        // Work directory.
-       path := "_test/_TestRemoveAll_"
+       path := tmpDir + "/_TestRemoveAll_"
        fpath := path + "/file"
        dpath := path + "/dir"
 
@@ -170,19 +171,22 @@ func TestMkdirAllWithSymlink(t *testing.T) {
                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)
@@ -198,7 +202,7 @@ func TestMkdirAllAtSlash(t *testing.T) {
        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)
diff --git a/libgo/go/os/signal/signal_stub.go b/libgo/go/os/signal/signal_stub.go
new file mode 100644 (file)
index 0000000..fc227cf
--- /dev/null
@@ -0,0 +1,11 @@
+// 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) {}
index 00622581f4f054ea1555b03145defada55af2e9b..a7990a359ece90f520bb17980d88fbb24814b2c8 100644 (file)
@@ -62,7 +62,7 @@ func dirstat(arg interface{}) (d *Dir, err error) {
                        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.
@@ -79,7 +79,7 @@ func dirstat(arg interface{}) (d *Dir, err error) {
                        return
                }
        }
-       return nil, &PathError{"stat", name, Ebadstat}
+       return nil, &PathError{"stat", name, errBadStat}
 }
 
 // Stat returns a FileInfo structure describing the named file.
index f0ac1301408755f1f048c17f2f672a007abdb587..ca18b32305e68a8223977aafc99e0742d455c222 100644 (file)
@@ -11,17 +11,11 @@ import (
        "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",
@@ -35,31 +29,39 @@ func ExampleClean() {
        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
 }
 
 */
index c3678f541d4631b13f61d7c000665922535a14bc..38d264fb97aa92cf8eb4090338ff70e679317269 100644 (file)
@@ -12,6 +12,7 @@ import (
        "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.
@@ -33,7 +34,8 @@ var ErrBadPattern = errors.New("syntax error in 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:
@@ -211,7 +213,6 @@ func getEsc(chunk string) (r rune, nchunk string, err error) {
 // 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) {
@@ -253,7 +254,6 @@ func Glob(pattern string) (matches []string, err error) {
 // 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)
index 3dc52aab4672873a6ab1a00b1d7f66b4174bdd5c..f468d33264bf50c2b9c0dcbf7e0d9e36f4299445 100644 (file)
@@ -36,7 +36,7 @@ const (
 // 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)
@@ -118,7 +118,8 @@ func Clean(path string) string {
 }
 
 // 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
@@ -127,7 +128,8 @@ func ToSlash(path string) string {
 }
 
 // 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
@@ -135,7 +137,8 @@ func FromSlash(path string) string {
        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{}
@@ -158,7 +161,8 @@ func Split(path string) (dir, file 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 != "" {
@@ -183,7 +187,8 @@ func Ext(path string) string {
 
 // 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.
@@ -443,7 +448,7 @@ func Base(path string) string {
        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.
index 36ab422c930b713719d3494170e03f5ede41eff7..93cca1e4c2b141ec255a9a45d01353fb0f6c0980 100644 (file)
@@ -558,6 +558,7 @@ var EvalSymlinksTestDirs = []EvalSymlinksTest{
        {"test/dir/link3", "../../"},
        {"test/link1", "../test"},
        {"test/link2", "dir"},
+       {"test/linkabs", "/"},
 }
 
 var EvalSymlinksTests = []EvalSymlinksTest{
@@ -570,6 +571,7 @@ var EvalSymlinksTests = []EvalSymlinksTest{
        {"test/link2/..", "test"},
        {"test/dir/link3", "."},
        {"test/link2/link3/test", "test"},
+       {"test/linkabs", "/"},
 }
 
 var EvalSymlinksAbsWindowsTests = []EvalSymlinksTest{
@@ -628,6 +630,9 @@ func TestEvalSymlinks(t *testing.T) {
        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) {
index ba7e4de321e3353ec0afa9813a44841538cfe60b..8154bf6025162104d2f205322a0a3a36fb8115b8 100644 (file)
@@ -10,6 +10,7 @@ import (
        "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.
@@ -31,7 +32,8 @@ var ErrBadPattern = errors.New("syntax error in 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:
index 20d89c9ff0cbcbee125e1c88ffa198cc67563ef5..13abed0b09da25120f3d920d72b658abb9e2ba83 100644 (file)
@@ -3,7 +3,7 @@
 // license that can be found in the LICENSE file.
 
 // Package path implements utility routines for manipulating slash-separated
-// filename paths.
+// paths.
 package path
 
 import (
@@ -25,7 +25,7 @@ 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 == "" {
@@ -100,17 +100,19 @@ func Clean(path string) string {
        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 != "" {
@@ -161,11 +163,12 @@ func IsAbs(path string) bool {
        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)
index bd6dcc971a1a4a37f91785599854b23b73ce5477..b802fc63f71d32d08bdbab8534a79d4c24112c46 100644 (file)
@@ -26,23 +26,11 @@ func GOMAXPROCS(n int) int
 // 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.
@@ -101,15 +89,14 @@ func (r *MemProfileRecord) Stack() []uintptr {
 // 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]
@@ -118,13 +105,21 @@ func (r *ThreadProfileRecord) Stack() []uintptr {
        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
@@ -142,3 +137,9 @@ func CPUProfile() []byte
 // 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
index eafa2f19f19e929ddbaf17617ad49922eba602bd..5fbfe547e46b485ecd20e292e8a1caf0f52f1530 100644 (file)
@@ -68,17 +68,6 @@ func funcline_go(*Func, uintptr) (string, 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
@@ -141,10 +130,10 @@ func Version() string {
        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
diff --git a/libgo/go/runtime/malloc1.go b/libgo/go/runtime/malloc1.go
new file mode 100644 (file)
index 0000000..da92f4c
--- /dev/null
@@ -0,0 +1,26 @@
+// 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))
+       }
+}
diff --git a/libgo/go/runtime/mallocrand.go b/libgo/go/runtime/mallocrand.go
new file mode 100644 (file)
index 0000000..f1bcb89
--- /dev/null
@@ -0,0 +1,93 @@
+// 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()
+       }
+}
diff --git a/libgo/go/runtime/mallocrep.go b/libgo/go/runtime/mallocrep.go
new file mode 100644 (file)
index 0000000..03ee71e
--- /dev/null
@@ -0,0 +1,72 @@
+// 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
+               }
+       }
+}
diff --git a/libgo/go/runtime/mallocrep1.go b/libgo/go/runtime/mallocrep1.go
new file mode 100644 (file)
index 0000000..41c104c
--- /dev/null
@@ -0,0 +1,143 @@
+// 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
+                       }
+               }
+       }
+}
index 1301674c027adc923d2a377510ec0a2b808a82b5..95e8aa7a5337ff70100cfc02f20e353fc71d9560 100644 (file)
@@ -17,11 +17,12 @@ type MemStats struct {
        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.
@@ -35,7 +36,8 @@ type MemStats struct {
        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
index 42f04f320a703f654258bf26dc77e86632efd292..099bb6a92f94f937f0803b6f74eb3027ba2dbe83 100644 (file)
@@ -10,19 +10,354 @@ package pprof
 
 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
@@ -44,6 +379,16 @@ func WriteHeapProfile(w io.Writer) error {
                // 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]
@@ -56,78 +401,120 @@ func WriteHeapProfile(w io.Writer) error {
        // 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
@@ -135,19 +522,14 @@ func WriteThreadProfile(w io.Writer) error {
                // 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
diff --git a/libgo/go/runtime/sema_test.go b/libgo/go/runtime/sema_test.go
deleted file mode 100644 (file)
index d95bb1e..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-// 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)
-}
diff --git a/libgo/go/sort/example_interface_test.go b/libgo/go/sort/example_interface_test.go
new file mode 100644 (file)
index 0000000..4c88821
--- /dev/null
@@ -0,0 +1,77 @@
+// 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)
+       }
+}
diff --git a/libgo/go/sort/example_reverse_test.go b/libgo/go/sort/example_reverse_test.go
new file mode 100644 (file)
index 0000000..7c7f05b
--- /dev/null
@@ -0,0 +1,30 @@
+// 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]
+}
index 2224db7e13c2d5bd8d894d6212b7ee1ee383bf8d..f57d02546f79aecc82e788ad2ede8ab01a3e7a66 100644 (file)
@@ -9,9 +9,9 @@ import (
        "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]
 }
index cd3031b0e61b86d30998389534e3ff82746a2460..d99117bed1d0b7878d8d5bc4655cae96269b9c39 100644 (file)
@@ -13,6 +13,7 @@ package strconv
 //   3) Multiply by 2^precision and round to get mantissa.
 
 import "math"
+import "runtime"
 
 var optimize = true // can change for testing
 
@@ -300,6 +301,11 @@ func (d *decimal) atof64() (f float64, ok bool) {
        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()
index bac23e6ea6742c816ca9f10367f82e22c193e72c..63d2fa44e02729adc12393aabe73341ddcc4aed9 100644 (file)
@@ -127,6 +127,7 @@ func TestUitoa(t *testing.T) {
 }
 
 func numAllocations(f func()) int {
+       runtime.GC()
        memstats := new(runtime.MemStats)
        runtime.ReadMemStats(memstats)
        n0 := memstats.Mallocs
index 5ef0b93d15e5d6ef9b3fc3c8a8707ac15d95b5d0..0b583411331a0be0e92c1e77ee2625356d13a320 100644 (file)
@@ -9,134 +9,142 @@ import (
        "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 {
@@ -148,25 +156,26 @@ func ExampleMap() {
                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 &lt;b&gt;HTML&lt;/b&gt;!
 func ExampleNewReplacer() {
        r := strings.NewReplacer("<", "&lt;", ">", "&gt;")
        fmt.Println(r.Replace("This is <b>HTML</b>!"))
+       // Output: This is &lt;b&gt;HTML&lt;/b&gt;!
 }
 
-// GOPHER
 func ExampleToUpper() {
        fmt.Println(strings.ToUpper("Gopher"))
+       // Output: GOPHER
 }
 
-// gopher
 func ExampleToLower() {
        fmt.Println(strings.ToLower("Gopher"))
+       // Output: gopher
 }
index 75494b53536fb8b11bfe4498eed4dd84010b79fd..1fc3deaf1e0c40013cc9b53cc210bf8f551dd6b6 100644 (file)
@@ -4,8 +4,6 @@
 
 package sync
 
-import "runtime"
-
 // Cond implements a condition variable, a rendezvous point
 // for goroutines waiting for or announcing the occurrence
 // of an event.
@@ -43,9 +41,10 @@ func NewCond(l Locker) *Cond {
 
 // 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:
 //
@@ -65,7 +64,7 @@ func (c *Cond) Wait() {
        c.newWaiters++
        c.m.Unlock()
        c.L.Unlock()
-       runtime.Semacquire(s)
+       runtime_Semacquire(s)
        c.L.Lock()
 }
 
@@ -84,7 +83,7 @@ func (c *Cond) Signal() {
        }
        if c.oldWaiters > 0 {
                c.oldWaiters--
-               runtime.Semrelease(c.oldSema)
+               runtime_Semrelease(c.oldSema)
        }
        c.m.Unlock()
 }
@@ -98,13 +97,13 @@ func (c *Cond) Broadcast() {
        // 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
diff --git a/libgo/go/sync/example_test.go b/libgo/go/sync/example_test.go
new file mode 100644 (file)
index 0000000..1424b1e
--- /dev/null
@@ -0,0 +1,34 @@
+// 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()
+}
diff --git a/libgo/go/sync/export_test.go b/libgo/go/sync/export_test.go
new file mode 100644 (file)
index 0000000..fa5983a
--- /dev/null
@@ -0,0 +1,9 @@
+// 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
index 4fc02743c6e717b19903ee99867f7d7cafba375b..9494cc3f82679f6cf364d91bb519727c7cfb54a2 100644 (file)
 // 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;
@@ -60,7 +57,7 @@ func (m *Mutex) Lock() {
                        if old&mutexLocked == 0 {
                                break
                        }
-                       runtime.Semacquire(&m.sema)
+                       runtime_Semacquire(&m.sema)
                        awoke = true
                }
        }
@@ -89,7 +86,7 @@ func (m *Mutex) Unlock() {
                // 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
index a514b4ad4c02a8086e4bb4a09b40217318eb74bb..bf78c6f609ca67c7e0ce42822521900dc089ac61 100644 (file)
@@ -15,8 +15,8 @@ import (
 
 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
 }
diff --git a/libgo/go/sync/runtime.go b/libgo/go/sync/runtime.go
new file mode 100644 (file)
index 0000000..e99599c
--- /dev/null
@@ -0,0 +1,18 @@
+// 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)
diff --git a/libgo/go/sync/runtime_sema_test.go b/libgo/go/sync/runtime_sema_test.go
new file mode 100644 (file)
index 0000000..57a8dbe
--- /dev/null
@@ -0,0 +1,101 @@
+// 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)
+}
index cb1a47720b21e9fa6af0c002208b77753c41b81b..782a9c319682b9bade8a0204363b9cbce8f27edc 100644 (file)
@@ -4,10 +4,7 @@
 
 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
@@ -29,7 +26,7 @@ const rwmutexMaxReaders = 1 << 30
 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)
        }
 }
 
@@ -42,7 +39,7 @@ func (rw *RWMutex) RUnlock() {
                // 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)
                }
        }
 }
@@ -60,7 +57,7 @@ func (rw *RWMutex) Lock() {
        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)
        }
 }
 
@@ -75,7 +72,7 @@ func (rw *RWMutex) Unlock() {
        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()
index a4c9b7e43cd852d3e778795d3c401f800e60f223..3e7d9d3c8f4099cdadb0a1e7b1b202c2cd509a69 100644 (file)
@@ -4,10 +4,7 @@
 
 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
@@ -60,7 +57,7 @@ func (wg *WaitGroup) Add(delta int) {
        }
        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
@@ -93,5 +90,5 @@ func (wg *WaitGroup) Wait() {
        }
        s := wg.sema
        wg.m.Unlock()
-       runtime.Semacquire(s)
+       runtime_Semacquire(s)
 }
index d5d60eae4cd20cb72929842cd4c284fccd762a5f..adc8c09f2173356269cbeca32f1aa0639e232e79 100644 (file)
 //         }
 //     }
 //
-// 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:
diff --git a/libgo/go/text/tabwriter/example_test.go b/libgo/go/text/tabwriter/example_test.go
new file mode 100644 (file)
index 0000000..20443cb
--- /dev/null
@@ -0,0 +1,38 @@
+// 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.
+}
index ea7c40081115b3f58be753d00e1d8b7624f2e31a..ce84600d6049562e877316456faaaf815acbe429 100644 (file)
@@ -169,12 +169,6 @@ const (
 //                     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")
index 1ffb330d432926c139a318688319d3fa18da411f..ace53564737acb36c3db2c60a24e33dfe4c5ab85 100644 (file)
@@ -2,11 +2,12 @@
 // 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 {
index 35c4c6811831a472ced222be42c7081820d23cc4..ae91f4a5419d3e92804be54ee637704ee2e93f5c 100644 (file)
@@ -22,6 +22,20 @@ Actions may not span newlines, although comments can.
 
 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
@@ -128,6 +142,11 @@ An argument is a simple value, denoted by one of the following.
            .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
@@ -148,6 +167,9 @@ value (argument) or a function or method call, possibly with multiple arguments:
                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:
@@ -303,7 +325,7 @@ produce the text
 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
index 973189a8a62c97de524bab028c4e730f0af023dd..af745286c0bc3f6bc553c2f0e4a55a30be18fcb1 100644 (file)
@@ -419,10 +419,14 @@ func (s *state) evalField(dot reflect.Value, fieldName string, args []parse.Node
                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
                        }
                }
index 9bb55e48aac602df616d5d2edd924a8acbeb06b2..159cf5100d933eaf0217bf685bc6f20e05d0915a 100644 (file)
@@ -59,6 +59,8 @@ type T struct {
        PI  *int
        PSI *[]int
        NIL *int
+       // Function (not method)
+       Func func(...string) string
        // Template to test evaluation of templates.
        Tmpl *Template
 }
@@ -118,6 +120,7 @@ var tVal = &T{
        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
 }
 
@@ -297,8 +300,13 @@ var execTests = []execTest{
                "{{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},
index 274f5ef147778afd0a94908abc4aee657e65e3c9..f205e6be1b4ebf9d5743443de0af1c3515650c1a 100644 (file)
@@ -193,7 +193,7 @@ func TestClone(t *testing.T) {
        if err != nil {
                t.Fatal(err)
        }
-       clone := root.Clone()
+       clone := Must(root.Clone())
        // Add variants to both.
        _, err = root.Parse(cloneText3)
        if err != nil {
index 4da756657d5201c152a42b13050c628407603bce..35194f7dfdb5ebeb570a2da1af4b1405e77214e9 100644 (file)
@@ -2,8 +2,10 @@
 // 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 (
index 87e39d3af740f817c1f1a9825f79d2fd88b75a92..7494f9d8c45a4286e90bf22c9bb85357e3c91498 100644 (file)
@@ -69,9 +69,9 @@ func (t *Template) init() {
 // 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
@@ -89,7 +89,7 @@ func (t *Template) Clone() *Template {
        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.
index b25e64cda3e0e1ad999713afd3e30de5a0c88283..944cc789c3167e21480ee31c02c144b017168996 100644 (file)
@@ -51,8 +51,8 @@ func ExampleMonth() {
        }
 }
 
-// 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
 }
index c7cfa792a29f3c717b8dc159e31980ddc27b846d..8484729448eb96ec155e4942b8e69f8dcf8d050c 100644 (file)
@@ -6,7 +6,10 @@
 
 package time
 
-import "syscall"
+import (
+       "errors"
+       "syscall"
+)
 
 // for testing: whatever interrupts a sleep
 func interrupt() {
@@ -38,3 +41,36 @@ func readFile(name string) ([]byte, error) {
        }
        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
+}
index 56a7414e0ce875ddbb7d8c33771c3bd750c8f5bb..7f69b492c9fdb1536899007515687797690e561a 100644 (file)
@@ -6,7 +6,10 @@
 
 package time
 
-import "syscall"
+import (
+       "errors"
+       "syscall"
+)
 
 // for testing: whatever interrupts a sleep
 func interrupt() {
@@ -38,3 +41,36 @@ func readFile(name string) ([]byte, error) {
        }
        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
+}
index 8c7242f4275cb7b5718a97b81545c9f34c00de7d..de63b4bf4bb87be5b8c7b17f038a640358b31180 100644 (file)
@@ -4,6 +4,70 @@
 
 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
+}
index 0c1c4d67ab00500069fef149992c0e3d4adc7c69..914f02c861c556a13f7572dea0b36bba701fda61 100644 (file)
@@ -12,9 +12,6 @@ import (
 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++ {
index aca56e746af1870e8e65d0d54ae9994841029615..3c57744043e24383c666e49e5e27169b873eee55 100644 (file)
@@ -4,7 +4,10 @@
 
 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
@@ -168,10 +171,7 @@ func (l *Location) lookupOffset(offset int) (name string, isDST bool, ok bool) {
 // 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.
 //
@@ -180,6 +180,13 @@ func (l *Location) lookupOffset(offset int) (name string, isDST bool, ok bool) {
 //
 // 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
@@ -187,5 +194,11 @@ func LoadLocation(name string) (*Location, error) {
        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)
 }
index 9c052d42cd32376b2aa4dfdb58adbbb2e360558e..6855238dc84f0b4c2456b93d066fd5dc14504b1a 100644 (file)
@@ -8,11 +8,10 @@ package time
 
 import (
        "errors"
+       "runtime"
        "syscall"
 )
 
-var badData = errors.New("malformed time zone information")
-
 func isSpace(r rune) bool {
        return r == ' ' || r == '\t' || r == '\n'
 }
@@ -51,7 +50,7 @@ func fields(s string) []string {
        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" {
@@ -112,33 +111,32 @@ func loadZoneData(s string) (l *Location, err error) {
        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
@@ -150,7 +148,8 @@ func initLocal() {
 }
 
 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)
diff --git a/libgo/go/time/zoneinfo_read.go b/libgo/go/time/zoneinfo_read.go
new file mode 100644 (file)
index 0000000..ebb4205
--- /dev/null
@@ -0,0 +1,341 @@
+// 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)
+}
index 540b653c57d22e9977e38c291f3d29ca2c68a0d7..1bf1f11e24f3bc3e867c8db6d0e78ca7f6c50852 100644 (file)
@@ -13,200 +13,10 @@ package time
 
 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()
@@ -218,6 +28,7 @@ var zoneDirs = []string{
        "/usr/share/zoneinfo/",
        "/usr/share/lib/zoneinfo/",
        "/usr/lib/locale/TZ/",
+       runtime.GOROOT() + "/lib/time/zoneinfo/",
 }
 
 func initLocal() {
@@ -229,7 +40,7 @@ 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"
@@ -248,7 +59,7 @@ func initLocal() {
 
 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
                }
index beef4de92b077f70f4eca693303531874fa23d92..754e392decabe0c1304aa144f297cf1662409cd3 100644 (file)
@@ -6,6 +6,7 @@ package time
 
 import (
        "errors"
+       "runtime"
        "syscall"
 )
 
@@ -151,7 +152,10 @@ func initLocal() {
        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)
 }
index 8b48c8663464191967825fd9867d113665b4a718..23641e8298f71dbdb565c9fda2562b0265d2bc6e 100644 (file)
@@ -19,6 +19,7 @@ package runtime
 #include "go-type.h"
 
 MHeap runtime_mheap;
+
 extern MStats mstats;  // defined in extern.go
 
 extern volatile int32 runtime_MemProfileRate
@@ -429,18 +430,6 @@ func new(typ *Type) (ret *uint8) {
        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);
 }
index c1cf02c6debf973b65546d3d951af305547df144..4cb07477f1534ecbc59d09ae5702c97905c17d15 100644 (file)
@@ -205,6 +205,7 @@ struct MStats
        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.
@@ -220,6 +221,7 @@ struct MStats
        // 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;
@@ -304,14 +306,16 @@ struct MSpan
 {
        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);
@@ -381,6 +385,7 @@ MSpan*      runtime_MHeap_LookupMaybe(MHeap *h, void *v);
 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);
@@ -406,19 +411,11 @@ enum
 
 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));
index 73c399df239ed0eee8eda4665b273002ec92b179..d852946cdbb6c6f3e9f7f3bb02febc5e88148b8c 100644 (file)
@@ -61,6 +61,21 @@ enum {
 
 #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;
 
@@ -92,7 +107,6 @@ struct FinBlock
        Finalizer fin[1];
 };
 
-
 static G *fing;
 static FinBlock *finq; // list of finalizers that are to be executed
 static FinBlock *finc; // cache of free blocks
@@ -778,9 +792,11 @@ sweep(void)
        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;
@@ -789,6 +805,11 @@ sweep(void)
                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;
 
@@ -875,11 +896,6 @@ runtime_gchelper(void)
                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
@@ -968,9 +984,9 @@ runtime_gc(int32 force)
        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;
        }
 
@@ -1032,6 +1048,7 @@ runtime_gc(int32 force)
        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++;
@@ -1045,8 +1062,9 @@ runtime_gc(int32 force)
                        (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.
@@ -1073,18 +1091,18 @@ runtime_ReadMemStats(MStats *stats)
 {
        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);
 }
 
index 5a5a1e71a1233e24dbbf2e73c0c8f2f670b26fa4..79359d9dfcafacf1ddfa064b5a4ce764efc819e7 100644 (file)
@@ -103,6 +103,8 @@ HaveSpan:
        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.
@@ -280,6 +282,8 @@ MHeap_FreeLocked(MHeap *h, MSpan *s)
        }
        mstats.heap_idle += s->npages<<PageShift;
        s->state = MSpanFree;
+       s->unusedsince = 0;
+       s->npreleased = 0;
        runtime_MSpanList_Remove(s);
        sp = (uintptr*)(s->start<<PageShift);
 
@@ -292,6 +296,7 @@ MHeap_FreeLocked(MHeap *h, MSpan *s)
                *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);
@@ -304,6 +309,7 @@ MHeap_FreeLocked(MHeap *h, MSpan *s)
                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;
@@ -317,8 +323,86 @@ MHeap_FreeLocked(MHeap *h, MSpan *s)
                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(&note);
+               runtime_entersyscall();
+               runtime_notetsleep(&note, 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.
@@ -333,6 +417,8 @@ runtime_MSpan_Init(MSpan *span, PageID start, uintptr npages)
        span->ref = 0;
        span->sizeclass = 0;
        span->state = 0;
+       span->unusedsince = 0;
+       span->npreleased = 0;
 }
 
 // Initialize an empty doubly-linked list.
index 95e05fabeb3958f2d889defafbd54efdb1ac0f79..e40dd61f7848c48178cf51e37c2b86a9925840e6 100644 (file)
@@ -26,6 +26,10 @@ struct Bucket
        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];
@@ -39,7 +43,7 @@ static uintptr bucketmem;
 
 // 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;
@@ -66,6 +70,9 @@ stkbucket(uintptr *stk, int32 nstk)
                   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]);
@@ -78,6 +85,26 @@ stkbucket(uintptr *stk, int32 nstk)
        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.
@@ -204,9 +231,9 @@ runtime_MProf_Malloc(void *p, uintptr size)
        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();
@@ -228,8 +255,8 @@ runtime_MProf_Free(void *p, uintptr size)
        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();
@@ -293,13 +320,13 @@ runtime_MProf_Mark(void (*scan)(byte *, int64))
        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;
        
@@ -317,3 +344,89 @@ func ThreadProfile(p Slice) (n int32, ok bool) {
                }
        }
 }
+
+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);
+       }
+}
+
index a4e4588299e47e631c81a579c0ac1d7383cfc540..d0ae09c45a096a77a3b3df44b686489fca42f05b 100644 (file)
@@ -362,6 +362,9 @@ runtime_mcall(void (*pfn)(G*))
        }
 }
 
+// Keep trace of scavenger's goroutine for deadlock detection.
+static G *scvg;
+
 // The bootstrap sequence is:
 //
 //     call osinit
@@ -413,6 +416,8 @@ runtime_schedinit(void)
        // 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");
@@ -547,7 +552,7 @@ mcommoninit(M *m)
        // 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);
 }
@@ -786,9 +791,12 @@ top:
                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);
@@ -797,6 +805,7 @@ top:
        // 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);
@@ -1472,11 +1481,17 @@ runtime_mid()
        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;
 }
index 713af1744910bdb287f87fbf36297d708c162c2f..113bb7163c524962b50449def4fd39540e4e088d 100644 (file)
@@ -143,6 +143,8 @@ struct      G
        M*      lockedm;
        M*      idlem;
        // int32        sig;
+       int32   writenbuf;
+       byte*   writebuf;
        // uintptr      sigcode0;
        // uintptr      sigcode1;
        // uintptr      sigpc;
@@ -189,9 +191,9 @@ struct      SigTab
 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
 };
 
@@ -277,6 +279,7 @@ void        runtime_panicstring(const char*) __attribute__ ((noreturn));
 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*);
@@ -296,6 +299,7 @@ int64       runtime_cputicks(void);
 
 void   runtime_stoptheworld(void);
 void   runtime_starttheworld(bool);
+extern uint32 runtime_worldsema;
 G*     __go_go(void (*pfn)(void*), void*);
 
 /*
@@ -348,6 +352,7 @@ void        runtime_futexwakeup(uint32*, uint32);
 #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 **);
@@ -373,8 +378,6 @@ void reflect_call(const struct __go_func_type *, const void *, _Bool, _Bool,
 #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)
@@ -384,6 +387,11 @@ bool       runtime_addfinalizer(void*, void(*fn)(void*), const struct __go_func_type *
 #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*);
index dd58cf38fb88f6e4893242ea6a8f479673348052..ff9c4f2e19e5f64fc5d70950270da3ecc97ec16d 100644 (file)
@@ -17,7 +17,7 @@
 // See Mullender and Cox, ``Semaphores in Plan 9,''
 // http://swtch.com/semaphore.pdf
 
-package runtime
+package sync
 #include "runtime.h"
 #include "arch.h"
 
@@ -172,10 +172,10 @@ runtime_semrelease(uint32 volatile *addr)
                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);
 }
index c550a4ebe9b864203c03870155ffd76905916538..be7c5920cbc5b4230fa1db2ea4f5b976a92a30d3 100644 (file)
@@ -142,10 +142,12 @@ func signal_enable(s uint32) {
                // 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);
 }