]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
libgo: Update to current master library sources.
authorIan Lance Taylor <ian@gcc.gnu.org>
Wed, 12 Dec 2012 23:13:29 +0000 (23:13 +0000)
committerIan Lance Taylor <ian@gcc.gnu.org>
Wed, 12 Dec 2012 23:13:29 +0000 (23:13 +0000)
From-SVN: r194460

145 files changed:
libgo/MERGE
libgo/Makefile.am
libgo/Makefile.in
libgo/go/bufio/bufio.go
libgo/go/bufio/bufio_test.go
libgo/go/bytes/buffer.go
libgo/go/bytes/buffer_test.go
libgo/go/bytes/reader.go
libgo/go/bytes/reader_test.go
libgo/go/container/heap/example_test.go
libgo/go/container/ring/ring.go
libgo/go/crypto/tls/conn.go
libgo/go/crypto/tls/handshake_client_test.go
libgo/go/crypto/tls/handshake_server_test.go
libgo/go/crypto/tls/tls.go
libgo/go/crypto/tls/tls_test.go
libgo/go/encoding/binary/binary.go
libgo/go/encoding/csv/writer.go
libgo/go/encoding/gob/doc.go
libgo/go/encoding/gob/timing_test.go
libgo/go/encoding/xml/marshal.go
libgo/go/exp/cookiejar/jar.go [new file with mode: 0644]
libgo/go/exp/cookiejar/storage.go [new file with mode: 0644]
libgo/go/exp/cookiejar/storage_test.go [new file with mode: 0644]
libgo/go/exp/gotype/gotype_test.go
libgo/go/exp/gotype/testdata/test1.go
libgo/go/exp/locale/collate/collate.go
libgo/go/exp/types/check.go
libgo/go/exp/types/check_test.go
libgo/go/exp/types/const.go
libgo/go/exp/types/expr.go
libgo/go/exp/types/operand.go
libgo/go/exp/types/predicates.go
libgo/go/exp/types/stmt.go
libgo/go/exp/types/testdata/decls0.src
libgo/go/exp/types/testdata/decls1.src
libgo/go/exp/types/testdata/expr3.src
libgo/go/exp/types/testdata/stmt0.src
libgo/go/exp/types/types.go
libgo/go/exp/types/universe.go
libgo/go/exp/winfsnotify/winfsnotify_test.go
libgo/go/fmt/fmt_test.go
libgo/go/go/ast/import.go
libgo/go/go/format/format.go [new file with mode: 0644]
libgo/go/go/format/format_test.go [new file with mode: 0644]
libgo/go/go/parser/parser.go
libgo/go/go/printer/nodes.go
libgo/go/go/printer/performance_test.go
libgo/go/go/printer/printer.go
libgo/go/go/printer/printer_test.go
libgo/go/go/printer/testdata/statements.golden
libgo/go/go/printer/testdata/statements.input
libgo/go/html/template/js.go
libgo/go/image/jpeg/huffman.go
libgo/go/log/syslog/syslog.go
libgo/go/log/syslog/syslog_test.go
libgo/go/math/big/nat_test.go
libgo/go/mime/multipart/multipart.go
libgo/go/mime/multipart/multipart_test.go
libgo/go/mime/multipart/quotedprintable.go [new file with mode: 0644]
libgo/go/mime/multipart/quotedprintable_test.go [new file with mode: 0644]
libgo/go/mime/multipart/writer.go
libgo/go/mime/multipart/writer_test.go
libgo/go/net/dial.go
libgo/go/net/fd_posix_test.go [new file with mode: 0644]
libgo/go/net/fd_unix.go
libgo/go/net/fd_unix_test.go
libgo/go/net/fd_windows.go
libgo/go/net/file_plan9.go
libgo/go/net/http/cgi/host_test.go
libgo/go/net/http/cgi/testdata/test.cgi
libgo/go/net/http/chunked.go
libgo/go/net/http/chunked_test.go
libgo/go/net/http/client_test.go
libgo/go/net/http/export_test.go
libgo/go/net/http/header_test.go
libgo/go/net/http/httptest/server.go
libgo/go/net/http/httputil/chunked.go
libgo/go/net/http/httputil/chunked_test.go
libgo/go/net/http/request.go
libgo/go/net/http/request_test.go
libgo/go/net/http/response.go
libgo/go/net/http/response_test.go
libgo/go/net/http/serve_test.go
libgo/go/net/http/server.go
libgo/go/net/http/transfer.go
libgo/go/net/http/transport.go
libgo/go/net/http/transport_test.go
libgo/go/net/ip.go
libgo/go/net/ip_test.go
libgo/go/net/ipraw_test.go
libgo/go/net/iprawsock.go
libgo/go/net/iprawsock_plan9.go
libgo/go/net/iprawsock_posix.go
libgo/go/net/ipsock.go
libgo/go/net/ipsock_plan9.go
libgo/go/net/ipsock_posix.go
libgo/go/net/multicast_posix_test.go
libgo/go/net/net.go
libgo/go/net/rpc/server.go
libgo/go/net/rpc/server_test.go
libgo/go/net/sendfile_freebsd.go
libgo/go/net/sendfile_linux.go
libgo/go/net/sock_posix.go
libgo/go/net/sockopt_posix.go
libgo/go/net/tcp_test.go
libgo/go/net/tcpsock.go
libgo/go/net/tcpsock_plan9.go
libgo/go/net/tcpsock_posix.go
libgo/go/net/timeout_test.go
libgo/go/net/udp_test.go
libgo/go/net/udpsock.go
libgo/go/net/udpsock_plan9.go
libgo/go/net/udpsock_posix.go
libgo/go/net/unixsock_plan9.go
libgo/go/net/url/url.go
libgo/go/os/dir_plan9.go
libgo/go/os/file_plan9.go
libgo/go/os/os_test.go
libgo/go/os/stat_plan9.go
libgo/go/os/user/lookup.go [new file with mode: 0644]
libgo/go/os/user/lookup_stubs.go
libgo/go/os/user/lookup_unix.go
libgo/go/os/user/lookup_windows.go
libgo/go/path/filepath/path_test.go
libgo/go/path/path_test.go
libgo/go/reflect/all_test.go
libgo/go/reflect/type.go
libgo/go/regexp/all_test.go
libgo/go/regexp/example_test.go
libgo/go/regexp/regexp.go
libgo/go/runtime/debug.go
libgo/go/runtime/extern.go
libgo/go/runtime/gc_test.go
libgo/go/runtime/mallocrep1.go
libgo/go/strconv/strconv_test.go
libgo/go/strings/reader.go
libgo/go/strings/reader_test.go
libgo/go/syscall/dir_plan9.go [new file with mode: 0644]
libgo/go/time/example_test.go
libgo/go/time/time.go
libgo/go/time/time_test.go
libgo/runtime/chan.c
libgo/runtime/mgc0.c
libgo/runtime/race.h

index 5ab9716a4eeeadab64d683a95d4f5c1c8fa329e7..deeb596317d39ef6f7acc4665883f11f158e29c8 100644 (file)
@@ -1,4 +1,4 @@
-a070de932857
+c031aa767edf
 
 The first line of this file holds the Mercurial revision number of the
 last merge done from the master library sources.
index 50a21b8cd1bc2c6aac5d52407776b144955b29de..fe0f20585fc520127bd7ca473216ccf927accc47 100644 (file)
@@ -221,6 +221,7 @@ endif
 toolexeclibgoexpdir = $(toolexeclibgodir)/exp
 
 toolexeclibgoexp_DATA = \
+       exp/cookiejar.gox \
        exp/ebnf.gox \
        exp/html.gox \
        $(exp_inotify_gox) \
@@ -251,6 +252,7 @@ toolexeclibgogo_DATA = \
        go/ast.gox \
        go/build.gox \
        go/doc.gox \
+       go/format.gox \
        go/parser.gox \
        go/printer.gox \
        go/scanner.gox \
@@ -1194,6 +1196,9 @@ go_encoding_xml_files = \
        go/encoding/xml/typeinfo.go \
        go/encoding/xml/xml.go
 
+go_exp_cookiejar_files = \
+       go/exp/cookiejar/jar.go \
+       go/exp/cookiejar/storage.go
 go_exp_ebnf_files = \
        go/exp/ebnf/ebnf.go \
        go/exp/ebnf/parser.go
@@ -1284,6 +1289,8 @@ go_go_doc_files = \
        go/go/doc/filter.go \
        go/go/doc/reader.go \
        go/go/doc/synopsis.go
+go_go_format_files = \
+       go/go/format/format.go
 go_go_parser_files = \
        go/go/parser/interface.go \
        go/go/parser/parser.go
@@ -1384,6 +1391,7 @@ go_math_rand_files = \
 go_mime_multipart_files = \
        go/mime/multipart/formdata.go \
        go/mime/multipart/multipart.go \
+       go/mime/multipart/quotedprintable.go \
        go/mime/multipart/writer.go
 
 go_net_http_files = \
@@ -1456,6 +1464,7 @@ go_os_signal_files = \
 
 go_os_user_files = \
        go/os/user/user.go \
+       go/os/user/lookup.go \
        go/os/user/lookup_unix.go
 
 go_path_filepath_files = \
@@ -1822,6 +1831,7 @@ libgo_go_objs = \
        encoding/json.lo \
        encoding/pem.lo \
        encoding/xml.lo \
+       exp/cookiejar.lo \
        exp/ebnf.lo \
        exp/html.lo \
        exp/html/atom.lo \
@@ -1836,6 +1846,7 @@ libgo_go_objs = \
        go/ast.lo \
        go/build.lo \
        go/doc.lo \
+       go/format.lo \
        go/parser.lo \
        go/printer.lo \
        go/scanner.lo \
@@ -2658,6 +2669,15 @@ encoding/xml/check: $(CHECK_DEPS)
        @$(CHECK)
 .PHONY: encoding/xml/check
 
+@go_include@ exp/cookiejar.lo.dep
+exp/cookiejar.lo.dep: $(go_exp_cookiejar_files)
+       $(BUILDDEPS)
+exp/cookiejar.lo: $(go_exp_cookiejar_files)
+       $(BUILDPACKAGE)
+exp/cookiejar/check: $(CHECK_DEPS)
+       @$(CHECK)
+.PHONY: exp/cookiejar/check
+
 @go_include@ exp/ebnf.lo.dep
 exp/ebnf.lo.dep: $(go_exp_ebnf_files)
        $(BUILDDEPS)
@@ -2802,6 +2822,15 @@ go/doc/check: $(CHECK_DEPS)
        @$(CHECK)
 .PHONY: go/doc/check
 
+@go_include@ go/format.lo.dep
+go/format.lo.dep: $(go_go_format_files)
+       $(BUILDDEPS)
+go/format.lo: $(go_go_format_files)
+       $(BUILDPACKAGE)
+go/format/check: $(CHECK_DEPS)
+       @$(CHECK)
+.PHONY: go/format/check
+
 @go_include@ go/parser.lo.dep
 go/parser.lo.dep: $(go_go_parser_files)
        $(BUILDDEPS)
@@ -3450,6 +3479,8 @@ encoding/pem.gox: encoding/pem.lo
 encoding/xml.gox: encoding/xml.lo
        $(BUILDGOX)
 
+exp/cookiejar.gox: exp/cookiejar.lo
+       $(BUILDGOX)
 exp/ebnf.gox: exp/ebnf.lo
        $(BUILDGOX)
 exp/html.gox: exp/html.lo
@@ -3482,6 +3513,8 @@ go/build.gox: go/build.lo
        $(BUILDGOX)
 go/doc.gox: go/doc.lo
        $(BUILDGOX)
+go/format.gox: go/format.lo
+       $(BUILDGOX)
 go/parser.gox: go/parser.lo
        $(BUILDGOX)
 go/printer.gox: go/printer.lo
@@ -3681,6 +3714,7 @@ TEST_PACKAGES = \
        encoding/json/check \
        encoding/pem/check \
        encoding/xml/check \
+       exp/cookiejar/check \
        exp/ebnf/check \
        exp/html/check \
        exp/html/atom/check \
@@ -3696,6 +3730,7 @@ TEST_PACKAGES = \
        go/ast/check \
        $(go_build_check_omitted_since_it_calls_6g) \
        go/doc/check \
+       go/format/check \
        go/parser/check \
        go/printer/check \
        go/scanner/check \
index 38b8ddfcc4da13b08b1a65a765d6789fc375a0df..9b8ea1a57e29d6267da158c75505f71aebac0b1d 100644 (file)
@@ -153,26 +153,27 @@ am__DEPENDENCIES_2 = bufio.lo bytes.lo bytes/index.lo crypto.lo \
        debug/pe.lo encoding/ascii85.lo encoding/asn1.lo \
        encoding/base32.lo encoding/base64.lo encoding/binary.lo \
        encoding/csv.lo encoding/gob.lo encoding/hex.lo \
-       encoding/json.lo encoding/pem.lo encoding/xml.lo exp/ebnf.lo \
-       exp/html.lo exp/html/atom.lo exp/locale/collate.lo \
-       exp/locale/collate/build.lo exp/norm.lo exp/proxy.lo \
-       exp/terminal.lo exp/types.lo exp/utf8string.lo \
-       html/template.lo go/ast.lo go/build.lo go/doc.lo go/parser.lo \
-       go/printer.lo go/scanner.lo go/token.lo hash/adler32.lo \
-       hash/crc32.lo hash/crc64.lo hash/fnv.lo net/http/cgi.lo \
-       net/http/fcgi.lo net/http/httptest.lo net/http/httputil.lo \
-       net/http/pprof.lo image/color.lo image/draw.lo image/gif.lo \
-       image/jpeg.lo image/png.lo index/suffixarray.lo io/ioutil.lo \
-       log/syslog.lo log/syslog/syslog_c.lo math/big.lo math/cmplx.lo \
-       math/rand.lo mime/multipart.lo net/http.lo net/mail.lo \
-       net/rpc.lo net/smtp.lo net/textproto.lo net/url.lo \
-       old/netchan.lo old/regexp.lo old/template.lo os/exec.lo \
-       $(am__DEPENDENCIES_1) os/signal.lo os/user.lo path/filepath.lo \
-       regexp/syntax.lo net/rpc/jsonrpc.lo runtime/debug.lo \
-       runtime/pprof.lo sync/atomic.lo sync/atomic_c.lo \
-       text/scanner.lo text/tabwriter.lo text/template.lo \
-       text/template/parse.lo testing/iotest.lo testing/quick.lo \
-       unicode/utf16.lo unicode/utf8.lo
+       encoding/json.lo encoding/pem.lo encoding/xml.lo \
+       exp/cookiejar.lo exp/ebnf.lo exp/html.lo exp/html/atom.lo \
+       exp/locale/collate.lo exp/locale/collate/build.lo exp/norm.lo \
+       exp/proxy.lo exp/terminal.lo exp/types.lo exp/utf8string.lo \
+       html/template.lo go/ast.lo go/build.lo go/doc.lo go/format.lo \
+       go/parser.lo go/printer.lo go/scanner.lo go/token.lo \
+       hash/adler32.lo hash/crc32.lo hash/crc64.lo hash/fnv.lo \
+       net/http/cgi.lo net/http/fcgi.lo net/http/httptest.lo \
+       net/http/httputil.lo net/http/pprof.lo image/color.lo \
+       image/draw.lo image/gif.lo image/jpeg.lo image/png.lo \
+       index/suffixarray.lo io/ioutil.lo log/syslog.lo \
+       log/syslog/syslog_c.lo math/big.lo math/cmplx.lo math/rand.lo \
+       mime/multipart.lo net/http.lo net/mail.lo net/rpc.lo \
+       net/smtp.lo net/textproto.lo net/url.lo old/netchan.lo \
+       old/regexp.lo old/template.lo os/exec.lo $(am__DEPENDENCIES_1) \
+       os/signal.lo os/user.lo path/filepath.lo regexp/syntax.lo \
+       net/rpc/jsonrpc.lo runtime/debug.lo runtime/pprof.lo \
+       sync/atomic.lo sync/atomic_c.lo text/scanner.lo \
+       text/tabwriter.lo text/template.lo text/template/parse.lo \
+       testing/iotest.lo testing/quick.lo unicode/utf16.lo \
+       unicode/utf8.lo
 libgo_la_DEPENDENCIES = $(am__DEPENDENCIES_2) \
        ../libbacktrace/libbacktrace.la $(am__DEPENDENCIES_1) \
        $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
@@ -608,6 +609,7 @@ toolexeclibgoencoding_DATA = \
 @LIBGO_IS_LINUX_TRUE@exp_inotify_gox = 
 toolexeclibgoexpdir = $(toolexeclibgodir)/exp
 toolexeclibgoexp_DATA = \
+       exp/cookiejar.gox \
        exp/ebnf.gox \
        exp/html.gox \
        $(exp_inotify_gox) \
@@ -634,6 +636,7 @@ toolexeclibgogo_DATA = \
        go/ast.gox \
        go/build.gox \
        go/doc.gox \
+       go/format.gox \
        go/parser.gox \
        go/printer.gox \
        go/scanner.gox \
@@ -1403,6 +1406,10 @@ go_encoding_xml_files = \
        go/encoding/xml/typeinfo.go \
        go/encoding/xml/xml.go
 
+go_exp_cookiejar_files = \
+       go/exp/cookiejar/jar.go \
+       go/exp/cookiejar/storage.go
+
 go_exp_ebnf_files = \
        go/exp/ebnf/ebnf.go \
        go/exp/ebnf/parser.go
@@ -1506,6 +1513,9 @@ go_go_doc_files = \
        go/go/doc/reader.go \
        go/go/doc/synopsis.go
 
+go_go_format_files = \
+       go/go/format/format.go
+
 go_go_parser_files = \
        go/go/parser/interface.go \
        go/go/parser/parser.go
@@ -1614,6 +1624,7 @@ go_math_rand_files = \
 go_mime_multipart_files = \
        go/mime/multipart/formdata.go \
        go/mime/multipart/multipart.go \
+       go/mime/multipart/quotedprintable.go \
        go/mime/multipart/writer.go
 
 go_net_http_files = \
@@ -1695,6 +1706,7 @@ go_os_signal_files = \
 
 go_os_user_files = \
        go/os/user/user.go \
+       go/os/user/lookup.go \
        go/os/user/lookup_unix.go
 
 go_path_filepath_files = \
@@ -1948,6 +1960,7 @@ libgo_go_objs = \
        encoding/json.lo \
        encoding/pem.lo \
        encoding/xml.lo \
+       exp/cookiejar.lo \
        exp/ebnf.lo \
        exp/html.lo \
        exp/html/atom.lo \
@@ -1962,6 +1975,7 @@ libgo_go_objs = \
        go/ast.lo \
        go/build.lo \
        go/doc.lo \
+       go/format.lo \
        go/parser.lo \
        go/printer.lo \
        go/scanner.lo \
@@ -2200,6 +2214,7 @@ TEST_PACKAGES = \
        encoding/json/check \
        encoding/pem/check \
        encoding/xml/check \
+       exp/cookiejar/check \
        exp/ebnf/check \
        exp/html/check \
        exp/html/atom/check \
@@ -2215,6 +2230,7 @@ TEST_PACKAGES = \
        go/ast/check \
        $(go_build_check_omitted_since_it_calls_6g) \
        go/doc/check \
+       go/format/check \
        go/parser/check \
        go/printer/check \
        go/scanner/check \
@@ -5096,6 +5112,15 @@ encoding/xml/check: $(CHECK_DEPS)
        @$(CHECK)
 .PHONY: encoding/xml/check
 
+@go_include@ exp/cookiejar.lo.dep
+exp/cookiejar.lo.dep: $(go_exp_cookiejar_files)
+       $(BUILDDEPS)
+exp/cookiejar.lo: $(go_exp_cookiejar_files)
+       $(BUILDPACKAGE)
+exp/cookiejar/check: $(CHECK_DEPS)
+       @$(CHECK)
+.PHONY: exp/cookiejar/check
+
 @go_include@ exp/ebnf.lo.dep
 exp/ebnf.lo.dep: $(go_exp_ebnf_files)
        $(BUILDDEPS)
@@ -5240,6 +5265,15 @@ go/doc/check: $(CHECK_DEPS)
        @$(CHECK)
 .PHONY: go/doc/check
 
+@go_include@ go/format.lo.dep
+go/format.lo.dep: $(go_go_format_files)
+       $(BUILDDEPS)
+go/format.lo: $(go_go_format_files)
+       $(BUILDPACKAGE)
+go/format/check: $(CHECK_DEPS)
+       @$(CHECK)
+.PHONY: go/format/check
+
 @go_include@ go/parser.lo.dep
 go/parser.lo.dep: $(go_go_parser_files)
        $(BUILDDEPS)
@@ -5880,6 +5914,8 @@ encoding/pem.gox: encoding/pem.lo
 encoding/xml.gox: encoding/xml.lo
        $(BUILDGOX)
 
+exp/cookiejar.gox: exp/cookiejar.lo
+       $(BUILDGOX)
 exp/ebnf.gox: exp/ebnf.lo
        $(BUILDGOX)
 exp/html.gox: exp/html.lo
@@ -5912,6 +5948,8 @@ go/build.gox: go/build.lo
        $(BUILDGOX)
 go/doc.gox: go/doc.lo
        $(BUILDGOX)
+go/format.gox: go/format.lo
+       $(BUILDGOX)
 go/parser.gox: go/parser.lo
        $(BUILDGOX)
 go/printer.gox: go/printer.lo
index cd51585f847d5600baa0f985ab26356fd253f694..4b690192c6813df1f18412afaa7cd9fec1624512 100644 (file)
@@ -64,6 +64,8 @@ func NewReader(rd io.Reader) *Reader {
        return NewReaderSize(rd, defaultBufSize)
 }
 
+var errNegativeRead = errors.New("bufio: reader returned negative count from Read")
+
 // fill reads a new chunk into the buffer.
 func (b *Reader) fill() {
        // Slide existing data to beginning.
@@ -75,6 +77,9 @@ func (b *Reader) fill() {
 
        // Read new data.
        n, e := b.rd.Read(b.buf[b.w:])
+       if n < 0 {
+               panic(errNegativeRead)
+       }
        b.w += n
        if e != nil {
                b.err = e
@@ -282,6 +287,9 @@ func (b *Reader) ReadSlice(delim byte) (line []byte, err error) {
 // of the line. The returned buffer is only valid until the next call to
 // ReadLine. ReadLine either returns a non-nil line or it returns an error,
 // never both.
+//
+// The text returned from ReadLine does not include the line end ("\r\n" or "\n").
+// No indication or error is given if the input ends without a final line end.
 func (b *Reader) ReadLine() (line []byte, isPrefix bool, err error) {
        line, err = b.ReadSlice('\n')
        if err == ErrBufferFull {
index 75d9edf8b1cfa350fa9cc2f84ced096ecd6e58b6..564621150ef116e8ceabd45c81b84e35efd3f506 100644 (file)
@@ -939,6 +939,29 @@ func (w *writeCountingDiscard) Write(p []byte) (int, error) {
        return len(p), nil
 }
 
+type negativeReader int
+
+func (r *negativeReader) Read([]byte) (int, error) { return -1, nil }
+
+func TestNegativeRead(t *testing.T) {
+       // should panic with a description pointing at the reader, not at itself.
+       // (should NOT panic with slice index error, for example.)
+       b := NewReader(new(negativeReader))
+       defer func() {
+               switch err := recover().(type) {
+               case nil:
+                       t.Fatal("read did not panic")
+               case error:
+                       if !strings.Contains(err.Error(), "reader returned negative count from Read") {
+                               t.Fatal("wrong panic: %v", err)
+                       }
+               default:
+                       t.Fatalf("unexpected panic value: %T(%v)", err, err)
+               }
+       }()
+       b.Read(make([]byte, 100))
+}
+
 // An onlyReader only implements io.Reader, no matter what other methods the underlying implementation may have.
 type onlyReader struct {
        r io.Reader
index efb9798ee018b0040672648e360a7941a4e7f23a..3ae930384f6b14201b81199bdeba2c9fe3dd4d79 100644 (file)
@@ -360,16 +360,24 @@ func (b *Buffer) UnreadByte() error {
 // ReadBytes returns err != nil if and only if the returned data does not end in
 // delim.
 func (b *Buffer) ReadBytes(delim byte) (line []byte, err error) {
+       slice, err := b.readSlice(delim)
+       // return a copy of slice. The buffer's backing array may
+       // be overwritten by later calls.
+       line = append(line, slice...)
+       return
+}
+
+// readSlice is like readBytes but returns a reference to internal buffer data.
+func (b *Buffer) readSlice(delim byte) (line []byte, err error) {
        i := IndexByte(b.buf[b.off:], delim)
-       size := i + 1
+       end := b.off + i + 1
        if i < 0 {
-               size = len(b.buf) - b.off
+               end = len(b.buf)
                err = io.EOF
        }
-       line = make([]byte, size)
-       copy(line, b.buf[b.off:])
-       b.off += size
-       return
+       line = b.buf[b.off:end]
+       b.off = end
+       return line, err
 }
 
 // ReadString reads until the first occurrence of delim in the input,
@@ -379,8 +387,8 @@ func (b *Buffer) ReadBytes(delim byte) (line []byte, err error) {
 // ReadString returns err != nil if and only if the returned data does not end
 // in delim.
 func (b *Buffer) ReadString(delim byte) (line string, err error) {
-       bytes, err := b.ReadBytes(delim)
-       return string(bytes), err
+       slice, err := b.readSlice(delim)
+       return string(slice), err
 }
 
 // NewBuffer creates and initializes a new Buffer using buf as its initial
index 92e29146b32d5061d3e2ce4af69c7fb090ef3ba7..c53544a74a06d317757bc9760f989f97d459f5ff 100644 (file)
@@ -375,6 +375,41 @@ func TestReadBytes(t *testing.T) {
        }
 }
 
+func TestReadString(t *testing.T) {
+       for _, test := range readBytesTests {
+               buf := NewBufferString(test.buffer)
+               var err error
+               for _, expected := range test.expected {
+                       var s string
+                       s, err = buf.ReadString(test.delim)
+                       if s != expected {
+                               t.Errorf("expected %q, got %q", expected, s)
+                       }
+                       if err != nil {
+                               break
+                       }
+               }
+               if err != test.err {
+                       t.Errorf("expected error %v, got %v", test.err, err)
+               }
+       }
+}
+
+func BenchmarkReadString(b *testing.B) {
+       const n = 32 << 10
+
+       data := make([]byte, n)
+       data[n-1] = 'x'
+       b.SetBytes(int64(n))
+       for i := 0; i < b.N; i++ {
+               buf := NewBuffer(data)
+               _, err := buf.ReadString('x')
+               if err != nil {
+                       b.Fatal(err)
+               }
+       }
+}
+
 func TestGrow(t *testing.T) {
        x := []byte{'x'}
        y := []byte{'y'}
index b34dfc11bffde916d5b963e4cff3626a2e9ddf32..77511b94555634d1ab934e3bd692802351eebce1 100644 (file)
@@ -125,7 +125,7 @@ func (r *Reader) Seek(offset int64, whence int) (int64, error) {
 func (r *Reader) WriteTo(w io.Writer) (n int64, err error) {
        r.prevRune = -1
        if r.i >= len(r.s) {
-               return 0, io.EOF
+               return 0, nil
        }
        b := r.s[r.i:]
        m, err := w.Write(b)
index 666881886760ea8ca6a6f78d2c9032800a14c2a4..f0a3e26c4a773380824d357d4028ce018b9724e9 100644 (file)
@@ -8,6 +8,7 @@ import (
        . "bytes"
        "fmt"
        "io"
+       "io/ioutil"
        "os"
        "testing"
 )
@@ -88,16 +89,20 @@ func TestReaderAt(t *testing.T) {
 }
 
 func TestReaderWriteTo(t *testing.T) {
-       for i := 3; i < 30; i += 3 {
-               s := data[:len(data)/i]
-               r := NewReader(testBytes[:len(testBytes)/i])
+       for i := 0; i < 30; i += 3 {
+               var l int
+               if i > 0 {
+                       l = len(data) / i
+               }
+               s := data[:l]
+               r := NewReader(testBytes[:l])
                var b Buffer
                n, err := r.WriteTo(&b)
                if expect := int64(len(s)); n != expect {
                        t.Errorf("got %v; want %v", n, expect)
                }
                if err != nil {
-                       t.Errorf("got error = %v; want nil", err)
+                       t.Errorf("for length %d: got error = %v; want nil", l, err)
                }
                if b.String() != s {
                        t.Errorf("got string %q; want %q", b.String(), s)
@@ -107,3 +112,26 @@ func TestReaderWriteTo(t *testing.T) {
                }
        }
 }
+
+// verify that copying from an empty reader always has the same results,
+// regardless of the presence of a WriteTo method.
+func TestReaderCopyNothing(t *testing.T) {
+       type nErr struct {
+               n   int64
+               err error
+       }
+       type justReader struct {
+               io.Reader
+       }
+       type justWriter struct {
+               io.Writer
+       }
+       discard := justWriter{ioutil.Discard} // hide ReadFrom
+
+       var with, withOut nErr
+       with.n, with.err = io.Copy(discard, NewReader(nil))
+       withOut.n, withOut.err = io.Copy(discard, justReader{NewReader(nil)})
+       if with != withOut {
+               t.Errorf("behavior differs: with = %#v; without: %#v", with, withOut)
+       }
+}
index 2050bc835915d1d3dab107af03354637d0fb37ce..70f654a00792a4956e52ba5a5667971120e86466 100644 (file)
@@ -37,15 +37,10 @@ func (pq PriorityQueue) Swap(i, j int) {
 func (pq *PriorityQueue) Push(x interface{}) {
        // Push and Pop use pointer receivers because they modify the slice's length,
        // not just its contents.
-       // To simplify indexing expressions in these methods, we save a copy of the
-       // slice object. We could instead write (*pq)[i].
-       a := *pq
-       n := len(a)
-       a = a[0 : n+1]
+       n := len(*pq)
        item := x.(*Item)
        item.index = n
-       a[n] = item
-       *pq = a
+       *pq = append(*pq, item)
 }
 
 func (pq *PriorityQueue) Pop() interface{} {
index 1d96918d37bec8146a8e94f07568f7974095f760..6d3b3e5b322266bd5bebc90f949c564947ff9fa8 100644 (file)
@@ -74,7 +74,7 @@ func New(n int) *Ring {
        return r
 }
 
-// Link connects ring r with with ring s such that r.Next()
+// Link connects ring r with ring s such that r.Next()
 // becomes s and returns the original value for r.Next().
 // r must not be empty.
 //
index 44f3e66daed42c8b9741511ef7dbbf1f29d2df9b..d8c2be00a26efd0b6472403866d0fa8a272bbc14 100644 (file)
@@ -758,8 +758,28 @@ func (c *Conn) Write(b []byte) (int, error) {
                return 0, alertInternalError
        }
 
+       // SSL 3.0 and TLS 1.0 are susceptible to a chosen-plaintext
+       // attack when using block mode ciphers due to predictable IVs.
+       // This can be prevented by splitting each Application Data
+       // record into two records, effectively randomizing the IV.
+       //
+       // http://www.openssl.org/~bodo/tls-cbc.txt
+       // https://bugzilla.mozilla.org/show_bug.cgi?id=665814
+       // http://www.imperialviolet.org/2012/01/15/beastfollowup.html
+
+       var m int
+       if len(b) > 1 && c.vers <= versionTLS10 {
+               if _, ok := c.out.cipher.(cipher.BlockMode); ok {
+                       n, err := c.writeRecord(recordTypeApplicationData, b[:1])
+                       if err != nil {
+                               return n, c.setError(err)
+                       }
+                       m, b = 1, b[1:]
+               }
+       }
+
        n, err := c.writeRecord(recordTypeApplicationData, b)
-       return n, c.setError(err)
+       return n + m, c.setError(err)
 }
 
 // Read can be made to time out and return a net.Error with Timeout() == true
index 58ee6186a5040698bdc5c5a8e67750500ad28f0e..9673947a409438a46d0eaa7223dc856dbe8c907d 100644 (file)
@@ -245,67 +245,24 @@ var ecdheAESClientScript = [][]byte{
                0x19, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00,
        },
        {
-               0x16, 0x03, 0x01, 0x00, 0x54, 0x02, 0x00, 0x00,
-               0x50, 0x03, 0x01, 0x50, 0x77, 0x31, 0xf7, 0x5b,
-               0xdb, 0x3d, 0x7a, 0x62, 0x76, 0x70, 0x95, 0x33,
-               0x73, 0x71, 0x13, 0xfe, 0xa3, 0xb1, 0xd8, 0xb3,
-               0x4d, 0x0d, 0xdc, 0xfe, 0x58, 0x6e, 0x6a, 0x3a,
-               0xf9, 0xde, 0xdc, 0x20, 0x8e, 0xfa, 0x3d, 0x60,
-               0xd0, 0xda, 0xa4, 0x0e, 0x36, 0xf0, 0xde, 0xb6,
-               0x81, 0xb4, 0x80, 0x5e, 0xf9, 0xd2, 0x4c, 0xec,
-               0xd1, 0x9c, 0x2a, 0x81, 0xc3, 0x36, 0x0b, 0x0f,
-               0x4a, 0x3d, 0xdf, 0x75, 0xc0, 0x13, 0x00, 0x00,
-               0x08, 0x00, 0x0b, 0x00, 0x04, 0x03, 0x00, 0x01,
-               0x02, 0x16, 0x03, 0x01, 0x02, 0x39, 0x0b, 0x00,
-               0x02, 0x35, 0x00, 0x02, 0x32, 0x00, 0x02, 0x2f,
-               0x30, 0x82, 0x02, 0x2b, 0x30, 0x82, 0x01, 0xd5,
-               0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x09, 0x00,
-               0xb1, 0x35, 0x13, 0x65, 0x11, 0x20, 0xc5, 0x92,
-               0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
-               0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30,
-               0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,
-               0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13,
-               0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13,
-               0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74,
-               0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06,
-               0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e,
-               0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57,
-               0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50,
-               0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e,
-               0x17, 0x0d, 0x31, 0x32, 0x30, 0x34, 0x30, 0x36,
-               0x31, 0x37, 0x31, 0x30, 0x31, 0x33, 0x5a, 0x17,
-               0x0d, 0x31, 0x35, 0x30, 0x34, 0x30, 0x36, 0x31,
-               0x37, 0x31, 0x30, 0x31, 0x33, 0x5a, 0x30, 0x45,
-               0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04,
-               0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30,
-               0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a,
-               0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61,
-               0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03,
-               0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74,
-               0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69,
-               0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74,
-               0x79, 0x20, 0x4c, 0x74, 0x64, 0x30, 0x5c, 0x30,
-               0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
-               0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x4b,
-               0x00, 0x30, 0x48, 0x02, 0x41, 0x00, 0x9f, 0xb3,
-               0xc3, 0x84, 0x27, 0x95, 0xff, 0x12, 0x31, 0x52,
-               0x0f, 0x15, 0xef, 0x46, 0x11, 0xc4, 0xad, 0x80,
-               0xe6, 0x36, 0x5b, 0x0f, 0xdd, 0x80, 0xd7, 0x61,
-               0x8d, 0xe0, 0xfc, 0x72, 0x45, 0x09, 0x34, 0xfe,
-               0x55, 0x66, 0x45, 0x43, 0x4c, 0x68, 0x97, 0x6a,
-               0xfe, 0xa8, 0xa0, 0xa5, 0xdf, 0x5f, 0x78, 0xff,
-               0xee, 0xd7, 0x64, 0xb8, 0x3f, 0x04, 0xcb, 0x6f,
-               0xff, 0x2a, 0xfe, 0xfe, 0xb9, 0xed, 0x02, 0x03,
-               0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7, 0x30, 0x81,
-               0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e,
-               0x04, 0x16, 0x04, 0x14, 0x78, 0xa6, 0x97, 0x9a,
-               0x63, 0xb5, 0xc5, 0xa1, 0xa5, 0x33, 0xba, 0x22,
-               0x7c, 0x23, 0x6e, 0x5b, 0x1b, 0x7a, 0xcc, 0x2b,
-               0x30, 0x75, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04,
-               0x6e, 0x30, 0x6c, 0x80, 0x14, 0x78, 0xa6, 0x97,
-               0x9a, 0x63, 0xb5, 0xc5, 0xa1, 0xa5, 0x33, 0xba,
-               0x22, 0x7c, 0x23, 0x6e, 0x5b, 0x1b, 0x7a, 0xcc,
-               0x2b, 0xa1, 0x49, 0xa4, 0x47, 0x30, 0x45, 0x31,
+               0x16, 0x03, 0x01, 0x00, 0x52, 0x02, 0x00, 0x00,
+               0x4e, 0x03, 0x01, 0x50, 0xad, 0x72, 0xb1, 0x14,
+               0x45, 0xce, 0x0a, 0x95, 0xf9, 0x63, 0xef, 0xa8,
+               0xe5, 0x07, 0x34, 0x04, 0xe9, 0x08, 0x0f, 0x38,
+               0xe4, 0x28, 0x27, 0x91, 0x07, 0x03, 0xe2, 0xfe,
+               0xe3, 0x25, 0xf7, 0x20, 0x08, 0x42, 0xa2, 0x01,
+               0x69, 0x53, 0xf0, 0xd9, 0x4c, 0xfa, 0x01, 0xa1,
+               0xce, 0x4b, 0xf8, 0x28, 0x21, 0xad, 0x06, 0xbe,
+               0xe0, 0x1b, 0x3b, 0xf7, 0xec, 0xd2, 0x52, 0xae,
+               0x2a, 0x57, 0xb7, 0xa8, 0xc0, 0x13, 0x00, 0x00,
+               0x06, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x16,
+               0x03, 0x01, 0x02, 0x39, 0x0b, 0x00, 0x02, 0x35,
+               0x00, 0x02, 0x32, 0x00, 0x02, 0x2f, 0x30, 0x82,
+               0x02, 0x2b, 0x30, 0x82, 0x01, 0xd5, 0xa0, 0x03,
+               0x02, 0x01, 0x02, 0x02, 0x09, 0x00, 0xb1, 0x35,
+               0x13, 0x65, 0x11, 0x20, 0xc5, 0x92, 0x30, 0x0d,
+               0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+               0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x45, 0x31,
                0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
                0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11,
                0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53,
@@ -314,39 +271,82 @@ var ecdheAESClientScript = [][]byte{
                0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65,
                0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64,
                0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79,
-               0x20, 0x4c, 0x74, 0x64, 0x82, 0x09, 0x00, 0xb1,
-               0x35, 0x13, 0x65, 0x11, 0x20, 0xc5, 0x92, 0x30,
-               0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05,
-               0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0d, 0x06,
+               0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d,
+               0x31, 0x32, 0x30, 0x34, 0x30, 0x36, 0x31, 0x37,
+               0x31, 0x30, 0x31, 0x33, 0x5a, 0x17, 0x0d, 0x31,
+               0x35, 0x30, 0x34, 0x30, 0x36, 0x31, 0x37, 0x31,
+               0x30, 0x31, 0x33, 0x5a, 0x30, 0x45, 0x31, 0x0b,
+               0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
+               0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06,
+               0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f,
+               0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65,
+               0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04,
+               0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72,
+               0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67,
+               0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20,
+               0x4c, 0x74, 0x64, 0x30, 0x5c, 0x30, 0x0d, 0x06,
                0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
-               0x01, 0x05, 0x05, 0x00, 0x03, 0x41, 0x00, 0x85,
-               0x36, 0x40, 0x73, 0xc1, 0xbb, 0x1a, 0xda, 0xd4,
-               0x59, 0x9f, 0x2d, 0xa2, 0x70, 0x31, 0x46, 0x74,
-               0xec, 0x83, 0x6e, 0xa8, 0xc8, 0x3c, 0x51, 0xaf,
-               0x39, 0xac, 0xec, 0x40, 0xbc, 0xe8, 0x22, 0x46,
-               0x1d, 0x99, 0xd6, 0x46, 0x2a, 0x24, 0xd4, 0x8b,
-               0x05, 0x08, 0x4b, 0xfb, 0x35, 0x11, 0x6e, 0x92,
-               0xbb, 0x77, 0xba, 0xe4, 0x12, 0xbb, 0xf4, 0xc8,
-               0x5e, 0x9c, 0x81, 0xa8, 0x97, 0x60, 0x4c, 0x16,
-               0x03, 0x01, 0x00, 0x8b, 0x0c, 0x00, 0x00, 0x87,
-               0x03, 0x00, 0x17, 0x41, 0x04, 0xec, 0x06, 0x1f,
-               0xa0, 0x5e, 0x29, 0x49, 0x71, 0x8b, 0x04, 0x9f,
-               0x47, 0x87, 0xb1, 0xcb, 0xae, 0x57, 0x8f, 0xd7,
-               0xf6, 0xf8, 0x59, 0x74, 0x64, 0x5d, 0x3a, 0x08,
-               0xaf, 0x20, 0xc6, 0xd9, 0xfc, 0x5e, 0x36, 0x8b,
-               0x62, 0x0e, 0xdb, 0xee, 0xd8, 0xcd, 0xef, 0x25,
-               0x8a, 0x38, 0x88, 0x2d, 0x5c, 0x71, 0x50, 0x22,
-               0xda, 0x3f, 0x94, 0x06, 0xc9, 0x68, 0x5b, 0x78,
-               0x3d, 0x95, 0xca, 0x54, 0x44, 0x00, 0x40, 0x36,
-               0xcf, 0x10, 0x81, 0xb4, 0x32, 0x45, 0x3c, 0xa5,
-               0x2d, 0x3e, 0xb0, 0xf8, 0xf4, 0x51, 0xf5, 0x28,
-               0x09, 0x85, 0x71, 0xa6, 0x79, 0x71, 0x4b, 0x4e,
-               0xda, 0x32, 0x5a, 0xc7, 0xb3, 0x57, 0xfd, 0xe8,
-               0x12, 0xab, 0xd8, 0x29, 0xfb, 0x8b, 0x43, 0x8f,
-               0x7e, 0x27, 0x63, 0x91, 0x84, 0x9c, 0x51, 0x0c,
-               0x26, 0x7e, 0x36, 0x3b, 0x37, 0x8d, 0x8f, 0x9e,
-               0xe2, 0x82, 0x62, 0xbb, 0xe5, 0xdf, 0xfc, 0x16,
-               0x03, 0x01, 0x00, 0x04, 0x0e, 0x00, 0x00, 0x00,
+               0x01, 0x01, 0x05, 0x00, 0x03, 0x4b, 0x00, 0x30,
+               0x48, 0x02, 0x41, 0x00, 0x9f, 0xb3, 0xc3, 0x84,
+               0x27, 0x95, 0xff, 0x12, 0x31, 0x52, 0x0f, 0x15,
+               0xef, 0x46, 0x11, 0xc4, 0xad, 0x80, 0xe6, 0x36,
+               0x5b, 0x0f, 0xdd, 0x80, 0xd7, 0x61, 0x8d, 0xe0,
+               0xfc, 0x72, 0x45, 0x09, 0x34, 0xfe, 0x55, 0x66,
+               0x45, 0x43, 0x4c, 0x68, 0x97, 0x6a, 0xfe, 0xa8,
+               0xa0, 0xa5, 0xdf, 0x5f, 0x78, 0xff, 0xee, 0xd7,
+               0x64, 0xb8, 0x3f, 0x04, 0xcb, 0x6f, 0xff, 0x2a,
+               0xfe, 0xfe, 0xb9, 0xed, 0x02, 0x03, 0x01, 0x00,
+               0x01, 0xa3, 0x81, 0xa7, 0x30, 0x81, 0xa4, 0x30,
+               0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16,
+               0x04, 0x14, 0x78, 0xa6, 0x97, 0x9a, 0x63, 0xb5,
+               0xc5, 0xa1, 0xa5, 0x33, 0xba, 0x22, 0x7c, 0x23,
+               0x6e, 0x5b, 0x1b, 0x7a, 0xcc, 0x2b, 0x30, 0x75,
+               0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x6e, 0x30,
+               0x6c, 0x80, 0x14, 0x78, 0xa6, 0x97, 0x9a, 0x63,
+               0xb5, 0xc5, 0xa1, 0xa5, 0x33, 0xba, 0x22, 0x7c,
+               0x23, 0x6e, 0x5b, 0x1b, 0x7a, 0xcc, 0x2b, 0xa1,
+               0x49, 0xa4, 0x47, 0x30, 0x45, 0x31, 0x0b, 0x30,
+               0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+               0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03,
+               0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d,
+               0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31,
+               0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a,
+               0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e,
+               0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69,
+               0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c,
+               0x74, 0x64, 0x82, 0x09, 0x00, 0xb1, 0x35, 0x13,
+               0x65, 0x11, 0x20, 0xc5, 0x92, 0x30, 0x0c, 0x06,
+               0x03, 0x55, 0x1d, 0x13, 0x04, 0x05, 0x30, 0x03,
+               0x01, 0x01, 0xff, 0x30, 0x0d, 0x06, 0x09, 0x2a,
+               0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05,
+               0x05, 0x00, 0x03, 0x41, 0x00, 0x85, 0x36, 0x40,
+               0x73, 0xc1, 0xbb, 0x1a, 0xda, 0xd4, 0x59, 0x9f,
+               0x2d, 0xa2, 0x70, 0x31, 0x46, 0x74, 0xec, 0x83,
+               0x6e, 0xa8, 0xc8, 0x3c, 0x51, 0xaf, 0x39, 0xac,
+               0xec, 0x40, 0xbc, 0xe8, 0x22, 0x46, 0x1d, 0x99,
+               0xd6, 0x46, 0x2a, 0x24, 0xd4, 0x8b, 0x05, 0x08,
+               0x4b, 0xfb, 0x35, 0x11, 0x6e, 0x92, 0xbb, 0x77,
+               0xba, 0xe4, 0x12, 0xbb, 0xf4, 0xc8, 0x5e, 0x9c,
+               0x81, 0xa8, 0x97, 0x60, 0x4c, 0x16, 0x03, 0x01,
+               0x00, 0x8b, 0x0c, 0x00, 0x00, 0x87, 0x03, 0x00,
+               0x17, 0x41, 0x04, 0x1c, 0x8f, 0x9c, 0x6d, 0xe7,
+               0xab, 0x3e, 0xf8, 0x0a, 0x5d, 0xe1, 0x86, 0xb4,
+               0xe2, 0x8e, 0xb2, 0x1c, 0x3b, 0xd9, 0xb6, 0x08,
+               0x80, 0x58, 0x21, 0xe9, 0x0e, 0xc6, 0x66, 0x67,
+               0x97, 0xcb, 0xb9, 0x92, 0x07, 0x00, 0xc4, 0xe5,
+               0xec, 0x5f, 0xb4, 0xe2, 0x20, 0xa9, 0xc9, 0x62,
+               0xd0, 0x98, 0xd5, 0xe3, 0x53, 0xff, 0xd0, 0x0a,
+               0x6e, 0x29, 0x69, 0x39, 0x2a, 0x4b, 0x5c, 0xd8,
+               0x6c, 0xf5, 0xfe, 0x00, 0x40, 0x35, 0xa7, 0x26,
+               0x2e, 0xc2, 0x48, 0x93, 0x32, 0xf7, 0x7d, 0x0f,
+               0x0d, 0x77, 0x56, 0x9a, 0x85, 0x0c, 0xa6, 0x74,
+               0x06, 0xb8, 0x3d, 0x90, 0x56, 0x12, 0x63, 0xff,
+               0x00, 0x5e, 0x0f, 0xf7, 0x24, 0xf7, 0xdb, 0x48,
+               0x71, 0xe9, 0x2e, 0x03, 0xd3, 0xfa, 0x3a, 0xae,
+               0xa0, 0xc1, 0x77, 0x3c, 0x4c, 0x59, 0xce, 0x33,
+               0x1a, 0xd2, 0x47, 0x83, 0xfa, 0xea, 0xd8, 0x1e,
+               0x06, 0xe7, 0x7d, 0xa0, 0x9b, 0x16, 0x03, 0x01,
+               0x00, 0x04, 0x0e, 0x00, 0x00, 0x00,
        },
        {
                0x16, 0x03, 0x01, 0x00, 0x46, 0x10, 0x00, 0x00,
@@ -359,34 +359,50 @@ var ecdheAESClientScript = [][]byte{
                0xe2, 0x32, 0x42, 0xe9, 0x58, 0xb6, 0xd7, 0x49,
                0xa6, 0xb5, 0x68, 0x1a, 0x41, 0x03, 0x56, 0x6b,
                0xdc, 0x5a, 0x89, 0x14, 0x03, 0x01, 0x00, 0x01,
-               0x01, 0x16, 0x03, 0x01, 0x00, 0x30, 0x9a, 0xaa,
-               0xca, 0x5b, 0x57, 0xae, 0x34, 0x92, 0x80, 0x45,
-               0x7f, 0xe6, 0xf9, 0x09, 0x19, 0xd0, 0xf0, 0x1e,
-               0x4b, 0xc3, 0xda, 0x71, 0xce, 0x34, 0x33, 0x56,
-               0x9f, 0x20, 0x9f, 0xf9, 0xa8, 0x62, 0x6c, 0x38,
-               0x1b, 0x41, 0xf5, 0x54, 0xf2, 0x79, 0x42, 0x6c,
-               0xb5, 0x0e, 0xe7, 0xe1, 0xbc, 0x54,
+               0x01, 0x16, 0x03, 0x01, 0x00, 0x30, 0xd9, 0xa7,
+               0x80, 0x56, 0x3f, 0xa3, 0x8f, 0x96, 0x72, 0x4e,
+               0x4e, 0x6e, 0x23, 0x41, 0x8f, 0xda, 0x91, 0xb2,
+               0x9e, 0x63, 0x23, 0x82, 0x64, 0xcd, 0x07, 0x24,
+               0xd3, 0x40, 0x20, 0x22, 0x4c, 0xe3, 0xff, 0x38,
+               0xbb, 0x43, 0x9d, 0x57, 0x11, 0xd5, 0x46, 0xa5,
+               0x05, 0x29, 0x92, 0x02, 0xce, 0xdf,
        },
        {
                0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03,
-               0x01, 0x00, 0x30, 0x62, 0x82, 0x41, 0x75, 0x2b,
-               0xee, 0x0f, 0xdc, 0x6c, 0x48, 0x5a, 0x63, 0xd6,
-               0xcb, 0x0a, 0xfd, 0x0a, 0x0e, 0xde, 0x8b, 0x41,
-               0x19, 0x0c, 0x13, 0x6b, 0x12, 0xd1, 0xc2, 0x53,
-               0xeb, 0x1e, 0xf3, 0x7a, 0xbf, 0x23, 0xc5, 0xa6,
-               0x81, 0xa1, 0xdb, 0xab, 0x2f, 0x2c, 0xbc, 0x35,
-               0x96, 0x72, 0x83,
+               0x01, 0x00, 0x90, 0xe7, 0xba, 0x0e, 0xb1, 0xda,
+               0x92, 0xb5, 0x77, 0x56, 0x38, 0xa6, 0x22, 0xc1,
+               0x72, 0xeb, 0x8a, 0x68, 0x09, 0xb6, 0x74, 0xad,
+               0xb3, 0x4a, 0xf2, 0xdd, 0x09, 0x9b, 0xc9, 0x4f,
+               0x84, 0x73, 0x8b, 0xd6, 0x97, 0x50, 0x23, 0x1c,
+               0xa0, 0xc2, 0x0c, 0x25, 0x18, 0xdd, 0x5e, 0x15,
+               0x4d, 0xd9, 0xef, 0x4f, 0x6a, 0x43, 0x61, 0x9c,
+               0x95, 0xde, 0x3c, 0x66, 0xc4, 0xc1, 0x33, 0x56,
+               0xdd, 0x2f, 0x90, 0xaf, 0x68, 0x5c, 0x9c, 0xa4,
+               0x90, 0x6d, 0xbf, 0x51, 0x1d, 0x68, 0xcb, 0x81,
+               0x77, 0x52, 0xa0, 0x93, 0x2a, 0xf8, 0xc7, 0x61,
+               0x87, 0x76, 0xca, 0x93, 0x9e, 0xd6, 0xee, 0x6f,
+               0x3f, 0xeb, 0x7d, 0x06, 0xdd, 0x73, 0x4e, 0x27,
+               0x16, 0x63, 0x92, 0xe4, 0xb2, 0x3f, 0x91, 0x23,
+               0x21, 0x97, 0x90, 0xce, 0x53, 0xb8, 0xb0, 0x9d,
+               0xbd, 0xbd, 0x33, 0x84, 0xad, 0x6b, 0x2e, 0x7b,
+               0xf5, 0xeb, 0x1d, 0x64, 0x37, 0x2e, 0x29, 0x4e,
+               0xb0, 0x93, 0xdb, 0x92, 0xc7, 0xaa, 0x94, 0xa5,
+               0x3b, 0x64, 0xd0,
        },
        {
-               0x17, 0x03, 0x01, 0x00, 0x20, 0xaf, 0x5d, 0x35,
-               0x57, 0x10, 0x60, 0xb3, 0x25, 0x7c, 0x26, 0x0f,
-               0xf3, 0x5e, 0xb3, 0x0d, 0xad, 0x14, 0x53, 0xcc,
-               0x0c, 0x08, 0xd9, 0xa2, 0x67, 0xab, 0xf4, 0x03,
-               0x17, 0x20, 0xf1, 0x7e, 0xca, 0x15, 0x03, 0x01,
-               0x00, 0x20, 0x30, 0xd0, 0xc1, 0xfb, 0x5f, 0xa6,
-               0x1b, 0xb4, 0x48, 0xc2, 0x0b, 0x98, 0xa8, 0x88,
-               0x7a, 0xba, 0xdf, 0x36, 0x06, 0xd8, 0xcc, 0xe9,
-               0x34, 0xdd, 0x64, 0xc8, 0x73, 0xc5, 0xa2, 0x34,
-               0x64, 0xb7,
+               0x17, 0x03, 0x01, 0x00, 0x20, 0x11, 0xd8, 0x6b,
+               0x3c, 0xf6, 0xbe, 0xf4, 0x54, 0x87, 0xec, 0x75,
+               0x0c, 0x44, 0xdb, 0x92, 0xfc, 0xde, 0x7e, 0x0f,
+               0x9f, 0x87, 0x87, 0x9c, 0x03, 0xd5, 0x07, 0x84,
+               0xe0, 0x3a, 0xf8, 0xae, 0x14, 0x17, 0x03, 0x01,
+               0x00, 0x20, 0xba, 0x54, 0xef, 0x5b, 0xce, 0xfd,
+               0x47, 0x76, 0x6d, 0xa1, 0x8b, 0xfd, 0x48, 0xde,
+               0x6e, 0x26, 0xc1, 0x0c, 0x9d, 0x54, 0xbf, 0x98,
+               0xf6, 0x1c, 0x80, 0xb9, 0xca, 0x93, 0x81, 0x0a,
+               0x2e, 0x06, 0x15, 0x03, 0x01, 0x00, 0x20, 0x93,
+               0x3e, 0x38, 0x17, 0xc9, 0x0a, 0xc3, 0xea, 0xd3,
+               0x92, 0x75, 0xa6, 0x53, 0x37, 0x4d, 0x74, 0x94,
+               0xbe, 0x01, 0xdc, 0x5c, 0x5a, 0x0f, 0x09, 0xf6,
+               0x57, 0x33, 0xc3, 0xbc, 0x3f, 0x7a, 0x4d,
        },
 }
index 2546f16e3cd20b82063e6b6c2e5a2f5677e748c2..6d2e28b4023c1078c0508820929f3643b9d1715b 100644 (file)
@@ -550,33 +550,99 @@ var rc4ServerScript = [][]byte{
 
 var des3ServerScript = [][]byte{
        {
-               0x16, 0x03, 0x01, 0x00, 0x54, 0x01, 0x00, 0x00,
-               0x50, 0x03, 0x01, 0x50, 0x77, 0x3d, 0xe3, 0x8e,
-               0x48, 0xe6, 0xbd, 0x6d, 0x72, 0x8a, 0x1a, 0x11,
-               0xb0, 0x8a, 0x7e, 0xff, 0x29, 0x07, 0xa8, 0x91,
-               0xbc, 0xea, 0x1e, 0x3e, 0x62, 0xc9, 0x8e, 0x72,
-               0x26, 0xd3, 0xca, 0x00, 0x00, 0x28, 0x00, 0x39,
-               0x00, 0x38, 0x00, 0x35, 0x00, 0x16, 0x00, 0x13,
-               0x00, 0x0a, 0x00, 0x33, 0x00, 0x32, 0x00, 0x2f,
-               0x00, 0x05, 0x00, 0x04, 0x00, 0x15, 0x00, 0x12,
-               0x00, 0x09, 0x00, 0x14, 0x00, 0x11, 0x00, 0x08,
-               0x00, 0x06, 0x00, 0x03, 0x00, 0xff, 0x02, 0x01,
-               0x00,
+               0x16, 0x03, 0x00, 0x00, 0xc5, 0x01, 0x00, 0x00,
+               0xc1, 0x03, 0x03, 0x50, 0xae, 0x5d, 0x38, 0xec,
+               0xaa, 0x2f, 0x41, 0xf9, 0xd2, 0x7b, 0xa1, 0xfd,
+               0x0f, 0xff, 0x4e, 0x54, 0x0e, 0x15, 0x57, 0xaf,
+               0x2c, 0x91, 0xb5, 0x35, 0x5b, 0x2e, 0xb0, 0xec,
+               0x20, 0xe5, 0xd2, 0x00, 0x00, 0x50, 0xc0, 0x09,
+               0xc0, 0x23, 0xc0, 0x2b, 0xc0, 0x0a, 0xc0, 0x24,
+               0xc0, 0x2c, 0xc0, 0x08, 0xc0, 0x13, 0xc0, 0x27,
+               0xc0, 0x2f, 0xc0, 0x14, 0xc0, 0x30, 0xc0, 0x12,
+               0x00, 0x33, 0x00, 0x67, 0x00, 0x45, 0x00, 0x9e,
+               0x00, 0x39, 0x00, 0x6b, 0x00, 0x88, 0x00, 0x16,
+               0x00, 0x32, 0x00, 0x40, 0x00, 0x44, 0x00, 0xa2,
+               0x00, 0x38, 0x00, 0x6a, 0x00, 0x87, 0x00, 0x13,
+               0x00, 0x66, 0x00, 0x2f, 0x00, 0x3c, 0x00, 0x41,
+               0x00, 0x9c, 0x00, 0x35, 0x00, 0x3d, 0x00, 0x84,
+               0x00, 0x0a, 0x00, 0x05, 0x00, 0x04, 0x01, 0x00,
+               0x00, 0x48, 0x00, 0x05, 0x00, 0x05, 0x01, 0x00,
+               0x00, 0x00, 0x00, 0xff, 0x01, 0x00, 0x01, 0x00,
+               0x00, 0x23, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0c,
+               0x00, 0x0a, 0x00, 0x13, 0x00, 0x15, 0x00, 0x17,
+               0x00, 0x18, 0x00, 0x19, 0x00, 0x0b, 0x00, 0x02,
+               0x01, 0x00, 0x00, 0x0d, 0x00, 0x1c, 0x00, 0x1a,
+               0x04, 0x01, 0x04, 0x02, 0x04, 0x03, 0x05, 0x01,
+               0x05, 0x03, 0x06, 0x01, 0x06, 0x03, 0x03, 0x01,
+               0x03, 0x02, 0x03, 0x03, 0x02, 0x01, 0x02, 0x02,
+               0x02, 0x03,
        },
        {
-               0x16, 0x03, 0x01, 0x00, 0x2a, 0x02, 0x00, 0x00,
-               0x26, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x16, 0x03, 0x01, 0x00, 0x30, 0x02, 0x00, 0x00,
+               0x2c, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-               0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x16,
-               0x03, 0x01, 0x02, 0xbe, 0x0b, 0x00, 0x02, 0xba,
-               0x00, 0x02, 0xb7, 0x00, 0x02, 0xb4, 0x30, 0x82,
-               0x02, 0xb0, 0x30, 0x82, 0x02, 0x19, 0xa0, 0x03,
-               0x02, 0x01, 0x02, 0x02, 0x09, 0x00, 0x85, 0xb0,
-               0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30, 0x0d,
-               0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
-               0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x45, 0x31,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00,
+               0x04, 0x00, 0x23, 0x00, 0x00, 0x16, 0x03, 0x01,
+               0x02, 0xbe, 0x0b, 0x00, 0x02, 0xba, 0x00, 0x02,
+               0xb7, 0x00, 0x02, 0xb4, 0x30, 0x82, 0x02, 0xb0,
+               0x30, 0x82, 0x02, 0x19, 0xa0, 0x03, 0x02, 0x01,
+               0x02, 0x02, 0x09, 0x00, 0x85, 0xb0, 0xbb, 0xa4,
+               0x8a, 0x7f, 0xb8, 0xca, 0x30, 0x0d, 0x06, 0x09,
+               0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+               0x05, 0x05, 0x00, 0x30, 0x45, 0x31, 0x0b, 0x30,
+               0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+               0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03,
+               0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d,
+               0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31,
+               0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a,
+               0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e,
+               0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69,
+               0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c,
+               0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x30,
+               0x30, 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, 0x39,
+               0x33, 0x38, 0x5a, 0x17, 0x0d, 0x31, 0x31, 0x30,
+               0x34, 0x32, 0x34, 0x30, 0x39, 0x30, 0x39, 0x33,
+               0x38, 0x5a, 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09,
+               0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x41,
+               0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55,
+               0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, 0x65,
+               0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, 0x21,
+               0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+               0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65,
+               0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, 0x74,
+               0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74,
+               0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09,
+               0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+               0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30,
+               0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xbb, 0x79,
+               0xd6, 0xf5, 0x17, 0xb5, 0xe5, 0xbf, 0x46, 0x10,
+               0xd0, 0xdc, 0x69, 0xbe, 0xe6, 0x2b, 0x07, 0x43,
+               0x5a, 0xd0, 0x03, 0x2d, 0x8a, 0x7a, 0x43, 0x85,
+               0xb7, 0x14, 0x52, 0xe7, 0xa5, 0x65, 0x4c, 0x2c,
+               0x78, 0xb8, 0x23, 0x8c, 0xb5, 0xb4, 0x82, 0xe5,
+               0xde, 0x1f, 0x95, 0x3b, 0x7e, 0x62, 0xa5, 0x2c,
+               0xa5, 0x33, 0xd6, 0xfe, 0x12, 0x5c, 0x7a, 0x56,
+               0xfc, 0xf5, 0x06, 0xbf, 0xfa, 0x58, 0x7b, 0x26,
+               0x3f, 0xb5, 0xcd, 0x04, 0xd3, 0xd0, 0xc9, 0x21,
+               0x96, 0x4a, 0xc7, 0xf4, 0x54, 0x9f, 0x5a, 0xbf,
+               0xef, 0x42, 0x71, 0x00, 0xfe, 0x18, 0x99, 0x07,
+               0x7f, 0x7e, 0x88, 0x7d, 0x7d, 0xf1, 0x04, 0x39,
+               0xc4, 0xa2, 0x2e, 0xdb, 0x51, 0xc9, 0x7c, 0xe3,
+               0xc0, 0x4c, 0x3b, 0x32, 0x66, 0x01, 0xcf, 0xaf,
+               0xb1, 0x1d, 0xb8, 0x71, 0x9a, 0x1d, 0xdb, 0xdb,
+               0x89, 0x6b, 0xae, 0xda, 0x2d, 0x79, 0x02, 0x03,
+               0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7, 0x30, 0x81,
+               0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e,
+               0x04, 0x16, 0x04, 0x14, 0xb1, 0xad, 0xe2, 0x85,
+               0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, 0xce, 0x23,
+               0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, 0x88, 0x39,
+               0x30, 0x75, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04,
+               0x6e, 0x30, 0x6c, 0x80, 0x14, 0xb1, 0xad, 0xe2,
+               0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, 0xce,
+               0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, 0x88,
+               0x39, 0xa1, 0x49, 0xa4, 0x47, 0x30, 0x45, 0x31,
                0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
                0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11,
                0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53,
@@ -585,163 +651,207 @@ var des3ServerScript = [][]byte{
                0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65,
                0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64,
                0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79,
-               0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d,
-               0x31, 0x30, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39,
-               0x30, 0x39, 0x33, 0x38, 0x5a, 0x17, 0x0d, 0x31,
-               0x31, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, 0x30,
-               0x39, 0x33, 0x38, 0x5a, 0x30, 0x45, 0x31, 0x0b,
-               0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
-               0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06,
-               0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f,
-               0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65,
-               0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04,
-               0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72,
-               0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67,
-               0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20,
-               0x4c, 0x74, 0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d,
-               0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
-               0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d,
-               0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00,
-               0xbb, 0x79, 0xd6, 0xf5, 0x17, 0xb5, 0xe5, 0xbf,
-               0x46, 0x10, 0xd0, 0xdc, 0x69, 0xbe, 0xe6, 0x2b,
-               0x07, 0x43, 0x5a, 0xd0, 0x03, 0x2d, 0x8a, 0x7a,
-               0x43, 0x85, 0xb7, 0x14, 0x52, 0xe7, 0xa5, 0x65,
-               0x4c, 0x2c, 0x78, 0xb8, 0x23, 0x8c, 0xb5, 0xb4,
-               0x82, 0xe5, 0xde, 0x1f, 0x95, 0x3b, 0x7e, 0x62,
-               0xa5, 0x2c, 0xa5, 0x33, 0xd6, 0xfe, 0x12, 0x5c,
-               0x7a, 0x56, 0xfc, 0xf5, 0x06, 0xbf, 0xfa, 0x58,
-               0x7b, 0x26, 0x3f, 0xb5, 0xcd, 0x04, 0xd3, 0xd0,
-               0xc9, 0x21, 0x96, 0x4a, 0xc7, 0xf4, 0x54, 0x9f,
-               0x5a, 0xbf, 0xef, 0x42, 0x71, 0x00, 0xfe, 0x18,
-               0x99, 0x07, 0x7f, 0x7e, 0x88, 0x7d, 0x7d, 0xf1,
-               0x04, 0x39, 0xc4, 0xa2, 0x2e, 0xdb, 0x51, 0xc9,
-               0x7c, 0xe3, 0xc0, 0x4c, 0x3b, 0x32, 0x66, 0x01,
-               0xcf, 0xaf, 0xb1, 0x1d, 0xb8, 0x71, 0x9a, 0x1d,
-               0xdb, 0xdb, 0x89, 0x6b, 0xae, 0xda, 0x2d, 0x79,
-               0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7,
-               0x30, 0x81, 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55,
-               0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xb1, 0xad,
-               0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69,
-               0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18,
-               0x88, 0x39, 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d,
-               0x23, 0x04, 0x6e, 0x30, 0x6c, 0x80, 0x14, 0xb1,
-               0xad, 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb,
-               0x69, 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e,
-               0x18, 0x88, 0x39, 0xa1, 0x49, 0xa4, 0x47, 0x30,
-               0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,
-               0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13,
-               0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13,
-               0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74,
-               0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06,
-               0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e,
-               0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57,
-               0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50,
-               0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09,
-               0x00, 0x85, 0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8,
-               0xca, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13,
-               0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30,
-               0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
-               0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81,
-               0x81, 0x00, 0x08, 0x6c, 0x45, 0x24, 0xc7, 0x6b,
-               0xb1, 0x59, 0xab, 0x0c, 0x52, 0xcc, 0xf2, 0xb0,
-               0x14, 0xd7, 0x87, 0x9d, 0x7a, 0x64, 0x75, 0xb5,
-               0x5a, 0x95, 0x66, 0xe4, 0xc5, 0x2b, 0x8e, 0xae,
-               0x12, 0x66, 0x1f, 0xeb, 0x4f, 0x38, 0xb3, 0x6e,
-               0x60, 0xd3, 0x92, 0xfd, 0xf7, 0x41, 0x08, 0xb5,
-               0x25, 0x13, 0xb1, 0x18, 0x7a, 0x24, 0xfb, 0x30,
-               0x1d, 0xba, 0xed, 0x98, 0xb9, 0x17, 0xec, 0xe7,
-               0xd7, 0x31, 0x59, 0xdb, 0x95, 0xd3, 0x1d, 0x78,
-               0xea, 0x50, 0x56, 0x5c, 0xd5, 0x82, 0x5a, 0x2d,
-               0x5a, 0x5f, 0x33, 0xc4, 0xb6, 0xd8, 0xc9, 0x75,
-               0x90, 0x96, 0x8c, 0x0f, 0x52, 0x98, 0xb5, 0xcd,
-               0x98, 0x1f, 0x89, 0x20, 0x5f, 0xf2, 0xa0, 0x1c,
-               0xa3, 0x1b, 0x96, 0x94, 0xdd, 0xa9, 0xfd, 0x57,
-               0xe9, 0x70, 0xe8, 0x26, 0x6d, 0x71, 0x99, 0x9b,
-               0x26, 0x6e, 0x38, 0x50, 0x29, 0x6c, 0x90, 0xa7,
-               0xbd, 0xd9, 0x16, 0x03, 0x01, 0x00, 0x04, 0x0e,
-               0x00, 0x00, 0x00,
+               0x20, 0x4c, 0x74, 0x64, 0x82, 0x09, 0x00, 0x85,
+               0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30,
+               0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05,
+               0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0d, 0x06,
+               0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+               0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00,
+               0x08, 0x6c, 0x45, 0x24, 0xc7, 0x6b, 0xb1, 0x59,
+               0xab, 0x0c, 0x52, 0xcc, 0xf2, 0xb0, 0x14, 0xd7,
+               0x87, 0x9d, 0x7a, 0x64, 0x75, 0xb5, 0x5a, 0x95,
+               0x66, 0xe4, 0xc5, 0x2b, 0x8e, 0xae, 0x12, 0x66,
+               0x1f, 0xeb, 0x4f, 0x38, 0xb3, 0x6e, 0x60, 0xd3,
+               0x92, 0xfd, 0xf7, 0x41, 0x08, 0xb5, 0x25, 0x13,
+               0xb1, 0x18, 0x7a, 0x24, 0xfb, 0x30, 0x1d, 0xba,
+               0xed, 0x98, 0xb9, 0x17, 0xec, 0xe7, 0xd7, 0x31,
+               0x59, 0xdb, 0x95, 0xd3, 0x1d, 0x78, 0xea, 0x50,
+               0x56, 0x5c, 0xd5, 0x82, 0x5a, 0x2d, 0x5a, 0x5f,
+               0x33, 0xc4, 0xb6, 0xd8, 0xc9, 0x75, 0x90, 0x96,
+               0x8c, 0x0f, 0x52, 0x98, 0xb5, 0xcd, 0x98, 0x1f,
+               0x89, 0x20, 0x5f, 0xf2, 0xa0, 0x1c, 0xa3, 0x1b,
+               0x96, 0x94, 0xdd, 0xa9, 0xfd, 0x57, 0xe9, 0x70,
+               0xe8, 0x26, 0x6d, 0x71, 0x99, 0x9b, 0x26, 0x6e,
+               0x38, 0x50, 0x29, 0x6c, 0x90, 0xa7, 0xbd, 0xd9,
+               0x16, 0x03, 0x01, 0x00, 0x04, 0x0e, 0x00, 0x00,
+               0x00,
        },
        {
                0x16, 0x03, 0x01, 0x00, 0x86, 0x10, 0x00, 0x00,
-               0x82, 0x00, 0x80, 0x33, 0x52, 0xe2, 0xd6, 0x79,
-               0xf8, 0xe6, 0xd4, 0xe2, 0x08, 0xb0, 0x73, 0x36,
-               0xa7, 0x61, 0x72, 0x19, 0xfb, 0xd1, 0x1f, 0xf5,
-               0xbc, 0x7c, 0x84, 0xdd, 0xed, 0x99, 0xd7, 0x5e,
-               0x3d, 0x11, 0xc3, 0x19, 0xb0, 0x7f, 0x10, 0x94,
-               0x72, 0x64, 0xf3, 0x2c, 0x3f, 0x8d, 0x73, 0x39,
-               0x9e, 0xca, 0x2e, 0x09, 0xbd, 0xb7, 0x8d, 0x4c,
-               0x5b, 0x58, 0xff, 0x4f, 0x53, 0xa9, 0xd4, 0x7c,
-               0x34, 0xe0, 0xaa, 0xa8, 0x14, 0xc0, 0x14, 0x25,
-               0x0b, 0xaa, 0x55, 0xab, 0x10, 0x34, 0x45, 0x72,
-               0xe8, 0x26, 0x6f, 0xf5, 0xbb, 0x3a, 0xfa, 0xd8,
-               0x4f, 0x70, 0xe1, 0xc1, 0xb6, 0x11, 0x1e, 0xd1,
-               0xe0, 0x0b, 0xa1, 0x3a, 0xdc, 0x94, 0x89, 0x7f,
-               0x88, 0x5e, 0x5a, 0xf1, 0x0c, 0x98, 0xe2, 0xab,
-               0x0e, 0x3a, 0xa8, 0x2f, 0xbb, 0xc5, 0x02, 0x07,
-               0x15, 0x5e, 0x46, 0x82, 0x54, 0x9c, 0x09, 0xea,
-               0xb9, 0x56, 0xf7, 0x14, 0x03, 0x01, 0x00, 0x01,
-               0x01, 0x16, 0x03, 0x01, 0x00, 0x28, 0xb9, 0xbf,
-               0x9a, 0xb8, 0xe4, 0x14, 0x6b, 0xc6, 0xf0, 0x27,
-               0xb7, 0xdb, 0xb2, 0xbc, 0x16, 0xd1, 0x3c, 0x0b,
-               0xc1, 0xe6, 0x1c, 0xa1, 0x29, 0xc7, 0x37, 0xe6,
-               0x56, 0x1d, 0x16, 0xb5, 0xa8, 0x0d, 0x4d, 0xdb,
-               0x9d, 0xf8, 0xb2, 0x6a, 0x90, 0x96,
+               0x82, 0x00, 0x80, 0x51, 0x04, 0xf1, 0x7a, 0xbf,
+               0xe8, 0xa5, 0x86, 0x09, 0xa7, 0xf3, 0xcc, 0x93,
+               0x00, 0x10, 0x5b, 0xb8, 0xc1, 0x51, 0x0d, 0x5b,
+               0xcd, 0xed, 0x26, 0x01, 0x69, 0x73, 0xf4, 0x05,
+               0x8a, 0x6a, 0xc3, 0xb1, 0x9e, 0x84, 0x4e, 0x39,
+               0xcf, 0x5e, 0x55, 0xa9, 0x70, 0x19, 0x96, 0x91,
+               0xcd, 0x2c, 0x78, 0x3c, 0xa2, 0x6d, 0xb0, 0x49,
+               0x86, 0xf6, 0xd1, 0x3a, 0xde, 0x00, 0x4b, 0xa6,
+               0x25, 0xbf, 0x85, 0x39, 0xce, 0xb1, 0xcf, 0xbc,
+               0x16, 0xc7, 0x66, 0xac, 0xf8, 0xd2, 0x3b, 0xd1,
+               0xcc, 0x16, 0xac, 0x63, 0x3c, 0xbe, 0xd9, 0xb6,
+               0x6a, 0xe4, 0x13, 0x8a, 0xf4, 0x56, 0x2f, 0x92,
+               0x54, 0xd8, 0xf0, 0x84, 0x01, 0x32, 0x1a, 0xa9,
+               0x2d, 0xaf, 0x82, 0x0e, 0x00, 0xfa, 0x07, 0x88,
+               0xd9, 0x87, 0xe7, 0xdc, 0x9e, 0xe9, 0x72, 0x49,
+               0xb8, 0xfa, 0x8c, 0x7b, 0x07, 0x0b, 0x03, 0x7c,
+               0x10, 0x8c, 0x8a, 0x14, 0x03, 0x01, 0x00, 0x01,
+               0x01, 0x16, 0x03, 0x01, 0x00, 0xa8, 0x61, 0xa4,
+               0xf4, 0x5f, 0x8a, 0x1f, 0x5c, 0x92, 0x3f, 0x8c,
+               0xdb, 0xd6, 0x10, 0xcd, 0x9e, 0xe7, 0xf0, 0xc4,
+               0x3c, 0xb6, 0x1c, 0x9a, 0x56, 0x73, 0x7f, 0xa6,
+               0x14, 0x24, 0xcb, 0x96, 0x1f, 0xe0, 0xaf, 0xcd,
+               0x3c, 0x66, 0x43, 0xb7, 0x37, 0x65, 0x34, 0x47,
+               0xf8, 0x43, 0xf1, 0xcc, 0x15, 0xb8, 0xdc, 0x35,
+               0xe0, 0xa4, 0x2d, 0x78, 0x94, 0xe0, 0x02, 0xf3,
+               0x76, 0x46, 0xf7, 0x9b, 0x8d, 0x0d, 0x5d, 0x0b,
+               0xd3, 0xdd, 0x9a, 0x9e, 0x62, 0x2e, 0xc5, 0x98,
+               0x75, 0x63, 0x0c, 0xbf, 0x8e, 0x49, 0x33, 0x23,
+               0x7c, 0x00, 0xcf, 0xfb, 0xcf, 0xba, 0x0f, 0x41,
+               0x39, 0x89, 0xb9, 0xcc, 0x59, 0xd0, 0x2b, 0xb6,
+               0xec, 0x04, 0xe2, 0xc0, 0x52, 0xc7, 0xcf, 0x71,
+               0x47, 0xff, 0x70, 0x7e, 0xa9, 0xbd, 0x1c, 0xdd,
+               0x17, 0xa5, 0x6c, 0xb7, 0x10, 0x4f, 0x42, 0x18,
+               0x37, 0x69, 0xa9, 0xd2, 0xb3, 0x18, 0x84, 0x92,
+               0xa7, 0x47, 0x21, 0xf6, 0x95, 0x63, 0x29, 0xd6,
+               0xa5, 0xb6, 0xda, 0x65, 0x67, 0x69, 0xc4, 0x26,
+               0xac, 0x8b, 0x08, 0x58, 0xdd, 0x3c, 0x31, 0x20,
+               0xd5, 0x0c, 0x88, 0x72, 0x18, 0x16, 0x88, 0x1e,
+               0x4a, 0x0f, 0xe1, 0xcf, 0x95, 0x24,
        },
        {
-               0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03,
-               0x01, 0x00, 0x28, 0x5d, 0xc9, 0xad, 0xcf, 0xf8,
-               0x37, 0x05, 0xec, 0x5e, 0xb2, 0x77, 0xb3, 0x1a,
-               0x91, 0x75, 0x1d, 0x8d, 0xdd, 0x1a, 0xff, 0xb6,
-               0xca, 0xf7, 0x59, 0x04, 0xb2, 0x11, 0x0a, 0x25,
-               0x7e, 0xc5, 0x7d, 0xba, 0x8a, 0x50, 0xcc, 0xe9,
-               0x89, 0xa0, 0x91, 0x17, 0x03, 0x01, 0x00, 0x28,
-               0x30, 0x68, 0x28, 0x1e, 0x75, 0x82, 0x04, 0xe7,
-               0xd3, 0x3b, 0xb1, 0x17, 0x32, 0x10, 0x7f, 0xae,
-               0x77, 0xeb, 0xf1, 0x46, 0xcc, 0xe5, 0xe0, 0xbe,
-               0x07, 0x37, 0x0d, 0x84, 0x54, 0xa1, 0x88, 0xac,
-               0xe5, 0x06, 0x7b, 0xee, 0xe6, 0xa1, 0xee, 0xb0,
-               0x15, 0x03, 0x01, 0x00, 0x18, 0x73, 0xa9, 0xf8,
-               0x5a, 0xd4, 0xfc, 0xd9, 0xa9, 0x82, 0x97, 0x50,
-               0x14, 0x76, 0x6c, 0x27, 0x9f, 0xa2, 0xf1, 0x52,
-               0xa0, 0xe3, 0xbd, 0xcb, 0xd3,
+               0x16, 0x03, 0x01, 0x00, 0x72, 0x04, 0x00, 0x00,
+               0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65,
+               0xe8, 0x4b, 0xde, 0xef, 0xba, 0x3e, 0x18, 0x1c,
+               0x1e, 0x5e, 0xbc, 0x87, 0xf1, 0x87, 0x8d, 0x72,
+               0xe3, 0xbe, 0x0f, 0xdf, 0xfd, 0xd0, 0xb2, 0x89,
+               0xf8, 0x05, 0x9a, 0x52, 0x47, 0x77, 0x9e, 0xe8,
+               0xb1, 0x1d, 0x18, 0xed, 0x6a, 0x4b, 0x63, 0x1d,
+               0xf1, 0x62, 0xd2, 0x65, 0x21, 0x26, 0x73, 0xd4,
+               0x35, 0x5b, 0x95, 0x89, 0x12, 0x59, 0x23, 0x8c,
+               0xc3, 0xfc, 0xf9, 0x4d, 0x21, 0x79, 0xa0, 0xbd,
+               0xff, 0x33, 0xa2, 0x3d, 0x0b, 0x6f, 0x89, 0xc9,
+               0x23, 0xe4, 0xe7, 0x9f, 0x1d, 0x98, 0xf6, 0xed,
+               0x02, 0x8d, 0xac, 0x1a, 0xf9, 0xcb, 0xa5, 0x14,
+               0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, 0x01,
+               0x00, 0x28, 0x91, 0x56, 0x80, 0xe2, 0x6d, 0x51,
+               0x88, 0x03, 0xf8, 0x49, 0xe6, 0x6a, 0x5a, 0xfb,
+               0x2f, 0x0b, 0xb5, 0xa1, 0x0d, 0x63, 0x83, 0xae,
+               0xb9, 0xbc, 0x05, 0xf0, 0x81, 0x00, 0x61, 0x83,
+               0x38, 0xda, 0x14, 0xf6, 0xea, 0xd8, 0x78, 0x65,
+               0xc7, 0x26, 0x17, 0x03, 0x01, 0x00, 0x18, 0x81,
+               0x30, 0x8b, 0x22, 0x5a, 0xd3, 0x7f, 0xc8, 0xf2,
+               0x8a, 0x6b, 0xa3, 0xba, 0x4d, 0xe7, 0x6e, 0xd2,
+               0xfd, 0xbf, 0xf2, 0xc5, 0x28, 0xa0, 0x62, 0x17,
+               0x03, 0x01, 0x00, 0x28, 0x17, 0x83, 0x3c, 0x78,
+               0x18, 0xfa, 0x8d, 0x58, 0x5c, 0xaa, 0x05, 0x7d,
+               0x67, 0x96, 0x11, 0x60, 0x11, 0xc0, 0x1e, 0x0d,
+               0x6a, 0x6e, 0x5f, 0x1d, 0x98, 0x4b, 0xff, 0x82,
+               0xee, 0x21, 0x06, 0x29, 0xd3, 0x8b, 0x80, 0x78,
+               0x39, 0x05, 0x34, 0x9b, 0x15, 0x03, 0x01, 0x00,
+               0x18, 0xa9, 0x38, 0x18, 0x4f, 0x9d, 0x84, 0x75,
+               0x88, 0x53, 0xd6, 0x85, 0xc2, 0x15, 0x4b, 0xe3,
+               0xe3, 0x35, 0x9a, 0x74, 0xc9, 0x3e, 0x13, 0xc1,
+               0x8c,
        },
 }
 
 var aesServerScript = [][]byte{
        {
-               0x16, 0x03, 0x02, 0x00, 0x7f, 0x01, 0x00, 0x00,
-               0x7b, 0x03, 0x02, 0x4d, 0x08, 0x2d, 0x0b, 0xb3,
-               0x57, 0x85, 0x71, 0x4b, 0xfb, 0x34, 0xab, 0x16,
-               0xd4, 0x92, 0x50, 0x81, 0x16, 0x95, 0x11, 0x28,
-               0x1a, 0xcb, 0xff, 0x09, 0x4d, 0x23, 0xa6, 0xfe,
-               0x2e, 0xbb, 0x78, 0x00, 0x00, 0x34, 0x00, 0x33,
-               0x00, 0x45, 0x00, 0x39, 0x00, 0x88, 0x00, 0x16,
-               0x00, 0x32, 0x00, 0x44, 0x00, 0x38, 0x00, 0x87,
-               0x00, 0x13, 0x00, 0x66, 0x00, 0x90, 0x00, 0x91,
-               0x00, 0x8f, 0x00, 0x8e, 0x00, 0x2f, 0x00, 0x41,
-               0x00, 0x35, 0x00, 0x84, 0x00, 0x0a, 0x00, 0x05,
-               0x00, 0x04, 0x00, 0x8c, 0x00, 0x8d, 0x00, 0x8b,
-               0x00, 0x8a, 0x01, 0x00, 0x00, 0x1e, 0x00, 0x09,
-               0x00, 0x03, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00,
-               0x0e, 0x00, 0x0c, 0x00, 0x00, 0x09, 0x6c, 0x6f,
-               0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74, 0xff,
-               0x01, 0x00, 0x01, 0x00,
+               0x16, 0x03, 0x00, 0x00, 0xc5, 0x01, 0x00, 0x00,
+               0xc1, 0x03, 0x03, 0x50, 0xae, 0x5c, 0xe9, 0x5e,
+               0x31, 0x93, 0x82, 0xa5, 0x6f, 0x51, 0x82, 0xc8,
+               0x55, 0x4f, 0x1f, 0x2e, 0x90, 0x98, 0x81, 0x13,
+               0x27, 0x80, 0x68, 0xb4, 0x2d, 0xba, 0x3a, 0x76,
+               0xd8, 0xd7, 0x2c, 0x00, 0x00, 0x50, 0xc0, 0x09,
+               0xc0, 0x23, 0xc0, 0x2b, 0xc0, 0x0a, 0xc0, 0x24,
+               0xc0, 0x2c, 0xc0, 0x08, 0xc0, 0x13, 0xc0, 0x27,
+               0xc0, 0x2f, 0xc0, 0x14, 0xc0, 0x30, 0xc0, 0x12,
+               0x00, 0x33, 0x00, 0x67, 0x00, 0x45, 0x00, 0x9e,
+               0x00, 0x39, 0x00, 0x6b, 0x00, 0x88, 0x00, 0x16,
+               0x00, 0x32, 0x00, 0x40, 0x00, 0x44, 0x00, 0xa2,
+               0x00, 0x38, 0x00, 0x6a, 0x00, 0x87, 0x00, 0x13,
+               0x00, 0x66, 0x00, 0x2f, 0x00, 0x3c, 0x00, 0x41,
+               0x00, 0x9c, 0x00, 0x35, 0x00, 0x3d, 0x00, 0x84,
+               0x00, 0x0a, 0x00, 0x05, 0x00, 0x04, 0x01, 0x00,
+               0x00, 0x48, 0x00, 0x05, 0x00, 0x05, 0x01, 0x00,
+               0x00, 0x00, 0x00, 0xff, 0x01, 0x00, 0x01, 0x00,
+               0x00, 0x23, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0c,
+               0x00, 0x0a, 0x00, 0x13, 0x00, 0x15, 0x00, 0x17,
+               0x00, 0x18, 0x00, 0x19, 0x00, 0x0b, 0x00, 0x02,
+               0x01, 0x00, 0x00, 0x0d, 0x00, 0x1c, 0x00, 0x1a,
+               0x04, 0x01, 0x04, 0x02, 0x04, 0x03, 0x05, 0x01,
+               0x05, 0x03, 0x06, 0x01, 0x06, 0x03, 0x03, 0x01,
+               0x03, 0x02, 0x03, 0x03, 0x02, 0x01, 0x02, 0x02,
+               0x02, 0x03,
        },
-
        {
-               0x16, 0x03, 0x01, 0x00, 0x2a, 0x02, 0x00, 0x00,
-               0x26, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x16, 0x03, 0x01, 0x00, 0x30, 0x02, 0x00, 0x00,
+               0x2c, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-               0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x16,
-               0x03, 0x01, 0x02, 0xbe, 0x0b, 0x00, 0x02, 0xba,
-               0x00, 0x02, 0xb7, 0x00, 0x02, 0xb4, 0x30, 0x82,
-               0x02, 0xb0, 0x30, 0x82, 0x02, 0x19, 0xa0, 0x03,
-               0x02, 0x01, 0x02, 0x02, 0x09, 0x00, 0x85, 0xb0,
-               0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30, 0x0d,
-               0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
-               0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x45, 0x31,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00,
+               0x04, 0x00, 0x23, 0x00, 0x00, 0x16, 0x03, 0x01,
+               0x02, 0xbe, 0x0b, 0x00, 0x02, 0xba, 0x00, 0x02,
+               0xb7, 0x00, 0x02, 0xb4, 0x30, 0x82, 0x02, 0xb0,
+               0x30, 0x82, 0x02, 0x19, 0xa0, 0x03, 0x02, 0x01,
+               0x02, 0x02, 0x09, 0x00, 0x85, 0xb0, 0xbb, 0xa4,
+               0x8a, 0x7f, 0xb8, 0xca, 0x30, 0x0d, 0x06, 0x09,
+               0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+               0x05, 0x05, 0x00, 0x30, 0x45, 0x31, 0x0b, 0x30,
+               0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
+               0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03,
+               0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d,
+               0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31,
+               0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a,
+               0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e,
+               0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69,
+               0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c,
+               0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x30,
+               0x30, 0x34, 0x32, 0x34, 0x30, 0x39, 0x30, 0x39,
+               0x33, 0x38, 0x5a, 0x17, 0x0d, 0x31, 0x31, 0x30,
+               0x34, 0x32, 0x34, 0x30, 0x39, 0x30, 0x39, 0x33,
+               0x38, 0x5a, 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09,
+               0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x41,
+               0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55,
+               0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, 0x65,
+               0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, 0x21,
+               0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
+               0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65,
+               0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, 0x74,
+               0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74,
+               0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09,
+               0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+               0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30,
+               0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xbb, 0x79,
+               0xd6, 0xf5, 0x17, 0xb5, 0xe5, 0xbf, 0x46, 0x10,
+               0xd0, 0xdc, 0x69, 0xbe, 0xe6, 0x2b, 0x07, 0x43,
+               0x5a, 0xd0, 0x03, 0x2d, 0x8a, 0x7a, 0x43, 0x85,
+               0xb7, 0x14, 0x52, 0xe7, 0xa5, 0x65, 0x4c, 0x2c,
+               0x78, 0xb8, 0x23, 0x8c, 0xb5, 0xb4, 0x82, 0xe5,
+               0xde, 0x1f, 0x95, 0x3b, 0x7e, 0x62, 0xa5, 0x2c,
+               0xa5, 0x33, 0xd6, 0xfe, 0x12, 0x5c, 0x7a, 0x56,
+               0xfc, 0xf5, 0x06, 0xbf, 0xfa, 0x58, 0x7b, 0x26,
+               0x3f, 0xb5, 0xcd, 0x04, 0xd3, 0xd0, 0xc9, 0x21,
+               0x96, 0x4a, 0xc7, 0xf4, 0x54, 0x9f, 0x5a, 0xbf,
+               0xef, 0x42, 0x71, 0x00, 0xfe, 0x18, 0x99, 0x07,
+               0x7f, 0x7e, 0x88, 0x7d, 0x7d, 0xf1, 0x04, 0x39,
+               0xc4, 0xa2, 0x2e, 0xdb, 0x51, 0xc9, 0x7c, 0xe3,
+               0xc0, 0x4c, 0x3b, 0x32, 0x66, 0x01, 0xcf, 0xaf,
+               0xb1, 0x1d, 0xb8, 0x71, 0x9a, 0x1d, 0xdb, 0xdb,
+               0x89, 0x6b, 0xae, 0xda, 0x2d, 0x79, 0x02, 0x03,
+               0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7, 0x30, 0x81,
+               0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e,
+               0x04, 0x16, 0x04, 0x14, 0xb1, 0xad, 0xe2, 0x85,
+               0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, 0xce, 0x23,
+               0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, 0x88, 0x39,
+               0x30, 0x75, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04,
+               0x6e, 0x30, 0x6c, 0x80, 0x14, 0xb1, 0xad, 0xe2,
+               0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69, 0xce,
+               0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18, 0x88,
+               0x39, 0xa1, 0x49, 0xa4, 0x47, 0x30, 0x45, 0x31,
                0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06,
                0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11,
                0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53,
@@ -750,120 +860,126 @@ var aesServerScript = [][]byte{
                0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65,
                0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64,
                0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79,
-               0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d,
-               0x31, 0x30, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39,
-               0x30, 0x39, 0x33, 0x38, 0x5a, 0x17, 0x0d, 0x31,
-               0x31, 0x30, 0x34, 0x32, 0x34, 0x30, 0x39, 0x30,
-               0x39, 0x33, 0x38, 0x5a, 0x30, 0x45, 0x31, 0x0b,
-               0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
-               0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06,
-               0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x53, 0x6f,
-               0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65,
-               0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04,
-               0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72,
-               0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67,
-               0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20,
-               0x4c, 0x74, 0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d,
-               0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
-               0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d,
-               0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00,
-               0xbb, 0x79, 0xd6, 0xf5, 0x17, 0xb5, 0xe5, 0xbf,
-               0x46, 0x10, 0xd0, 0xdc, 0x69, 0xbe, 0xe6, 0x2b,
-               0x07, 0x43, 0x5a, 0xd0, 0x03, 0x2d, 0x8a, 0x7a,
-               0x43, 0x85, 0xb7, 0x14, 0x52, 0xe7, 0xa5, 0x65,
-               0x4c, 0x2c, 0x78, 0xb8, 0x23, 0x8c, 0xb5, 0xb4,
-               0x82, 0xe5, 0xde, 0x1f, 0x95, 0x3b, 0x7e, 0x62,
-               0xa5, 0x2c, 0xa5, 0x33, 0xd6, 0xfe, 0x12, 0x5c,
-               0x7a, 0x56, 0xfc, 0xf5, 0x06, 0xbf, 0xfa, 0x58,
-               0x7b, 0x26, 0x3f, 0xb5, 0xcd, 0x04, 0xd3, 0xd0,
-               0xc9, 0x21, 0x96, 0x4a, 0xc7, 0xf4, 0x54, 0x9f,
-               0x5a, 0xbf, 0xef, 0x42, 0x71, 0x00, 0xfe, 0x18,
-               0x99, 0x07, 0x7f, 0x7e, 0x88, 0x7d, 0x7d, 0xf1,
-               0x04, 0x39, 0xc4, 0xa2, 0x2e, 0xdb, 0x51, 0xc9,
-               0x7c, 0xe3, 0xc0, 0x4c, 0x3b, 0x32, 0x66, 0x01,
-               0xcf, 0xaf, 0xb1, 0x1d, 0xb8, 0x71, 0x9a, 0x1d,
-               0xdb, 0xdb, 0x89, 0x6b, 0xae, 0xda, 0x2d, 0x79,
-               0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa7,
-               0x30, 0x81, 0xa4, 0x30, 0x1d, 0x06, 0x03, 0x55,
-               0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xb1, 0xad,
-               0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb, 0x69,
-               0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e, 0x18,
-               0x88, 0x39, 0x30, 0x75, 0x06, 0x03, 0x55, 0x1d,
-               0x23, 0x04, 0x6e, 0x30, 0x6c, 0x80, 0x14, 0xb1,
-               0xad, 0xe2, 0x85, 0x5a, 0xcf, 0xcb, 0x28, 0xdb,
-               0x69, 0xce, 0x23, 0x69, 0xde, 0xd3, 0x26, 0x8e,
-               0x18, 0x88, 0x39, 0xa1, 0x49, 0xa4, 0x47, 0x30,
-               0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,
-               0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13,
-               0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13,
-               0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74,
-               0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06,
-               0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e,
-               0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57,
-               0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50,
-               0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x82, 0x09,
-               0x00, 0x85, 0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8,
-               0xca, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13,
-               0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30,
-               0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
-               0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81,
-               0x81, 0x00, 0x08, 0x6c, 0x45, 0x24, 0xc7, 0x6b,
-               0xb1, 0x59, 0xab, 0x0c, 0x52, 0xcc, 0xf2, 0xb0,
-               0x14, 0xd7, 0x87, 0x9d, 0x7a, 0x64, 0x75, 0xb5,
-               0x5a, 0x95, 0x66, 0xe4, 0xc5, 0x2b, 0x8e, 0xae,
-               0x12, 0x66, 0x1f, 0xeb, 0x4f, 0x38, 0xb3, 0x6e,
-               0x60, 0xd3, 0x92, 0xfd, 0xf7, 0x41, 0x08, 0xb5,
-               0x25, 0x13, 0xb1, 0x18, 0x7a, 0x24, 0xfb, 0x30,
-               0x1d, 0xba, 0xed, 0x98, 0xb9, 0x17, 0xec, 0xe7,
-               0xd7, 0x31, 0x59, 0xdb, 0x95, 0xd3, 0x1d, 0x78,
-               0xea, 0x50, 0x56, 0x5c, 0xd5, 0x82, 0x5a, 0x2d,
-               0x5a, 0x5f, 0x33, 0xc4, 0xb6, 0xd8, 0xc9, 0x75,
-               0x90, 0x96, 0x8c, 0x0f, 0x52, 0x98, 0xb5, 0xcd,
-               0x98, 0x1f, 0x89, 0x20, 0x5f, 0xf2, 0xa0, 0x1c,
-               0xa3, 0x1b, 0x96, 0x94, 0xdd, 0xa9, 0xfd, 0x57,
-               0xe9, 0x70, 0xe8, 0x26, 0x6d, 0x71, 0x99, 0x9b,
-               0x26, 0x6e, 0x38, 0x50, 0x29, 0x6c, 0x90, 0xa7,
-               0xbd, 0xd9, 0x16, 0x03, 0x01, 0x00, 0x04, 0x0e,
-               0x00, 0x00, 0x00,
+               0x20, 0x4c, 0x74, 0x64, 0x82, 0x09, 0x00, 0x85,
+               0xb0, 0xbb, 0xa4, 0x8a, 0x7f, 0xb8, 0xca, 0x30,
+               0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05,
+               0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0d, 0x06,
+               0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+               0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00,
+               0x08, 0x6c, 0x45, 0x24, 0xc7, 0x6b, 0xb1, 0x59,
+               0xab, 0x0c, 0x52, 0xcc, 0xf2, 0xb0, 0x14, 0xd7,
+               0x87, 0x9d, 0x7a, 0x64, 0x75, 0xb5, 0x5a, 0x95,
+               0x66, 0xe4, 0xc5, 0x2b, 0x8e, 0xae, 0x12, 0x66,
+               0x1f, 0xeb, 0x4f, 0x38, 0xb3, 0x6e, 0x60, 0xd3,
+               0x92, 0xfd, 0xf7, 0x41, 0x08, 0xb5, 0x25, 0x13,
+               0xb1, 0x18, 0x7a, 0x24, 0xfb, 0x30, 0x1d, 0xba,
+               0xed, 0x98, 0xb9, 0x17, 0xec, 0xe7, 0xd7, 0x31,
+               0x59, 0xdb, 0x95, 0xd3, 0x1d, 0x78, 0xea, 0x50,
+               0x56, 0x5c, 0xd5, 0x82, 0x5a, 0x2d, 0x5a, 0x5f,
+               0x33, 0xc4, 0xb6, 0xd8, 0xc9, 0x75, 0x90, 0x96,
+               0x8c, 0x0f, 0x52, 0x98, 0xb5, 0xcd, 0x98, 0x1f,
+               0x89, 0x20, 0x5f, 0xf2, 0xa0, 0x1c, 0xa3, 0x1b,
+               0x96, 0x94, 0xdd, 0xa9, 0xfd, 0x57, 0xe9, 0x70,
+               0xe8, 0x26, 0x6d, 0x71, 0x99, 0x9b, 0x26, 0x6e,
+               0x38, 0x50, 0x29, 0x6c, 0x90, 0xa7, 0xbd, 0xd9,
+               0x16, 0x03, 0x01, 0x00, 0x04, 0x0e, 0x00, 0x00,
+               0x00,
        },
-
        {
                0x16, 0x03, 0x01, 0x00, 0x86, 0x10, 0x00, 0x00,
-               0x82, 0x00, 0x80, 0x71, 0x9c, 0xe7, 0x23, 0xfc,
-               0xb9, 0x19, 0x29, 0x82, 0xbf, 0xef, 0x08, 0xf7,
-               0x99, 0x36, 0xc3, 0x4c, 0x6f, 0x05, 0xd2, 0x8b,
-               0x62, 0x2b, 0x19, 0x9b, 0x7f, 0xc0, 0xcc, 0x48,
-               0x30, 0x5f, 0xcd, 0xc3, 0x70, 0x55, 0x53, 0x73,
-               0xfa, 0x79, 0x74, 0xf3, 0xa3, 0x76, 0x9f, 0xa1,
-               0x7f, 0x98, 0xc2, 0xc0, 0xe3, 0xc5, 0xa0, 0x31,
-               0x2f, 0xa6, 0xe8, 0x1e, 0x61, 0x46, 0xb3, 0x9b,
-               0x4b, 0x16, 0xf1, 0x2d, 0xc7, 0x63, 0x7f, 0x79,
-               0x22, 0x30, 0xd1, 0xf2, 0xfc, 0x77, 0x98, 0x0a,
-               0x16, 0x11, 0x63, 0x71, 0x7f, 0x70, 0xef, 0x16,
-               0xbb, 0x39, 0x87, 0x34, 0xac, 0x49, 0xbd, 0x07,
-               0x67, 0xcb, 0x9c, 0xcc, 0xde, 0xef, 0xb1, 0xe0,
-               0xdb, 0x01, 0xb5, 0x35, 0xa9, 0xb3, 0x10, 0x0c,
-               0x4b, 0xee, 0xb3, 0x4e, 0xfd, 0xbe, 0x15, 0x27,
-               0xf0, 0x46, 0xb2, 0x38, 0xba, 0x5f, 0xcc, 0x89,
-               0xec, 0x29, 0x82, 0x14, 0x03, 0x01, 0x00, 0x01,
-               0x01, 0x16, 0x03, 0x01, 0x00, 0x30, 0x3c, 0xfb,
-               0xa4, 0x12, 0xcb, 0x00, 0xf9, 0x57, 0x7e, 0x9b,
-               0xc9, 0xdc, 0x0c, 0xba, 0x9a, 0x81, 0x62, 0xfb,
-               0x26, 0x13, 0x53, 0xfe, 0xaa, 0xcc, 0x82, 0xbb,
-               0xb6, 0x67, 0x7f, 0x39, 0xbe, 0x4d, 0xbb, 0xc0,
-               0x6c, 0x24, 0x31, 0x83, 0xa5, 0x50, 0x3a, 0x75,
-               0x32, 0x64, 0xb5, 0xdb, 0xbe, 0x0a,
+               0x82, 0x00, 0x80, 0x51, 0x2e, 0xec, 0x0d, 0x86,
+               0xf3, 0x9f, 0xf2, 0x77, 0x04, 0x27, 0x2b, 0x0e,
+               0x9c, 0xab, 0x35, 0x84, 0x65, 0xff, 0x36, 0xef,
+               0xc0, 0x08, 0xc9, 0x1d, 0x9f, 0x29, 0xae, 0x8d,
+               0xc5, 0x66, 0x81, 0x31, 0x92, 0x5e, 0x3d, 0xac,
+               0xaa, 0x37, 0x28, 0x2c, 0x06, 0x91, 0xa6, 0xc2,
+               0xd0, 0x83, 0x34, 0x24, 0x1c, 0x88, 0xfc, 0x0a,
+               0xcf, 0xbf, 0xc2, 0x94, 0xe2, 0xed, 0xa7, 0x6a,
+               0xa8, 0x8d, 0x3d, 0xf7, 0x06, 0x7d, 0x69, 0xf8,
+               0x0d, 0xb2, 0xf7, 0xe4, 0x45, 0xcb, 0x0a, 0x25,
+               0xcb, 0xb2, 0x2e, 0x38, 0x9a, 0x84, 0x75, 0xe8,
+               0xe1, 0x42, 0x39, 0xa2, 0x18, 0x0e, 0x48, 0xca,
+               0x33, 0x16, 0x4e, 0xf6, 0x2f, 0xec, 0x07, 0xe7,
+               0x57, 0xe1, 0x20, 0x40, 0x40, 0x6d, 0x4e, 0x29,
+               0x04, 0x1a, 0x8c, 0x99, 0xfb, 0x19, 0x3c, 0xaa,
+               0x75, 0x64, 0xd3, 0xa6, 0xe6, 0xed, 0x3f, 0x5a,
+               0xd2, 0xc9, 0x80, 0x14, 0x03, 0x01, 0x00, 0x01,
+               0x01, 0x16, 0x03, 0x01, 0x01, 0x10, 0xe9, 0x9e,
+               0x06, 0x92, 0x18, 0xbf, 0x5e, 0xaf, 0x33, 0xc1,
+               0xbf, 0x0e, 0x12, 0x07, 0x48, 0x4f, 0x6b, 0x6c,
+               0xf5, 0x23, 0x5e, 0x87, 0xa7, 0xd3, 0x50, 0x79,
+               0x38, 0xdc, 0xe0, 0x49, 0xd3, 0x81, 0x21, 0x12,
+               0xd0, 0x3d, 0x9a, 0xfb, 0x83, 0xc1, 0x8b, 0xfc,
+               0x14, 0xd5, 0xd5, 0xa7, 0xa3, 0x34, 0x14, 0x71,
+               0xbe, 0xea, 0x37, 0x18, 0x12, 0x7f, 0x41, 0xfb,
+               0xc5, 0x51, 0x17, 0x9d, 0x96, 0x58, 0x14, 0xfb,
+               0x4f, 0xd7, 0xd3, 0x15, 0x0f, 0xec, 0x5a, 0x0d,
+               0x35, 0xbb, 0x3c, 0x81, 0x5b, 0x3f, 0xdf, 0x52,
+               0xa4, 0x4c, 0xcd, 0x13, 0xe1, 0x10, 0x37, 0x34,
+               0xbf, 0xb4, 0x80, 0x1e, 0x8d, 0xe2, 0xc3, 0x7a,
+               0x0f, 0x7b, 0x7d, 0x23, 0xeb, 0xd0, 0x99, 0x69,
+               0xad, 0x0a, 0x2d, 0xb3, 0x6c, 0xd6, 0x80, 0x11,
+               0x7f, 0x6c, 0xed, 0x1b, 0xcd, 0x08, 0x22, 0x56,
+               0x90, 0x0e, 0xa4, 0xc3, 0x29, 0x33, 0x96, 0x30,
+               0x34, 0x94, 0xa1, 0xeb, 0x9c, 0x1b, 0x5a, 0xd1,
+               0x03, 0x61, 0xf9, 0xdd, 0xf3, 0x64, 0x8a, 0xfd,
+               0x5f, 0x44, 0xdb, 0x2e, 0xa7, 0xfd, 0xe1, 0x1a,
+               0x66, 0xc5, 0x01, 0x9c, 0xc7, 0xd1, 0xc4, 0xd3,
+               0xea, 0x14, 0x3c, 0xed, 0x74, 0xbb, 0x1b, 0x97,
+               0x8f, 0xf1, 0x29, 0x39, 0x33, 0x92, 0x93, 0x4e,
+               0xf5, 0x87, 0x91, 0x61, 0x65, 0x8d, 0x27, 0x8d,
+               0x76, 0xc1, 0xfa, 0x6a, 0x99, 0x80, 0xb1, 0x9b,
+               0x29, 0x53, 0xce, 0x3e, 0xb6, 0x9a, 0xce, 0x3c,
+               0x19, 0x5e, 0x48, 0x83, 0xaa, 0xa7, 0x66, 0x98,
+               0x59, 0xf4, 0xbb, 0xf2, 0xbc, 0xd9, 0xc5, 0x9a,
+               0xc8, 0x2c, 0x63, 0x58, 0xd5, 0xd4, 0xbc, 0x03,
+               0xa9, 0x06, 0xa9, 0x80, 0x0d, 0xb3, 0x46, 0x2d,
+               0xe3, 0xc6, 0xaf, 0x1a, 0x39, 0x18, 0x7e, 0x1e,
+               0x83, 0x80, 0x46, 0x11, 0xd2, 0x13, 0x9f, 0xda,
+               0xfc, 0x2d, 0x42, 0xaa, 0x5a, 0x1d, 0x4c, 0x31,
+               0xe5, 0x58, 0x78, 0x5e, 0xe2, 0x04, 0xd6, 0x23,
+               0x7f, 0x3f, 0x06, 0xc0, 0x54, 0xf8,
        },
-
        {
-               0x14, 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03,
-               0x01, 0x00, 0x30, 0x43, 0x24, 0x42, 0x55, 0x08,
-               0xe4, 0xc2, 0x15, 0xc9, 0xdb, 0x71, 0x69, 0xee,
-               0x09, 0xc5, 0x1c, 0xfd, 0x46, 0x10, 0xa0, 0x68,
-               0x21, 0xf2, 0x48, 0xac, 0x6c, 0xc0, 0x2b, 0x62,
-               0x07, 0x8f, 0x48, 0x33, 0x0a, 0x6b, 0x62, 0x28,
-               0x2e, 0x2c, 0xad, 0xcb, 0x34, 0x85, 0xca, 0x2e,
-               0xcd, 0x84, 0xf0,
+               0x16, 0x03, 0x01, 0x00, 0x72, 0x04, 0x00, 0x00,
+               0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65,
+               0xe8, 0x4b, 0xfb, 0xef, 0xba, 0xed, 0xc5, 0x36,
+               0xc8, 0x5a, 0x41, 0x3f, 0x05, 0xfa, 0xfe, 0x48,
+               0xc3, 0x91, 0x12, 0x8b, 0xe8, 0x32, 0x6a, 0x9f,
+               0xdc, 0x97, 0xe2, 0x77, 0xb9, 0x96, 0x2d, 0xd4,
+               0xe5, 0xbd, 0xa1, 0xfd, 0x94, 0xbb, 0x74, 0x63,
+               0xb1, 0x0c, 0x38, 0xbc, 0x6f, 0x69, 0xaf, 0xa3,
+               0x46, 0x9c, 0x96, 0x41, 0xde, 0x59, 0x23, 0xff,
+               0x15, 0x6b, 0x3a, 0xef, 0x91, 0x6d, 0x92, 0x44,
+               0xdc, 0x72, 0x1f, 0x40, 0x3d, 0xb5, 0x34, 0x8f,
+               0x2a, 0xac, 0x21, 0x69, 0x05, 0x6f, 0xb2, 0x60,
+               0x32, 0x5d, 0x3d, 0x97, 0xb4, 0x24, 0x99, 0x14,
+               0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, 0x01,
+               0x00, 0x30, 0x68, 0x27, 0x97, 0xca, 0x63, 0x09,
+               0x22, 0xed, 0x0e, 0x61, 0x7c, 0x76, 0x31, 0x9c,
+               0xbe, 0x27, 0xc9, 0xe6, 0x09, 0xc3, 0xc3, 0xc2,
+               0xf4, 0xa2, 0x32, 0xba, 0x7c, 0xf2, 0x0f, 0xb8,
+               0x3d, 0xcb, 0xe2, 0x4c, 0xc0, 0x7d, 0x8e, 0x5b,
+               0x5a, 0xed, 0x05, 0x5c, 0x15, 0x96, 0x69, 0xc2,
+               0x6f, 0x5f, 0x17, 0x03, 0x01, 0x00, 0x20, 0x5a,
+               0xfe, 0x0b, 0xe1, 0x6f, 0xa8, 0x54, 0x19, 0x78,
+               0xca, 0xba, 0x2e, 0x1e, 0x2e, 0xe1, 0x5d, 0x17,
+               0xe5, 0x97, 0x05, 0x2c, 0x08, 0x0c, 0xff, 0xa8,
+               0x59, 0xa9, 0xde, 0x5e, 0x21, 0x34, 0x04, 0x17,
+               0x03, 0x01, 0x00, 0x30, 0x86, 0xb1, 0x3f, 0x88,
+               0x43, 0xf0, 0x07, 0xee, 0xa8, 0xf4, 0xbc, 0xe7,
+               0x5f, 0xc6, 0x8c, 0x86, 0x4c, 0xca, 0x70, 0x88,
+               0xcc, 0x6a, 0xb4, 0x3d, 0x40, 0xe8, 0x54, 0x89,
+               0x19, 0x43, 0x1f, 0x76, 0xe2, 0xac, 0xb2, 0x5b,
+               0x92, 0xf8, 0x57, 0x39, 0x2a, 0xc3, 0x6d, 0x13,
+               0x45, 0xfa, 0x36, 0x9e, 0x15, 0x03, 0x01, 0x00,
+               0x20, 0x6d, 0xed, 0x7b, 0x59, 0x28, 0x2a, 0x27,
+               0x04, 0x15, 0x07, 0x4e, 0xeb, 0x13, 0x00, 0xe3,
+               0x3a, 0x3f, 0xf8, 0xaa, 0x2b, 0x3b, 0x1a, 0x8c,
+               0x12, 0xd6, 0x4c, 0xec, 0x2a, 0xaf, 0x33, 0x60,
+               0xaf,
        },
 }
 
index 182506c59efd077d866fc62d24b6aeea61c3e475..9230656d6a481d0fdc38d194cd2e959fef69ec9f 100644 (file)
@@ -155,7 +155,7 @@ func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (cert Certificate, err error)
                        err = errors.New("crypto/tls: failed to parse key PEM data")
                        return
                }
-               if strings.HasSuffix(keyDERBlock.Type, " PRIVATE KEY") {
+               if keyDERBlock.Type == "PRIVATE KEY" || strings.HasSuffix(keyDERBlock.Type, " PRIVATE KEY") {
                        break
                }
        }
index 31b858d83274d8f5ec67c90a8504ada54e3d70be..38229014cd662bba804577ed638acd139274999b 100644 (file)
@@ -33,6 +33,19 @@ D2lWusoe2/nEqfDVVWGWlyJ7yOmqaVm/iNUN9B2N2g==
 -----END RSA PRIVATE KEY-----
 `
 
+// keyPEM is the same as rsaKeyPEM, but declares itself as just
+// "PRIVATE KEY", not "RSA PRIVATE KEY".  http://golang.org/issue/4477
+var keyPEM = `-----BEGIN PRIVATE KEY-----
+MIIBOwIBAAJBANLJhPHhITqQbPklG3ibCVxwGMRfp/v4XqhfdQHdcVfHap6NQ5Wo
+k/4xIA+ui35/MmNartNuC+BdZ1tMuVCPFZcCAwEAAQJAEJ2N+zsR0Xn8/Q6twa4G
+6OB1M1WO+k+ztnX/1SvNeWu8D6GImtupLTYgjZcHufykj09jiHmjHx8u8ZZB/o1N
+MQIhAPW+eyZo7ay3lMz1V01WVjNKK9QSn1MJlb06h/LuYv9FAiEA25WPedKgVyCW
+SmUwbPw8fnTcpqDWE3yTO3vKcebqMSsCIBF3UmVue8YU3jybC3NxuXq3wNm34R8T
+xVLHwDXh/6NJAiEAl2oHGGLz64BuAfjKrqwz7qMYr9HCLIe/YsoWq/olzScCIQDi
+D2lWusoe2/nEqfDVVWGWlyJ7yOmqaVm/iNUN9B2N2g==
+-----END PRIVATE KEY-----
+`
+
 var ecdsaCertPEM = `-----BEGIN CERTIFICATE-----
 MIIB/jCCAWICCQDscdUxw16XFDAJBgcqhkjOPQQBMEUxCzAJBgNVBAYTAkFVMRMw
 EQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0
@@ -62,21 +75,22 @@ kohxS/xfFg/TEwRSSws+roJr4JFKpO2t3/be5OdqmQ==
 
 var keyPairTests = []struct {
        algo string
-       cert *string
-       key  *string
+       cert string
+       key  string
 }{
-       {"ECDSA", &ecdsaCertPEM, &ecdsaKeyPEM},
-       {"RSA", &rsaCertPEM, &rsaKeyPEM},
+       {"ECDSA", ecdsaCertPEM, ecdsaKeyPEM},
+       {"RSA", rsaCertPEM, rsaKeyPEM},
+       {"RSA-untyped", rsaCertPEM, keyPEM}, // golang.org/issue/4477
 }
 
 func TestX509KeyPair(t *testing.T) {
        var pem []byte
        for _, test := range keyPairTests {
-               pem = []byte(*test.cert + *test.key)
+               pem = []byte(test.cert + test.key)
                if _, err := X509KeyPair(pem, pem); err != nil {
                        t.Errorf("Failed to load %s cert followed by %s key: %s", test.algo, test.algo, err)
                }
-               pem = []byte(*test.key + *test.cert)
+               pem = []byte(test.key + test.cert)
                if _, err := X509KeyPair(pem, pem); err != nil {
                        t.Errorf("Failed to load %s key followed by %s cert: %s", test.algo, test.algo, err)
                }
index 06670141e16ee828214e5b057508cb954d5f3df1..04d5723c1e27c0d8a0d7eba95b275f258d4fdb1d 100644 (file)
@@ -184,7 +184,7 @@ func Read(r io.Reader, order ByteOrder, data interface{}) error {
 // values, or a pointer to such data.
 // Bytes written to w are encoded using the specified byte order
 // and read from successive fields of the data.
-// When writing structs, zero values are are written for fields
+// When writing structs, zero values are written for fields
 // with blank (_) field names.
 func Write(w io.Writer, order ByteOrder, data interface{}) error {
        // Fast path for basic types.
index 324944cc8299ff6ab3eac21629ac8b1b8404136f..17e485083e9d829b5cb815a3cbe43c11f2ac99c5 100644 (file)
@@ -22,7 +22,7 @@ import (
 //
 // If UseCRLF is true, the Writer ends each record with \r\n instead of \n.
 type Writer struct {
-       Comma   rune // Field delimiter (set to to ',' by NewWriter)
+       Comma   rune // Field delimiter (set to ',' by NewWriter)
        UseCRLF bool // True to use \r\n as the line terminator
        w       *bufio.Writer
 }
index 6d77c171f412739f599ddbb73df3f804f01e7558..8b6fcfb4c877c50b596e401f39380b8512118864 100644 (file)
@@ -328,7 +328,7 @@ reserved).
        01      // Add 1 to get field number 0: field[1].name
        01      // 1 byte
        59      // structType.field[1].name = "Y"
-       01      // Add 1 to get field number 1: field[0].id
+       01      // Add 1 to get field number 1: field[1].id
        04      // struct.Type.field[1].typeId is 2 (signed int).
        00      // End of structType.field[1]; end of structType.field.
        00      // end of wireType.structType structure
index b9371c42309eec79e858001e423ac106afcc1468..9a0e51d1fe19bea0193f56bcddb8bc0508f61ce0 100644 (file)
@@ -50,6 +50,7 @@ func BenchmarkEndToEndByteBuffer(b *testing.B) {
 }
 
 func TestCountEncodeMallocs(t *testing.T) {
+       defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
        var buf bytes.Buffer
        enc := NewEncoder(&buf)
        bench := &Bench{7, 3.2, "now is the time", []byte("for all good men")}
@@ -69,6 +70,7 @@ func TestCountEncodeMallocs(t *testing.T) {
 }
 
 func TestCountDecodeMallocs(t *testing.T) {
+       defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
        var buf bytes.Buffer
        enc := NewEncoder(&buf)
        bench := &Bench{7, 3.2, "now is the time", []byte("for all good men")}
index 8592a0c15cb963872ce3b5fabf896111929f2473..17134c5eb4d75f73e6591d43e1a3154588b2e85f 100644 (file)
@@ -45,7 +45,7 @@ const (
 //     - a field with tag "name,attr" becomes an attribute with
 //       the given name in the XML element.
 //     - a field with tag ",attr" becomes an attribute with the
-//       field name in the in the XML element.
+//       field name in the XML element.
 //     - a field with tag ",chardata" is written as character data,
 //       not as an XML element.
 //     - a field with tag ",innerxml" is written verbatim, not subject
diff --git a/libgo/go/exp/cookiejar/jar.go b/libgo/go/exp/cookiejar/jar.go
new file mode 100644 (file)
index 0000000..2159ec5
--- /dev/null
@@ -0,0 +1,89 @@
+// 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 cookiejar implements an RFC 6265-compliant http.CookieJar.
+//
+// TODO: example code to create a memory-backed cookie jar with the default
+// public suffix list.
+package cookiejar
+
+import (
+       "net/http"
+       "net/url"
+)
+
+// PublicSuffixList provides the public suffix of a domain. For example:
+//      - the public suffix of "example.com" is "com",
+//      - the public suffix of "foo1.foo2.foo3.co.uk" is "co.uk", and
+//      - the public suffix of "bar.pvt.k12.wy.us" is "pvt.k12.wy.us".
+//
+// Implementations of PublicSuffixList must be safe for concurrent use by
+// multiple goroutines.
+//
+// An implementation that always returns "" is valid and may be useful for
+// testing but it is not secure: it means that the HTTP server for foo.com can
+// set a cookie for bar.com.
+type PublicSuffixList interface {
+       // PublicSuffix returns the public suffix of domain.
+       //
+       // TODO: specify which of the caller and callee is responsible for IP
+       // addresses, for leading and trailing dots, for case sensitivity, and
+       // for IDN/Punycode.
+       PublicSuffix(domain string) string
+
+       // String returns a description of the source of this public suffix list.
+       // A Jar will store its PublicSuffixList's description in its storage,
+       // and update the stored cookies if its list has a different description
+       // than the stored list. The description will typically contain something
+       // like a time stamp or version number.
+       String() string
+}
+
+// Options are the options for creating a new Jar.
+type Options struct {
+       // Storage is the cookie jar storage. It may not be nil.
+       Storage Storage
+
+       // PublicSuffixList is the public suffix list that determines whether an
+       // HTTP server can set a cookie for a domain. It may not be nil.
+       PublicSuffixList PublicSuffixList
+
+       // TODO: ErrorFunc for handling storage errors?
+}
+
+// Jar implements the http.CookieJar interface from the net/http package.
+type Jar struct {
+       storage Storage
+       psList  PublicSuffixList
+}
+
+// New returns a new cookie jar.
+func New(o *Options) *Jar {
+       return &Jar{
+               storage: o.Storage,
+               psList:  o.PublicSuffixList,
+       }
+}
+
+// TODO(nigeltao): how do we reject HttpOnly cookies? Do we post-process the
+// return value from Jar.Cookies?
+//
+// HttpOnly cookies are those for regular HTTP(S) requests but should not be
+// visible from JavaScript. The HttpOnly bit mitigates XSS attacks; it's not
+// for HTTP vs HTTPS vs FTP transports.
+
+// Cookies implements the Cookies method of the http.CookieJar interface.
+//
+// It returns an empty slice if the URL's scheme is not HTTP or HTTPS.
+func (j *Jar) Cookies(u *url.URL) []*http.Cookie {
+       // TODO.
+       return nil
+}
+
+// SetCookies implements the SetCookies method of the http.CookieJar interface.
+//
+// It does nothing if the URL's scheme is not HTTP or HTTPS.
+func (j *Jar) SetCookies(u *url.URL, cookies []*http.Cookie) {
+       // TODO.
+}
diff --git a/libgo/go/exp/cookiejar/storage.go b/libgo/go/exp/cookiejar/storage.go
new file mode 100644 (file)
index 0000000..5294f58
--- /dev/null
@@ -0,0 +1,101 @@
+// 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 cookiejar
+
+import (
+       "time"
+)
+
+// Storage is a Jar's storage. It is a multi-map, mapping keys to one or more
+// entries. Each entry consists of a subkey, creation time, last access time,
+// and some arbitrary data.
+//
+// The Add and Delete methods have undefined behavior if the key is invalid.
+// A valid key must use only bytes in the character class [a-z0-9.-] and
+// must have at least one non-. byte. Note that this excludes any key
+// containing a capital ASCII letter as well as the empty string.
+type Storage interface {
+       // A client must call Lock before calling other methods and must call
+       // Unlock when finished. Between the calls to Lock and Unlock, a client
+       // can assume that other clients are not modifying the Storage.
+       Lock()
+       Unlock()
+
+       // Add adds entries to the storage. Each entry's Subkey and Data must
+       // both be non-empty.
+       //
+       // If the Storage already contains an entry with the same key and
+       // subkey then the new entry is stored with the creation time of the
+       // old entry, and the old entry is deleted.
+       //
+       // Adding entries may cause other entries to be deleted, to maintain an
+       // implementation-specific storage constraint.
+       Add(key string, entries ...Entry) error
+
+       // Delete deletes all entries for the given key.
+       Delete(key string) error
+
+       // Entries calls f for each entry stored for that key. If f returns a
+       // non-nil error then the iteration stops and Entries returns that
+       // error. Iteration is not guaranteed to be in any particular order.
+       //
+       // If f returns an Update action then that stored entry's LastAccess
+       // time will be set to the time that f returned. If f returns a Delete
+       // action then that entry will be deleted from the Storage.
+       //
+       // f may call a Storage's Add and Delete methods; those modifications
+       // will not affect the list of entries visited in this call to Entries.
+       Entries(key string, f func(entry Entry) (Action, time.Time, error)) error
+
+       // Keys calls f for each key stored. f will not be called on a key with
+       // zero entries. If f returns a non-nil error then the iteration stops
+       // and Keys returns that error. Iteration is not guaranteed to be in any
+       // particular order.
+       //
+       // f may call a Storage's Add, Delete and Entries methods; those
+       // modifications will not affect the list of keys visited in this call
+       // to Keys.
+       Keys(f func(key string) error) error
+}
+
+// Entry is an entry in a Storage.
+type Entry struct {
+       Subkey     string
+       Data       string
+       Creation   time.Time
+       LastAccess time.Time
+}
+
+// Action is an action returned by the function passed to Entries.
+type Action int
+
+const (
+       // Pass means to take no further action with an Entry.
+       Pass Action = iota
+       // Update means to update the LastAccess time of an Entry.
+       Update
+       // Delete means to delete an Entry.
+       Delete
+)
+
+// ValidStorageKey returns whether the given key is valid for a Storage.
+func ValidStorageKey(key string) bool {
+       hasNonDot := false
+       for i := 0; i < len(key); i++ {
+               switch c := key[i]; {
+               case 'a' <= c && c <= 'z':
+                       fallthrough
+               case '0' <= c && c <= '9':
+                       fallthrough
+               case c == '-':
+                       hasNonDot = true
+               case c == '.':
+                       // No-op.
+               default:
+                       return false
+               }
+       }
+       return hasNonDot
+}
diff --git a/libgo/go/exp/cookiejar/storage_test.go b/libgo/go/exp/cookiejar/storage_test.go
new file mode 100644 (file)
index 0000000..de6aa2b
--- /dev/null
@@ -0,0 +1,48 @@
+// 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 cookiejar
+
+import (
+       "testing"
+)
+
+var validStorageKeyTests = map[string]bool{
+       "":            false,
+       ".":           false,
+       "..":          false,
+       "/":           false,
+       "EXAMPLE.com": false,
+       "\n":          false,
+       "\r":          false,
+       "\r\n":        false,
+       "\x00":        false,
+       "back\\slash": false,
+       "co:lon":      false,
+       "com,ma":      false,
+       "semi;colon":  false,
+       "sl/ash":      false,
+       "sp ace":      false,
+       "under_score": false,
+       "Ï€":           false,
+
+       "-":                true,
+       ".dot":             true,
+       ".dot.":            true,
+       ".metadata":        true,
+       ".x..y..z...":      true,
+       "dot.":             true,
+       "example.com":      true,
+       "foo":              true,
+       "hy-phen":          true,
+       "xn--bcher-kva.ch": true,
+}
+
+func TestValidStorageKey(t *testing.T) {
+       for key, want := range validStorageKeyTests {
+               if got := ValidStorageKey(key); got != want {
+                       t.Errorf("%q: got %v, want %v", key, got, want)
+               }
+       }
+}
index 42d716d81f97f864b7ebb8c591e0a9d64b8d82a4..2d58f32883951868ef37a50cf5f4967247f74961 100644 (file)
@@ -5,20 +5,38 @@
 package main
 
 import (
+       "go/build"
        "path/filepath"
        "runtime"
+       "strings"
        "testing"
 )
 
-func runTest(t *testing.T, path, pkg string) {
+func runTest(t *testing.T, path string) {
        exitCode = 0
-       *pkgName = pkg
-       *recursive = false
 
-       if pkg == "" {
+       *recursive = false
+       if suffix := ".go"; strings.HasSuffix(path, suffix) {
+               // single file
+               path = filepath.Join(runtime.GOROOT(), "src/pkg", path)
+               path, file := filepath.Split(path)
+               *pkgName = file[:len(file)-len(suffix)]
                processFiles([]string{path}, true)
        } else {
-               processDirectory(path)
+               // package directory
+               // TODO(gri) gotype should use the build package instead
+               pkg, err := build.Import(path, "", 0)
+               if err != nil {
+                       t.Errorf("build.Import error for path = %s: %s", path, err)
+                       return
+               }
+               // TODO(gri) there ought to be a more direct way using the build package...
+               files := make([]string, len(pkg.GoFiles))
+               for i, file := range pkg.GoFiles {
+                       files[i] = filepath.Join(pkg.Dir, file)
+               }
+               *pkgName = pkg.Name
+               processFiles(files, true)
        }
 
        if exitCode != 0 {
@@ -26,26 +44,167 @@ func runTest(t *testing.T, path, pkg string) {
        }
 }
 
-var tests = []struct {
-       path string
-       pkg  string
-}{
+var tests = []string{
        // individual files
-       {"testdata/test1.go", ""},
+       "exp/gotype/testdata/test1.go",
 
        // directories
-       {filepath.Join(runtime.GOROOT(), "src/pkg/go/ast"), "ast"},
-       {filepath.Join(runtime.GOROOT(), "src/pkg/go/build"), "build"},
-       {filepath.Join(runtime.GOROOT(), "src/pkg/go/doc"), "doc"},
-       {filepath.Join(runtime.GOROOT(), "src/pkg/go/parser"), "parser"},
-       {filepath.Join(runtime.GOROOT(), "src/pkg/go/printer"), "printer"},
-       {filepath.Join(runtime.GOROOT(), "src/pkg/go/scanner"), "scanner"},
-       {filepath.Join(runtime.GOROOT(), "src/pkg/go/token"), "token"},
-       {filepath.Join(runtime.GOROOT(), "src/pkg/exp/types"), "types"},
+       // Note: packages that don't typecheck yet are commented out
+       // "archive/tar", // investigate
+       "archive/zip",
+
+       "bufio",
+       "bytes",
+
+       "compress/bzip2",
+       "compress/flate",
+       "compress/gzip",
+       "compress/lzw",
+       "compress/zlib",
+
+       "container/heap",
+       "container/list",
+       "container/ring",
+
+       "crypto",
+       "crypto/aes",
+       "crypto/cipher",
+       "crypto/des",
+       "crypto/dsa",
+       "crypto/ecdsa",
+       "crypto/elliptic",
+       "crypto/hmac",
+       "crypto/md5",
+       "crypto/rand",
+       "crypto/rc4",
+       // "crypto/rsa", // investigate (GOARCH=386)
+       "crypto/sha1",
+       "crypto/sha256",
+       "crypto/sha512",
+       "crypto/subtle",
+       "crypto/tls",
+       // "crypto/x509", // investigate
+       "crypto/x509/pkix",
+
+       "database/sql",
+       "database/sql/driver",
+
+       "debug/dwarf",
+       "debug/elf",
+       "debug/gosym",
+       "debug/macho",
+       "debug/pe",
+
+       "encoding/ascii85",
+       "encoding/asn1",
+       "encoding/base32",
+       "encoding/base64",
+       // "encoding/binary", // complex() doesn't work yet
+       "encoding/csv",
+       // "encoding/gob", // complex() doesn't work yet
+       "encoding/hex",
+       "encoding/json",
+       "encoding/pem",
+       "encoding/xml",
+
+       "errors",
+       "expvar",
+       "flag",
+       "fmt",
+
+       "exp/types",
+       "exp/gotype",
+
+       "go/ast",
+       "go/build",
+       // "go/doc", // variadic parameters don't work yet fully
+       "go/format",
+       "go/parser",
+       "go/printer",
+       "go/scanner",
+       "go/token",
+
+       "hash/adler32",
+       // "hash/crc32", // investigate
+       "hash/crc64",
+       "hash/fnv",
+
+       "image",
+       "image/color",
+       "image/draw",
+       "image/gif",
+       "image/jpeg",
+       "image/png",
+
+       "index/suffixarray",
+
+       "io",
+       // "io/ioutil", // investigate
+
+       "log",
+       "log/syslog",
+
+       "math",
+       // "math/big", // investigate
+       // "math/cmplx", // complex doesn't work yet
+       "math/rand",
+
+       "mime",
+       "mime/multipart",
+
+       // "net", // depends on C files
+       "net/http",
+       "net/http/cgi",
+       // "net/http/fcgi", // investigate
+       "net/http/httptest",
+       "net/http/httputil",
+       // "net/http/pprof", // investigate
+       "net/mail",
+       // "net/rpc", // investigate
+       "net/rpc/jsonrpc",
+       "net/smtp",
+       "net/textproto",
+       "net/url",
+
+       // "path", // variadic parameters don't work yet fully
+       // "path/filepath", // investigate
+
+       // "reflect", // investigate
+
+       "regexp",
+       "regexp/syntax",
+
+       "runtime",
+       // "runtime/cgo", // import "C"
+       "runtime/debug",
+       "runtime/pprof",
+
+       "sort",
+       // "strconv", // investigate
+       "strings",
+
+       // "sync", // platform-specific files
+       // "sync/atomic", // platform-specific files
+
+       // "syscall", // platform-specific files
+
+       "testing",
+       "testing/iotest",
+       "testing/quick",
+
+       "text/scanner",
+       "text/tabwriter",
+       // "text/template", // variadic parameters don't work yet fully
+       // "text/template/parse", // variadic parameters don't work yet fully
+
+       // "time", // platform-specific files
+       "unicode",
+       "unicode/utf16",
+       "unicode/utf8",
 }
 
 func Test(t *testing.T) {
        for _, test := range tests {
-               runTest(t, test.path, test.pkg)
+               runTest(t, test)
        }
 }
index ba8a51f135e3c0c5a32c260f399aaab1e5ea544e..6a6f477e7807a72a6c3b9ed9b9bdcf960c86e0e8 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 p
+package test1
 
 func _() {
        // the scope of a local type declaration starts immediately after the type name
index a08dcae0d5f84f3ae219eeafb324532bcf5ed212..8a5c9dc7a86a88ad7ef4bacc3997f68709dc8296 100644 (file)
@@ -450,7 +450,7 @@ func (c *Collator) keyFromElems(buf *Buffer, ws []colElem) {
                }
                // Derive the quaternary weights from the options and other levels.
                // Note that we represent maxQuaternary as 0xFF. The first byte of the
-               // representation of a primary weight is always smaller than 0xFF,
+               // representation of a primary weight is always smaller than 0xFF,
                // so using this single byte value will compare correctly.
                if Quaternary <= c.Strength && c.Alternate >= AltShifted {
                        if c.Alternate == AltShiftTrimmed {
index 1300d0a6dd812afc4c131c8476ddcfda61cf0eb7..af2d0c64d1113326671e1ba004095d9f4e4e46a1 100644 (file)
@@ -36,7 +36,7 @@ type checker struct {
 //
 // TODO(gri) This is very similar to the declare function in go/parser; it
 // is only used to associate methods with their respective receiver base types.
-// In a future version, it might be simpler and cleaner do to all the resolution
+// In a future version, it might be simpler and cleaner to do all the resolution
 // in the type-checking phase. It would simplify the parser, AST, and also
 // reduce some amount of code duplication.
 //
@@ -188,14 +188,13 @@ func (check *checker) object(obj *ast.Object, cycleOk bool) {
 
        case ast.Fun:
                fdecl := obj.Decl.(*ast.FuncDecl)
-               if fdecl.Recv != nil {
-                       // This will ensure that the method base type is
-                       // type-checked
-                       check.collectFields(token.FUNC, fdecl.Recv, true)
-               }
+               check.collectParams(fdecl.Recv, false) // ensure method base is type-checked
                ftyp := check.typ(fdecl.Type, cycleOk).(*Signature)
                obj.Type = ftyp
-               check.function(ftyp, fdecl.Body)
+               // functions implemented elsewhere (say in assembly) have no body
+               if fdecl.Body != nil {
+                       check.function(ftyp, fdecl.Body)
+               }
 
        default:
                panic("unreachable")
@@ -355,12 +354,19 @@ func check(fset *token.FileSet, pkg *ast.Package, errh func(token.Pos, string),
        check.mapf = f
        check.initexprs = make(map[*ast.ValueSpec][]ast.Expr)
 
-       // handle bailouts
+       // handle panics
        defer func() {
-               if p := recover(); p != nil {
-                       _ = p.(bailout) // re-panic if not a bailout
+               switch p := recover().(type) {
+               case nil:
+                       // normal return - nothing to do
+               case bailout:
+                       // early exit
+                       err = check.firsterr
+               default:
+                       // unexpected panic: don't crash clients
+                       // panic(p) // enable for debugging
+                       err = fmt.Errorf("types.check internal error: %v", p)
                }
-               err = check.firsterr
        }()
 
        // determine missing constant initialization expressions
index bd60f5b64844704452d08490281a7b8cfd2ee053..9a80278d23c8e742f6c13fe052c2ef5ec853e4bb 100644 (file)
@@ -49,6 +49,7 @@ var tests = []struct {
        {"decls0", []string{"testdata/decls0.src"}},
        {"decls1", []string{"testdata/decls1.src"}},
        {"decls2", []string{"testdata/decls2a.src", "testdata/decls2b.src"}},
+       {"decls3", []string{"testdata/decls3.src"}},
        {"const0", []string{"testdata/const0.src"}},
        {"expr0", []string{"testdata/expr0.src"}},
        {"expr1", []string{"testdata/expr1.src"}},
index 55010407778dd8777aae8108f26c9a96a4604f80..c678e4749b0c9df84e8fb97a0ff43355f36621ed 100644 (file)
@@ -278,7 +278,7 @@ func isRepresentableConst(x interface{}, as BasicKind) bool {
                return as == String || as == UntypedString
 
        case nilType:
-               return as == UntypedNil
+               return as == UntypedNil || as == UnsafePointer
 
        default:
                unreachable()
index 58a33d0548e8fabcbd984a8931bb8d82e665f229..e1f627b98f27baaeda54275266ac3fd4c42ecdbb 100644 (file)
@@ -12,75 +12,131 @@ import (
        "strconv"
 )
 
-// TODO(gri)
+// TODO(gri) Cleanups
 // - don't print error messages referring to invalid types (they are likely spurious errors)
 // - simplify invalid handling: maybe just use Typ[Invalid] as marker, get rid of invalid Mode for values?
 // - rethink error handling: should all callers check if x.mode == valid after making a call?
+// - at the moment, iota is passed around almost everywhere - in many places we know it cannot be used
 
-func (check *checker) tag(field *ast.Field) string {
-       if t := field.Tag; t != nil {
-               assert(t.Kind == token.STRING)
-               if tag, err := strconv.Unquote(t.Value); err == nil {
-                       return tag
+// TODO(gri) API issues
+// - clients need access to result type information (tuples)
+// - clients need access to constant values
+// - clients need access to built-in type information
+
+// TODO(gri) Bugs
+// - expression hints are (correctly) used untyped for composite literal components, but also
+//   in possibly overlapping use as hints for shift expressions - investigate
+
+func (check *checker) collectParams(list *ast.FieldList, variadicOk bool) (params ObjList, isVariadic bool) {
+       if list == nil {
+               return
+       }
+       var last *ast.Object
+       for i, field := range list.List {
+               ftype := field.Type
+               if t, _ := ftype.(*ast.Ellipsis); t != nil {
+                       ftype = t.Elt
+                       if variadicOk && i == len(list.List)-1 {
+                               isVariadic = true
+                       } else {
+                               check.invalidAST(field.Pos(), "... not permitted")
+                               // ok to continue
+                       }
+               }
+               // the parser ensures that f.Tag is nil and we don't
+               // care if a constructed AST contains a non-nil tag
+               typ := check.typ(ftype, true)
+               if len(field.Names) > 0 {
+                       // named parameter
+                       for _, name := range field.Names {
+                               obj := name.Obj
+                               obj.Type = typ
+                               params = append(params, obj)
+                               last = obj
+                       }
+               } else {
+                       // anonymous parameter
+                       obj := ast.NewObj(ast.Var, "")
+                       obj.Type = typ
+                       params = append(params, obj)
+                       last = obj
                }
-               check.invalidAST(t.Pos(), "incorrect tag syntax: %q", t.Value)
        }
-       return ""
+       // For a variadic function, change the last parameter's object type
+       // from T to []T (this is the type used inside the function), but
+       // keep a copy of the object with the original type T in the params
+       // list (this is the externally visible type).
+       if isVariadic {
+               // if isVariadic is set, last must exist and len(params) > 0
+               copy := *last
+               last.Type = &Slice{Elt: last.Type.(Type)}
+               params[len(params)-1] = &copy
+       }
+       return
 }
 
-// collectFields collects interface methods (tok = token.INTERFACE), and function arguments/results (tok = token.FUNC).
-func (check *checker) collectFields(tok token.Token, list *ast.FieldList, cycleOk bool) (fields ObjList, tags []string, isVariadic bool) {
-       if list != nil {
-               for _, field := range list.List {
-                       ftype := field.Type
-                       if t, ok := ftype.(*ast.Ellipsis); ok {
-                               ftype = t.Elt
-                               isVariadic = true
+func (check *checker) collectMethods(list *ast.FieldList) (methods ObjList) {
+       if list == nil {
+               return
+       }
+       for _, f := range list.List {
+               typ := check.typ(f.Type, len(f.Names) > 0) // cycles are not ok for embedded interfaces
+               // the parser ensures that f.Tag is nil and we don't
+               // care if a constructed AST contains a non-nil tag
+               if len(f.Names) > 0 {
+                       // methods (the parser ensures that there's only one
+                       // and we don't care if a constructed AST has more)
+                       if _, ok := typ.(*Signature); !ok {
+                               check.invalidAST(f.Type.Pos(), "%s is not a method signature", typ)
+                               continue
                        }
-                       typ := check.typ(ftype, cycleOk)
-                       tag := check.tag(field)
-                       if len(field.Names) > 0 {
-                               // named fields
-                               for _, name := range field.Names {
-                                       obj := name.Obj
-                                       obj.Type = typ
-                                       fields = append(fields, obj)
-                                       if tok == token.STRUCT {
-                                               tags = append(tags, tag)
-                                       }
-                               }
-                       } else {
-                               // anonymous field
-                               switch tok {
-                               case token.FUNC:
-                                       obj := ast.NewObj(ast.Var, "")
-                                       obj.Type = typ
-                                       fields = append(fields, obj)
-                               case token.INTERFACE:
-                                       utyp := underlying(typ)
-                                       if typ, ok := utyp.(*Interface); ok {
-                                               // TODO(gri) This is not good enough. Check for double declarations!
-                                               fields = append(fields, typ.Methods...)
-                                       } else if utyp != Typ[Invalid] {
-                                               // if utyp is invalid, don't complain (the root cause was reported before)
-                                               check.errorf(ftype.Pos(), "interface contains embedded non-interface type")
-                                       }
-                               default:
-                                       panic("unreachable")
-                               }
+                       for _, name := range f.Names {
+                               obj := name.Obj
+                               obj.Type = typ
+                               methods = append(methods, obj)
+                       }
+               } else {
+                       // embedded interface
+                       utyp := underlying(typ)
+                       if ityp, ok := utyp.(*Interface); ok {
+                               methods = append(methods, ityp.Methods...)
+                       } else if utyp != Typ[Invalid] {
+                               // if utyp is invalid, don't complain (the root cause was reported before)
+                               check.errorf(f.Type.Pos(), "%s is not an interface type", typ)
                        }
                }
        }
+       // check for double declarations
+       methods.Sort()
+       prev := ""
+       for _, obj := range methods {
+               if obj.Name == prev {
+                       check.errorf(list.Pos(), "multiple methods named %s", prev)
+                       return // keep multiple entries, lookup will only return the first entry
+               }
+       }
        return
 }
 
-func (check *checker) collectStructFields(list *ast.FieldList, cycleOk bool) (fields []*StructField) {
+func (check *checker) tag(t *ast.BasicLit) string {
+       if t != nil {
+               if t.Kind == token.STRING {
+                       if val, err := strconv.Unquote(t.Value); err == nil {
+                               return val
+                       }
+               }
+               check.invalidAST(t.Pos(), "incorrect tag syntax: %q", t.Value)
+       }
+       return ""
+}
+
+func (check *checker) collectFields(list *ast.FieldList, cycleOk bool) (fields []*StructField) {
        if list == nil {
                return
        }
        for _, f := range list.List {
                typ := check.typ(f.Type, cycleOk)
-               tag := check.tag(f)
+               tag := check.tag(f.Tag)
                if len(f.Names) > 0 {
                        // named fields
                        for _, name := range f.Names {
@@ -90,9 +146,9 @@ func (check *checker) collectStructFields(list *ast.FieldList, cycleOk bool) (fi
                        // anonymous field
                        switch t := deref(typ).(type) {
                        case *Basic:
-                               fields = append(fields, &StructField{t.Name, t, tag, true})
+                               fields = append(fields, &StructField{t.Name, typ, tag, true})
                        case *NamedType:
-                               fields = append(fields, &StructField{t.Obj.Name, t, tag, true})
+                               fields = append(fields, &StructField{t.Obj.Name, typ, tag, true})
                        default:
                                if typ != Typ[Invalid] {
                                        check.invalidAST(f.Type.Pos(), "anonymous field type %s must be named", typ)
@@ -115,9 +171,6 @@ var unaryOpPredicates = opPredicates{
 func (check *checker) op(m opPredicates, x *operand, op token.Token) bool {
        if pred := m[op]; pred != nil {
                if !pred(x.typ) {
-                       // TODO(gri) better error message for <-x where x is a send-only channel
-                       //           (<- is defined but not permitted). Special-case here or
-                       //           handle higher up.
                        check.invalidOp(x.pos(), "operator %s not defined for %s", op, x)
                        return false
                }
@@ -173,7 +226,7 @@ func (check *checker) unary(x *operand, op token.Token) {
                }
                // Typed constants must be representable in
                // their type after each constant operation.
-               check.isRepresentable(x, x.typ.(*Basic))
+               check.isRepresentable(x, underlying(x.typ).(*Basic))
                return
        }
 
@@ -424,34 +477,107 @@ func (check *checker) binary(x, y *operand, op token.Token, hint Type) {
 }
 
 // index checks an index expression for validity. If length >= 0, it is the upper
-// bound for the index. The result is a valid integer constant, or nil.
+// bound for the index. The result is a valid index >= 0, or a negative value.
 //
-func (check *checker) index(index ast.Expr, length int64, iota int) interface{} {
+func (check *checker) index(index ast.Expr, length int64, iota int) int64 {
        var x operand
 
        check.expr(&x, index, nil, iota)
        if !x.isInteger() {
                check.errorf(x.pos(), "index %s must be integer", &x)
-               return nil
+               return -1
        }
        if x.mode != constant {
-               return nil // we cannot check more
+               return -1 // we cannot check more
        }
-       // x.mode == constant and the index value must be >= 0
-       if isNegConst(x.val) {
+       // The spec doesn't require int64 indices, but perhaps it should.
+       i, ok := x.val.(int64)
+       if !ok {
+               check.errorf(x.pos(), "stupid index %s", &x)
+               return -1
+       }
+       if i < 0 {
                check.errorf(x.pos(), "index %s must not be negative", &x)
-               return nil
+               return -1
        }
-       // x.val >= 0
-       if length >= 0 && compareConst(x.val, length, token.GEQ) {
+       if length >= 0 && i >= length {
                check.errorf(x.pos(), "index %s is out of bounds (>= %d)", &x, length)
-               return nil
+               return -1
+       }
+
+       return i
+}
+
+// indexElts checks the elements (elts) of an array or slice composite literal
+// against the literals element type (typ), and the element indices against
+// the literal length if known (length >= 0). It returns the length of the
+// literal (maximum index value + 1).
+//
+func (check *checker) indexedElts(elts []ast.Expr, typ Type, length int64, iota int) int64 {
+       visited := make(map[int64]bool, len(elts))
+       var index, max int64
+       for _, e := range elts {
+               // determine and check index
+               validIndex := false
+               eval := e
+               if kv, _ := e.(*ast.KeyValueExpr); kv != nil {
+                       if i := check.index(kv.Key, length, iota); i >= 0 {
+                               index = i
+                               validIndex = true
+                       }
+                       eval = kv.Value
+               } else if length >= 0 && index >= length {
+                       check.errorf(e.Pos(), "index %d is out of bounds (>= %d)", index, length)
+               } else {
+                       validIndex = true
+               }
+
+               // if we have a valid index, check for duplicate entries
+               if validIndex {
+                       if visited[index] {
+                               check.errorf(e.Pos(), "duplicate index %d in array or slice literal", index)
+                       }
+                       visited[index] = true
+               }
+               index++
+               if index > max {
+                       max = index
+               }
+
+               // check element against composite literal element type
+               var x operand
+               check.expr(&x, eval, typ, iota)
+               if !x.isAssignable(typ) {
+                       check.errorf(x.pos(), "cannot use %s as %s value in array or slice literal", &x, typ)
+               }
+       }
+       return max
+}
+
+func (check *checker) argument(sig *Signature, i int, arg ast.Expr) {
+       var par *ast.Object
+       if n := len(sig.Params); i < n {
+               par = sig.Params[i]
+       } else if sig.IsVariadic {
+               par = sig.Params[n-1]
+       } else {
+               check.errorf(arg.Pos(), "too many arguments")
+               return
        }
 
-       return x.val
+       // TODO(gri) deal with ... last argument
+       var z, x operand
+       z.mode = variable
+       z.expr = nil            // TODO(gri) can we do better here?
+       z.typ = par.Type.(Type) // TODO(gri) should become something like checkObj(&z, ...) eventually
+       check.expr(&x, arg, z.typ, -1)
+       if x.mode == invalid {
+               return // ignore this argument
+       }
+       check.assignOperand(&z, &x)
 }
 
-func (check *checker) callRecord(x *operand) {
+func (check *checker) recordType(x *operand) {
        if x.mode != invalid {
                check.mapf(x.expr, x.typ)
        }
@@ -470,7 +596,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
        }
 
        if check.mapf != nil {
-               defer check.callRecord(x)
+               defer check.recordType(x)
        }
 
        switch e := e.(type) {
@@ -527,7 +653,10 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
                x.typ = obj.Type.(Type)
 
        case *ast.Ellipsis:
-               unimplemented()
+               // ellipses are handled explictly where they are legal
+               // (array composite literals and parameter lists)
+               check.errorf(e.Pos(), "invalid use of '...'")
+               goto Error
 
        case *ast.BasicLit:
                x.setConst(e.Kind, e.Value)
@@ -537,27 +666,146 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
                }
 
        case *ast.FuncLit:
-               x.mode = value
-               x.typ = check.typ(e.Type, false)
-               // TODO(gri) handle errors (e.g. x.typ is not a *Signature)
-               check.function(x.typ.(*Signature), e.Body)
+               if typ, ok := check.typ(e.Type, false).(*Signature); ok {
+                       x.mode = value
+                       x.typ = typ
+                       check.function(typ, e.Body)
+               } else {
+                       check.invalidAST(e.Pos(), "invalid function literal %s", e)
+                       goto Error
+               }
 
        case *ast.CompositeLit:
-               // TODO(gri)
-               //      - determine element type if nil
-               //      - deal with map elements
-               var typ Type
+               typ := hint
+               openArray := false
                if e.Type != nil {
-                       // TODO(gri) Fix this - just to get going for now
-                       typ = check.typ(e.Type, false)
+                       // [...]T array types may only appear with composite literals.
+                       // Check for them here so we don't have to handle ... in general.
+                       typ = nil
+                       if atyp, _ := e.Type.(*ast.ArrayType); atyp != nil && atyp.Len != nil {
+                               if ellip, _ := atyp.Len.(*ast.Ellipsis); ellip != nil && ellip.Elt == nil {
+                                       // We have an "open" [...]T array type.
+                                       // Create a new ArrayType with unknown length (-1)
+                                       // and finish setting it up after analyzing the literal.
+                                       typ = &Array{Len: -1, Elt: check.typ(atyp.Elt, cycleOk)}
+                                       openArray = true
+                               }
+                       }
+                       if typ == nil {
+                               typ = check.typ(e.Type, false)
+                       }
                }
-               for _, e := range e.Elts {
-                       var x operand
-                       check.expr(&x, e, hint, iota)
-                       // TODO(gri) check assignment compatibility to element type
+               if typ == nil {
+                       check.errorf(e.Pos(), "missing type in composite literal")
+                       goto Error
+               }
+
+               switch utyp := underlying(deref(typ)).(type) {
+               case *Struct:
+                       if len(e.Elts) == 0 {
+                               break
+                       }
+                       fields := utyp.Fields
+                       if _, ok := e.Elts[0].(*ast.KeyValueExpr); ok {
+                               // all elements must have keys
+                               visited := make([]bool, len(fields))
+                               for _, e := range e.Elts {
+                                       kv, _ := e.(*ast.KeyValueExpr)
+                                       if kv == nil {
+                                               check.errorf(e.Pos(), "mixture of field:value and value elements in struct literal")
+                                               continue
+                                       }
+                                       key, _ := kv.Key.(*ast.Ident)
+                                       if key == nil {
+                                               check.errorf(kv.Pos(), "invalid field name %s in struct literal", kv.Key)
+                                               continue
+                                       }
+                                       i := utyp.fieldIndex(key.Name)
+                                       if i < 0 {
+                                               check.errorf(kv.Pos(), "unknown field %s in struct literal", key.Name)
+                                               continue
+                                       }
+                                       // 0 <= i < len(fields)
+                                       if visited[i] {
+                                               check.errorf(kv.Pos(), "duplicate field name %s in struct literal", key.Name)
+                                               continue
+                                       }
+                                       visited[i] = true
+                                       check.expr(x, kv.Value, nil, iota)
+                                       etyp := fields[i].Type
+                                       if !x.isAssignable(etyp) {
+                                               check.errorf(x.pos(), "cannot use %s as %s value in struct literal", x, etyp)
+                                               continue
+                                       }
+                               }
+                       } else {
+                               // no element must have a key
+                               for i, e := range e.Elts {
+                                       if kv, _ := e.(*ast.KeyValueExpr); kv != nil {
+                                               check.errorf(kv.Pos(), "mixture of field:value and value elements in struct literal")
+                                               continue
+                                       }
+                                       check.expr(x, e, nil, iota)
+                                       if i >= len(fields) {
+                                               check.errorf(x.pos(), "too many values in struct literal")
+                                               break // cannot continue
+                                       }
+                                       // i < len(fields)
+                                       etyp := fields[i].Type
+                                       if !x.isAssignable(etyp) {
+                                               check.errorf(x.pos(), "cannot use %s as an element of type %s in struct literal", x, etyp)
+                                               continue
+                                       }
+                               }
+                               if len(e.Elts) < len(fields) {
+                                       check.errorf(e.Rbrace, "too few values in struct literal")
+                                       // ok to continue
+                               }
+                       }
+
+               case *Array:
+                       n := check.indexedElts(e.Elts, utyp.Elt, utyp.Len, iota)
+                       // if we have an "open" [...]T array, set the length now that we know it
+                       if openArray {
+                               utyp.Len = n
+                       }
+
+               case *Slice:
+                       check.indexedElts(e.Elts, utyp.Elt, -1, iota)
+
+               case *Map:
+                       visited := make(map[interface{}]bool, len(e.Elts))
+                       for _, e := range e.Elts {
+                               kv, _ := e.(*ast.KeyValueExpr)
+                               if kv == nil {
+                                       check.errorf(e.Pos(), "missing key in map literal")
+                                       continue
+                               }
+                               check.expr(x, kv.Key, nil, iota)
+                               if !x.isAssignable(utyp.Key) {
+                                       check.errorf(x.pos(), "cannot use %s as %s key in map literal", x, utyp.Key)
+                                       continue
+                               }
+                               if x.mode == constant {
+                                       if visited[x.val] {
+                                               check.errorf(x.pos(), "duplicate key %s in map literal", x.val)
+                                               continue
+                                       }
+                                       visited[x.val] = true
+                               }
+                               check.expr(x, kv.Value, utyp.Elt, iota)
+                               if !x.isAssignable(utyp.Elt) {
+                                       check.errorf(x.pos(), "cannot use %s as %s value in map literal", x, utyp.Elt)
+                                       continue
+                               }
+                       }
+
+               default:
+                       check.errorf(e.Pos(), "%s is not a valid composite literal type", typ)
+                       goto Error
                }
-               // TODO(gri) this is not correct - leave for now to get going
-               x.mode = variable
+
+               x.mode = variable // TODO(gri) mode is really a value - keep for now to get going
                x.typ = typ
 
        case *ast.ParenExpr:
@@ -604,7 +852,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
                }
                mode, typ := lookupField(x.typ, sel)
                if mode == invalid {
-                       check.invalidOp(e.Pos(), "%s has no field or method %s", x, sel)
+                       check.invalidOp(e.Pos(), "%s has no single field or method %s", x, sel)
                        goto Error
                }
                if x.mode == typexpr {
@@ -617,7 +865,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
                        // the receiver type becomes the type of the first function
                        // argument of the method expression's function type
                        // TODO(gri) at the moment, method sets don't correctly track
-                       // pointer vs non-pointer receivers -> typechecker is too lenient
+                       // pointer vs non-pointer receivers => typechecker is too lenient
                        arg := ast.NewObj(ast.Var, "")
                        arg.Type = x.typ
                        x.mode = value
@@ -659,15 +907,29 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
                        }
                        x.typ = typ.Elt
 
+               case *Pointer:
+                       if typ, _ := underlying(typ.Base).(*Array); typ != nil {
+                               valid = true
+                               length = typ.Len
+                               x.mode = variable
+                               x.typ = typ.Elt
+                       }
+
                case *Slice:
                        valid = true
                        x.mode = variable
                        x.typ = typ.Elt
 
                case *Map:
-                       // TODO(gri) check index type
+                       var key operand
+                       check.expr(&key, e.Index, nil, iota)
+                       if key.mode == invalid || !key.isAssignable(typ.Key) {
+                               check.invalidOp(x.pos(), "cannot use %s as map index of type %s", &key, typ.Key)
+                               goto Error
+                       }
                        x.mode = valueok
                        x.typ = typ.Elt
+                       x.expr = e
                        return
                }
 
@@ -698,7 +960,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
                                }
                                // a sliced string always yields a string value
                                // of the same type as the original string (not
-                               // a constant) even if the string and the indexes
+                               // a constant) even if the string and the indices
                                // are constant
                                x.mode = value
                                // x.typ doesn't change
@@ -713,6 +975,14 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
                        }
                        x.typ = &Slice{Elt: typ.Elt}
 
+               case *Pointer:
+                       if typ, _ := underlying(typ.Base).(*Array); typ != nil {
+                               valid = true
+                               length = typ.Len + 1 // +1 for slice
+                               x.mode = variable
+                               x.typ = &Slice{Elt: typ.Elt}
+                       }
+
                case *Slice:
                        valid = true
                        x.mode = variable
@@ -724,33 +994,55 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
                        goto Error
                }
 
-               var lo interface{} = zeroConst
+               lo := int64(0)
                if e.Low != nil {
                        lo = check.index(e.Low, length, iota)
                }
 
-               var hi interface{}
+               hi := int64(-1)
                if e.High != nil {
                        hi = check.index(e.High, length, iota)
                } else if length >= 0 {
                        hi = length
                }
 
-               if lo != nil && hi != nil && compareConst(lo, hi, token.GTR) {
-                       check.errorf(e.Low.Pos(), "inverted slice range: %v > %v", lo, hi)
+               if lo >= 0 && hi >= 0 && lo > hi {
+                       check.errorf(e.Low.Pos(), "inverted slice range: %d > %d", lo, hi)
                        // ok to continue
                }
 
        case *ast.TypeAssertExpr:
                check.expr(x, e.X, hint, iota)
-               if _, ok := underlying(x.typ).(*Interface); !ok {
-                       check.invalidOp(e.X.Pos(), "non-interface type %s in type assertion", x.typ)
+               if x.mode == invalid {
+                       goto Error
+               }
+               var T *Interface
+               if T, _ = underlying(x.typ).(*Interface); T == nil {
+                       check.invalidOp(x.pos(), "%s is not an interface", x)
+                       goto Error
+               }
+               // x.(type) expressions are handled explicitly in type switches
+               if e.Type == nil {
+                       check.errorf(e.Pos(), "use of .(type) outside type switch")
+                       goto Error
+               }
+               typ := check.typ(e.Type, false)
+               if typ == Typ[Invalid] {
+                       goto Error
+               }
+               if method, wrongType := missingMethod(typ, T); method != nil {
+                       var msg string
+                       if wrongType {
+                               msg = "%s cannot have dynamic type %s (wrong type for method %s)"
+                       } else {
+                               msg = "%s cannot have dynamic type %s (missing method %s)"
+                       }
+                       check.errorf(e.Type.Pos(), msg, x, typ, method.Name)
                        // ok to continue
                }
-               // TODO(gri) some type asserts are compile-time decidable
                x.mode = valueok
                x.expr = e
-               x.typ = check.typ(e.Type, false)
+               x.typ = typ
 
        case *ast.CallExpr:
                check.exprOrType(x, e.Fun, nil, iota, false)
@@ -760,21 +1052,11 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
                        check.conversion(x, e, x.typ, iota)
                } else if sig, ok := underlying(x.typ).(*Signature); ok {
                        // check parameters
-                       // TODO(gri) complete this
-                       // - deal with various forms of calls
-                       // - handle variadic calls
-                       if len(sig.Params) == len(e.Args) {
-                               var z, x operand
-                               z.mode = variable
-                               for i, arg := range e.Args {
-                                       z.expr = nil                      // TODO(gri) can we do better here?
-                                       z.typ = sig.Params[i].Type.(Type) // TODO(gri) should become something like checkObj(&z, ...) eventually
-                                       check.expr(&x, arg, z.typ, iota)
-                                       if x.mode == invalid {
-                                               goto Error
-                                       }
-                                       check.assignOperand(&z, &x)
-                               }
+                       // TODO(gri)
+                       // - deal with single multi-valued function arguments: f(g())
+                       // - variadic functions only partially addressed
+                       for i, arg := range e.Args {
+                               check.argument(sig, i, arg)
                        }
 
                        // determine result
@@ -827,32 +1109,26 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
                check.binary(x, &y, e.Op, hint)
 
        case *ast.KeyValueExpr:
-               unimplemented()
+               // key:value expressions are handled in composite literals
+               check.invalidAST(e.Pos(), "no key:value expected")
+               goto Error
 
        case *ast.ArrayType:
                if e.Len != nil {
-                       var n int64 = -1
-                       if ellip, ok := e.Len.(*ast.Ellipsis); ok {
-                               // TODO(gri) need to check somewhere that [...]T types are only used with composite literals
-                               if ellip.Elt != nil {
-                                       check.invalidAST(ellip.Pos(), "ellipsis only expected")
-                                       // ok to continue
-                               }
-                       } else {
-                               check.expr(x, e.Len, nil, 0)
-                               if x.mode == invalid {
-                                       goto Error
-                               }
-                               if x.mode == constant {
-                                       if i, ok := x.val.(int64); ok && i == int64(int(i)) {
-                                               n = i
-                                       }
-                               }
-                               if n < 0 {
-                                       check.errorf(e.Len.Pos(), "invalid array bound %s", e.Len)
-                                       // ok to continue
-                                       n = 0
+                       check.expr(x, e.Len, nil, iota)
+                       if x.mode == invalid {
+                               goto Error
+                       }
+                       if x.mode != constant {
+                               if x.mode != invalid {
+                                       check.errorf(x.pos(), "array length %s must be constant", x)
                                }
+                               goto Error
+                       }
+                       n, ok := x.val.(int64)
+                       if !ok || n < 0 {
+                               check.errorf(x.pos(), "invalid array length %s", x)
+                               goto Error
                        }
                        x.typ = &Array{Len: n, Elt: check.typ(e.Elt, cycleOk)}
                } else {
@@ -862,19 +1138,17 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
 
        case *ast.StructType:
                x.mode = typexpr
-               x.typ = &Struct{Fields: check.collectStructFields(e.Fields, cycleOk)}
+               x.typ = &Struct{Fields: check.collectFields(e.Fields, cycleOk)}
 
        case *ast.FuncType:
-               params, _, isVariadic := check.collectFields(token.FUNC, e.Params, true)
-               results, _, _ := check.collectFields(token.FUNC, e.Results, true)
+               params, isVariadic := check.collectParams(e.Params, true)
+               results, _ := check.collectParams(e.Results, false)
                x.mode = typexpr
                x.typ = &Signature{Recv: nil, Params: params, Results: results, IsVariadic: isVariadic}
 
        case *ast.InterfaceType:
-               methods, _, _ := check.collectFields(token.INTERFACE, e.Methods, cycleOk)
-               methods.Sort()
                x.mode = typexpr
-               x.typ = &Interface{Methods: methods}
+               x.typ = &Interface{Methods: check.collectMethods(e.Methods)}
 
        case *ast.MapType:
                x.mode = typexpr
@@ -920,10 +1194,7 @@ func (check *checker) expr(x *operand, e ast.Expr, hint Type, iota int) {
        }
 }
 
-// expr is like rawExpr but reports an error if e doesn't represents a type.
-// It returns e's type, or Typ[Invalid] if an error occured.
-//
-func (check *checker) typ(e ast.Expr, cycleOk bool) Type {
+func (check *checker) rawTyp(e ast.Expr, cycleOk, nilOk bool) Type {
        var x operand
        check.rawExpr(&x, e, nil, -1, cycleOk)
        switch x.mode {
@@ -933,8 +1204,27 @@ func (check *checker) typ(e ast.Expr, cycleOk bool) Type {
                check.errorf(x.pos(), "%s used as type", &x)
        case typexpr:
                return x.typ
+       case constant:
+               if nilOk && x.isNil() {
+                       return nil
+               }
+               fallthrough
        default:
                check.errorf(x.pos(), "%s is not a type", &x)
        }
        return Typ[Invalid]
 }
+
+// typOrNil is like rawExpr but reports an error if e doesn't represents a type or the predeclared value nil.
+// It returns e's type, nil, or Typ[Invalid] if an error occured.
+//
+func (check *checker) typOrNil(e ast.Expr, cycleOk bool) Type {
+       return check.rawTyp(e, cycleOk, true)
+}
+
+// typ is like rawExpr but reports an error if e doesn't represents a type.
+// It returns e's type, or Typ[Invalid] if an error occured.
+//
+func (check *checker) typ(e ast.Expr, cycleOk bool) Type {
+       return check.rawTyp(e, cycleOk, false)
+}
index 49ba899d917980d37b573a9ed8678e3fefb62ea1..1a5e5172a8b1073ccf1d1790347c0710e3e3e19f 100644 (file)
@@ -119,21 +119,15 @@ func (x *operand) setConst(tok token.Token, lit string) {
        }
 }
 
-// implements reports whether x implements interface T.
-func (x *operand) implements(T *Interface) bool {
-       if x.mode == invalid {
-               return true // avoid spurious errors
-       }
-
-       unimplemented()
-       return true
-}
-
 // isNil reports whether x is the predeclared nil constant.
 func (x *operand) isNil() bool {
        return x.mode == constant && x.val == nilConst
 }
 
+// TODO(gri) The functions operand.isAssignable, checker.convertUntyped,
+//           checker.isRepresentable, and checker.assignOperand are
+//           overlapping in functionality. Need to simplify and clean up.
+
 // isAssignable reports whether x is assignable to a variable of type T.
 func (x *operand) isAssignable(T Type) bool {
        if x.mode == invalid || T == Typ[Invalid] {
@@ -157,8 +151,10 @@ func (x *operand) isAssignable(T Type) bool {
        }
 
        // T is an interface type and x implements T
-       if Ti, ok := Tu.(*Interface); ok && x.implements(Ti) {
-               return true
+       if Ti, ok := Tu.(*Interface); ok {
+               if m, _ := missingMethod(x.typ, Ti); m == nil {
+                       return true
+               }
        }
 
        // x is a bidirectional channel value, T is a channel
@@ -181,8 +177,18 @@ func (x *operand) isAssignable(T Type) bool {
        }
 
        // x is an untyped constant representable by a value of type T
-       // - this is taken care of in the assignment check
-       // TODO(gri) double-check - isAssignable is used elsewhere
+       // TODO(gri) This is borrowing from checker.convertUntyped and
+       //           checker.isRepresentable. Need to clean up.
+       if isUntyped(Vu) {
+               switch t := Tu.(type) {
+               case *Basic:
+                       return x.mode == constant && isRepresentableConst(x.val, t.Kind)
+               case *Interface:
+                       return x.isNil() || len(t.Methods) == 0
+               case *Pointer, *Signature, *Slice, *Map, *Chan:
+                       return x.isNil()
+               }
+       }
 
        return false
 }
@@ -199,35 +205,50 @@ type lookupResult struct {
        typ  Type
 }
 
-// lookupFieldRecursive is similar to FieldByNameFunc in reflect/type.go
-// TODO(gri): FieldByNameFunc seems more complex - what are we missing?
-func lookupFieldRecursive(list []*NamedType, name string) (res lookupResult) {
-       // visited records the types that have been searched already
-       visited := make(map[Type]bool)
+type embeddedType struct {
+       typ       *NamedType
+       multiples bool // if set, typ is embedded multiple times at the same level
+}
+
+// lookupFieldBreadthFirst searches all types in list for a single entry (field
+// or method) of the given name. If such a field is found, the result describes
+// the field mode and type; otherwise the result mode is invalid.
+// (This function is similar in structure to FieldByNameFunc in reflect/type.go)
+//
+func lookupFieldBreadthFirst(list []embeddedType, name string) (res lookupResult) {
+       // visited records the types that have been searched already.
+       visited := make(map[*NamedType]bool)
 
        // embedded types of the next lower level
-       var next []*NamedType
+       var next []embeddedType
 
-       potentialMatch := func(mode operandMode, typ Type) bool {
-               if res.mode != invalid {
-                       // name appeared multiple times at this level - annihilate
+       // potentialMatch is invoked every time a match is found.
+       potentialMatch := func(multiples bool, mode operandMode, typ Type) bool {
+               if multiples || res.mode != invalid {
+                       // name appeared already at this level - annihilate
                        res.mode = invalid
                        return false
                }
+               // first appearance of name
                res.mode = mode
                res.typ = typ
                return true
        }
 
-       // look for name in all types of this level
+       // Search the current level if there is any work to do and collect
+       // embedded types of the next lower level in the next list.
        for len(list) > 0 {
+               // The res.mode indicates whether we have found a match already
+               // on this level (mode != invalid), or not (mode == invalid).
                assert(res.mode == invalid)
-               for _, typ := range list {
+
+               // start with empty next list (don't waste underlying array)
+               next = next[:0]
+
+               // look for name in all types at this level
+               for _, e := range list {
+                       typ := e.typ
                        if visited[typ] {
-                               // We have seen this type before, at a higher level.
-                               // That higher level shadows the lower level we are
-                               // at now, and either we would have found or not
-                               // found the field before. Ignore this type now.
                                continue
                        }
                        visited[typ] = true
@@ -236,7 +257,7 @@ func lookupFieldRecursive(list []*NamedType, name string) (res lookupResult) {
                        if data := typ.Obj.Data; data != nil {
                                if obj := data.(*ast.Scope).Lookup(name); obj != nil {
                                        assert(obj.Type != nil)
-                                       if !potentialMatch(value, obj.Type.(Type)) {
+                                       if !potentialMatch(e.multiples, value, obj.Type.(Type)) {
                                                return // name collision
                                        }
                                }
@@ -244,21 +265,26 @@ func lookupFieldRecursive(list []*NamedType, name string) (res lookupResult) {
 
                        switch typ := underlying(typ).(type) {
                        case *Struct:
-                               // look for a matching fieldm and collect embedded types
+                               // look for a matching field and collect embedded types
                                for _, f := range typ.Fields {
                                        if f.Name == name {
                                                assert(f.Type != nil)
-                                               if !potentialMatch(variable, f.Type) {
+                                               if !potentialMatch(e.multiples, variable, f.Type) {
                                                        return // name collision
                                                }
                                                continue
                                        }
                                        // Collect embedded struct fields for searching the next
-                                       // lower level, but only if we have not seen a match yet.
+                                       // lower level, but only if we have not seen a match yet
+                                       // (if we have a match it is either the desired field or
+                                       // we have a name collision on the same level; in either
+                                       // case we don't need to look further).
                                        // Embedded fields are always of the form T or *T where
-                                       // T is a named type.
+                                       // T is a named type. If typ appeared multiple times at
+                                       // this level, f.Type appears multiple times at the next
+                                       // level.
                                        if f.IsAnonymous && res.mode == invalid {
-                                               next = append(next, deref(f.Type).(*NamedType))
+                                               next = append(next, embeddedType{deref(f.Type).(*NamedType), e.multiples})
                                        }
                                }
 
@@ -267,7 +293,7 @@ func lookupFieldRecursive(list []*NamedType, name string) (res lookupResult) {
                                for _, obj := range typ.Methods {
                                        if obj.Name == name {
                                                assert(obj.Type != nil)
-                                               if !potentialMatch(value, obj.Type.(Type)) {
+                                               if !potentialMatch(e.multiples, value, obj.Type.(Type)) {
                                                        return // name collision
                                                }
                                        }
@@ -276,17 +302,41 @@ func lookupFieldRecursive(list []*NamedType, name string) (res lookupResult) {
                }
 
                if res.mode != invalid {
-                       // we found a match on this level
+                       // we found a single match on this level
                        return
                }
 
-               // search the next level
-               list = append(list[:0], next...) // don't waste underlying arrays
-               next = next[:0]
+               // No match and no collision so far.
+               // Compute the list to search for the next level.
+               list = list[:0] // don't waste underlying array
+               for _, e := range next {
+                       // Instead of adding the same type multiple times, look for
+                       // it in the list and mark it as multiple if it was added
+                       // before.
+                       // We use a sequential search (instead of a map for next)
+                       // because the lists tend to be small, can easily be reused,
+                       // and explicit search appears to be faster in this case.
+                       if alt := findType(list, e.typ); alt != nil {
+                               alt.multiples = true
+                       } else {
+                               list = append(list, e)
+                       }
+               }
+
        }
+
        return
 }
 
+func findType(list []embeddedType, typ *NamedType) *embeddedType {
+       for i := range list {
+               if p := &list[i]; p.typ == typ {
+                       return p
+               }
+       }
+       return nil
+}
+
 func lookupField(typ Type, name string) (operandMode, Type) {
        typ = deref(typ)
 
@@ -301,17 +351,20 @@ func lookupField(typ Type, name string) (operandMode, Type) {
 
        switch typ := underlying(typ).(type) {
        case *Struct:
-               var list []*NamedType
+               var next []embeddedType
                for _, f := range typ.Fields {
                        if f.Name == name {
                                return variable, f.Type
                        }
                        if f.IsAnonymous {
-                               list = append(list, deref(f.Type).(*NamedType))
+                               // Possible optimization: If the embedded type
+                               // is a pointer to the current type we could
+                               // ignore it.
+                               next = append(next, embeddedType{typ: deref(f.Type).(*NamedType)})
                        }
                }
-               if len(list) > 0 {
-                       res := lookupFieldRecursive(list, name)
+               if len(next) > 0 {
+                       res := lookupFieldBreadthFirst(next, name)
                        return res.mode, res.typ
                }
 
index 503027e2d9877e808db00a78ad387a2c10b0a37c..2c1a99192aa57c1433727de40d00228919404752 100644 (file)
@@ -6,6 +6,8 @@
 
 package types
 
+import "go/ast"
+
 func isNamed(typ Type) bool {
        if _, ok := typ.(*Basic); ok {
                return ok
@@ -247,3 +249,34 @@ func defaultType(typ Type) Type {
        }
        return typ
 }
+
+// missingMethod returns (nil, false) if typ implements T, otherwise
+// it returns the first missing method required by T and whether it
+// is missing or simply has the wrong type.
+//
+func missingMethod(typ Type, T *Interface) (method *ast.Object, wrongType bool) {
+       // TODO(gri): distinguish pointer and non-pointer receivers
+       // an interface type implements T if it has no methods with conflicting signatures
+       // Note: This is stronger than the current spec. Should the spec require this?
+       if ityp, _ := underlying(typ).(*Interface); ityp != nil {
+               for _, m := range T.Methods {
+                       mode, sig := lookupField(ityp, m.Name) // TODO(gri) no need to go via lookupField
+                       if mode != invalid && !isIdentical(sig, m.Type.(Type)) {
+                               return m, true
+                       }
+               }
+               return
+       }
+
+       // a concrete type implements T if it implements all methods of T.
+       for _, m := range T.Methods {
+               mode, sig := lookupField(typ, m.Name)
+               if mode == invalid {
+                       return m, false
+               }
+               if !isIdentical(sig, m.Type.(Type)) {
+                       return m, true
+               }
+       }
+       return
+}
index 4f012499a206dd91037a2f6aceee2a9792c40360..e2c6448debad896f09252d7ba3bbd457ea3c9e7d 100644 (file)
@@ -27,8 +27,8 @@ func (check *checker) assignOperand(z, x *operand) {
        }
 }
 
-// assign1to1 typechecks a single assignment of the form lhs := rhs (if rhs != nil),
-// or lhs := x (if rhs == nil). If decl is set, the lhs operand must be an identifier.
+// assign1to1 typechecks a single assignment of the form lhs = rhs (if rhs != nil),
+// or lhs = x (if rhs == nil). If decl is set, the lhs operand must be an identifier.
 // If its type is not set, it is deduced from the type or value of x. If lhs has a
 // type it is used as a hint when evaluating rhs, if present.
 //
@@ -226,6 +226,38 @@ func (check *checker) stmtList(list []ast.Stmt) {
        }
 }
 
+func (check *checker) call(call *ast.CallExpr) {
+       var x operand
+       check.rawExpr(&x, call, nil, -1, false) // don't check if value is used
+       // TODO(gri) If a builtin is called, the builtin must be valid in statement context.
+}
+
+func (check *checker) multipleDefaults(list []ast.Stmt) {
+       var first ast.Stmt
+       for _, s := range list {
+               var d ast.Stmt
+               switch c := s.(type) {
+               case *ast.CaseClause:
+                       if len(c.List) == 0 {
+                               d = s
+                       }
+               case *ast.CommClause:
+                       if c.Comm == nil {
+                               d = s
+                       }
+               default:
+                       check.invalidAST(s.Pos(), "case/communication clause expected")
+               }
+               if d != nil {
+                       if first != nil {
+                               check.errorf(d.Pos(), "multiple defaults (first at %s)", first.Pos())
+                       } else {
+                               first = d
+                       }
+               }
+       }
+}
+
 // stmt typechecks statement s.
 func (check *checker) stmt(s ast.Stmt) {
        switch s := s.(type) {
@@ -265,7 +297,7 @@ func (check *checker) stmt(s ast.Stmt) {
                }
                check.rawExpr(&x, s.X, nil, -1, false)
                if x.mode == typexpr {
-                       check.errorf(x.pos(), "%s is not an expression", x)
+                       check.errorf(x.pos(), "%s is not an expression", &x)
                }
 
        case *ast.SendStmt:
@@ -347,10 +379,10 @@ func (check *checker) stmt(s ast.Stmt) {
                }
 
        case *ast.GoStmt:
-               unimplemented()
+               check.call(s.Call)
 
        case *ast.DeferStmt:
-               unimplemented()
+               check.call(s.Call)
 
        case *ast.ReturnStmt:
                sig := check.functypes[len(check.functypes)-1]
@@ -403,31 +435,122 @@ func (check *checker) stmt(s ast.Stmt) {
                        x.typ = Typ[UntypedBool]
                        x.val = true
                }
+
+               check.multipleDefaults(s.Body.List)
                for _, s := range s.Body.List {
-                       if clause, ok := s.(*ast.CaseClause); ok {
-                               for _, expr := range clause.List {
-                                       var y operand
-                                       check.expr(&y, expr, nil, -1)
-                                       // TODO(gri) x and y must be comparable
-                               }
-                               check.stmtList(clause.Body)
-                       } else {
-                               check.errorf(s.Pos(), "invalid AST: case clause expected")
+                       clause, _ := s.(*ast.CaseClause)
+                       if clause == nil {
+                               continue // error reported before
                        }
+                       for _, expr := range clause.List {
+                               var y operand
+                               check.expr(&y, expr, nil, -1)
+                               // TODO(gri) x and y must be comparable
+                       }
+                       check.stmtList(clause.Body)
                }
 
        case *ast.TypeSwitchStmt:
-               unimplemented()
+               check.optionalStmt(s.Init)
+
+               // A type switch guard must be of the form:
+               //
+               //     TypeSwitchGuard = [ identifier ":=" ] PrimaryExpr "." "(" "type" ")" .
+               //
+               // The parser is checking syntactic correctness;
+               // remaining syntactic errors are considered AST errors here.
+               // TODO(gri) better factoring of error handling (invalid ASTs)
+               //
+               var lhs *ast.Object // lhs identifier object or nil
+               var rhs ast.Expr
+               switch guard := s.Assign.(type) {
+               case *ast.ExprStmt:
+                       rhs = guard.X
+               case *ast.AssignStmt:
+                       if len(guard.Lhs) != 1 || guard.Tok != token.DEFINE || len(guard.Rhs) != 1 {
+                               check.invalidAST(s.Pos(), "incorrect form of type switch guard")
+                               return
+                       }
+                       ident, _ := guard.Lhs[0].(*ast.Ident)
+                       if ident == nil {
+                               check.invalidAST(s.Pos(), "incorrect form of type switch guard")
+                               return
+                       }
+                       lhs = ident.Obj
+                       rhs = guard.Rhs[0]
+               default:
+                       check.invalidAST(s.Pos(), "incorrect form of type switch guard")
+                       return
+               }
+
+               // rhs must be of the form: expr.(type) and expr must be an interface
+               expr, _ := rhs.(*ast.TypeAssertExpr)
+               if expr == nil || expr.Type != nil {
+                       check.invalidAST(s.Pos(), "incorrect form of type switch guard")
+                       return
+               }
+               var x operand
+               check.expr(&x, expr.X, nil, -1)
+               if x.mode == invalid {
+                       return
+               }
+               var T *Interface
+               if T, _ = underlying(x.typ).(*Interface); T == nil {
+                       check.errorf(x.pos(), "%s is not an interface", &x)
+                       return
+               }
+
+               check.multipleDefaults(s.Body.List)
+               for _, s := range s.Body.List {
+                       clause, _ := s.(*ast.CaseClause)
+                       if clause == nil {
+                               continue // error reported before
+                       }
+                       // Check each type in this type switch case.
+                       var typ Type
+                       for _, expr := range clause.List {
+                               typ = check.typOrNil(expr, false)
+                               if typ != nil && typ != Typ[Invalid] {
+                                       if method, wrongType := missingMethod(typ, T); method != nil {
+                                               var msg string
+                                               if wrongType {
+                                                       msg = "%s cannot have dynamic type %s (wrong type for method %s)"
+                                               } else {
+                                                       msg = "%s cannot have dynamic type %s (missing method %s)"
+                                               }
+                                               check.errorf(expr.Pos(), msg, &x, typ, method.Name)
+                                               // ok to continue
+                                       }
+                               }
+                       }
+                       // If lhs exists, set its type for each clause.
+                       if lhs != nil {
+                               // In clauses with a case listing exactly one type, the variable has that type;
+                               // otherwise, the variable has the type of the expression in the TypeSwitchGuard.
+                               if len(clause.List) != 1 || typ == nil {
+                                       typ = x.typ
+                               }
+                               lhs.Type = typ
+                       }
+                       check.stmtList(clause.Body)
+               }
+
+               // There is only one object (lhs) associated with a lhs identifier, but that object
+               // assumes different types for different clauses. Set it to nil when we are done so
+               // that the type cannot be used by mistake.
+               if lhs != nil {
+                       lhs.Type = nil
+               }
 
        case *ast.SelectStmt:
+               check.multipleDefaults(s.Body.List)
                for _, s := range s.Body.List {
-                       c, ok := s.(*ast.CommClause)
-                       if !ok {
-                               check.invalidAST(s.Pos(), "communication clause expected")
-                               continue
+                       clause, _ := s.(*ast.CommClause)
+                       if clause == nil {
+                               continue // error reported before
                        }
-                       check.optionalStmt(c.Comm) // TODO(gri) check correctness of c.Comm (must be Send/RecvStmt)
-                       check.stmtList(c.Body)
+                       check.optionalStmt(clause.Comm) // TODO(gri) check correctness of c.Comm (must be Send/RecvStmt)
+                       check.stmtList(clause.Body)
                }
 
        case *ast.ForStmt:
@@ -443,7 +566,79 @@ func (check *checker) stmt(s ast.Stmt) {
                check.stmt(s.Body)
 
        case *ast.RangeStmt:
-               unimplemented()
+               // check expression to iterate over
+               decl := s.Tok == token.DEFINE
+               var x operand
+               check.expr(&x, s.X, nil, -1)
+               if x.mode == invalid {
+                       // if we don't have a declaration, we can still check the loop's body
+                       if !decl {
+                               check.stmt(s.Body)
+                       }
+                       return
+               }
+
+               // determine key/value types
+               var key, val Type
+               switch typ := underlying(x.typ).(type) {
+               case *Basic:
+                       if isString(typ) {
+                               key = Typ[UntypedInt]
+                               val = Typ[UntypedRune]
+                       }
+               case *Array:
+                       key = Typ[UntypedInt]
+                       val = typ.Elt
+               case *Slice:
+                       key = Typ[UntypedInt]
+                       val = typ.Elt
+               case *Pointer:
+                       if typ, _ := underlying(typ.Base).(*Array); typ != nil {
+                               key = Typ[UntypedInt]
+                               val = typ.Elt
+                       }
+               case *Map:
+                       key = typ.Key
+                       val = typ.Elt
+               case *Chan:
+                       key = typ.Elt
+                       if typ.Dir&ast.RECV == 0 {
+                               check.errorf(x.pos(), "cannot range over send-only channel %s", &x)
+                               // ok to continue
+                       }
+                       if s.Value != nil {
+                               check.errorf(s.Value.Pos(), "iteration over %s permits only one iteration variable", &x)
+                               // ok to continue
+                       }
+               }
+
+               if key == nil {
+                       check.errorf(x.pos(), "cannot range over %s", &x)
+                       // if we don't have a declaration, we can still check the loop's body
+                       if !decl {
+                               check.stmt(s.Body)
+                       }
+                       return
+               }
+
+               // check assignment to/declaration of iteration variables
+               // TODO(gri) The error messages/positions are not great here,
+               //           they refer to the expression in the range clause.
+               //           Should give better messages w/o too much code
+               //           duplication (assignment checking).
+               if s.Key != nil {
+                       x.typ = key
+                       check.assign1to1(s.Key, nil, &x, decl, -1)
+               } else {
+                       check.invalidAST(s.Pos(), "range clause requires index iteration variable")
+                       // ok to continue
+               }
+               if s.Value != nil {
+                       x.typ = val
+                       check.assign1to1(s.Value, nil, &x, decl, -1)
+               }
+
+               check.stmt(s.Body)
 
        default:
                check.errorf(s.Pos(), "invalid statement")
index 3537a9e55832be4c6fdcdd6d6b633e46a8f38279..70623c6166fe07d0baa9cf160b8cf60e2a84b989 100644 (file)
@@ -40,8 +40,17 @@ type (
 )
 
 
+// invalid array types
 type (
-       p1 pi /* ERROR "no field or method foo" */ .foo
+       iA0 [... /* ERROR "invalid use of '...'" */ ]byte
+       iA1 [1 /* ERROR "invalid array length" */ <<100]int
+       iA2 [- /* ERROR "invalid array length" */ 1]complex128
+       iA3 ["foo" /* ERROR "invalid array length" */ ]string
+)
+
+
+type (
+       p1 pi /* ERROR "no single field or method foo" */ .foo
        p2 unsafe.Pointer
 )
 
@@ -131,7 +140,7 @@ type (
                m1(I5)
        }
        I6 interface {
-               S0 /* ERROR "non-interface" */
+               S0 /* ERROR "not an interface" */
        }
        I7 interface {
                I1
index 16da045ef29d378fc42f5673d91257f6ef338997..be927091c1b0842b8600ad5cd05e997cbfe264d2 100644 (file)
@@ -73,7 +73,7 @@ var (
 
 // Various more complex expressions
 var (
-       u1 = x /* ERROR "non-interface type" */ .(int)
+       u1 = x /* ERROR "not an interface" */ .(int)
        u2 = iface.([]int)
        u3 = iface.(a /* ERROR "not a type" */ )
        u4, ok = iface.(int)
index 890f5e993859a04b417e9b26d590ae5e72a30e31..a5ea4d2b82ee471a5cfb12fe85abecec2e80aadb 100644 (file)
@@ -6,43 +6,43 @@
 
 package expr3
 
-// TODO(gri) Move the code below into function "shifts" once we check
-//           declarations with initilizations inside functions.
-var (
-       i0 int
-       u0 uint
-)
-
-var (
-       v0 = 1<<0
-       v1 = 1<<i0 /* ERROR "must be unsigned" */
-       v2 = 1<<u0
-       v3 = 1<<"foo" /* ERROR "must be unsigned" */
-       v4 = 1<<- /* ERROR "stupid shift" */ 1
-       v5 = 1<<1025 /* ERROR "stupid shift" */
-       v6 = 1 /* ERROR "overflows" */ <<100
-
-       v10 uint = 1 << 0
-       v11 uint = 1 << u0
-       v12 float32 = 1 /* ERROR "must be integer" */ << u0
-)
-
-// TODO(gri) enable commented out tests below.
-
-// from the spec
-var (
-       s uint = 33
-       i = 1<<s           // 1 has type int
-       j int32 = 1<<s     // 1 has type int32; j == 0
-       k = uint64(1<<s)   // 1 has type uint64; k == 1<<33
-       m int = 1.0<<s     // 1.0 has type int
-//     n = 1.0<<s != 0    // 1.0 has type int; n == false if ints are 32bits in size
-       o = 1<<s == 2<<s   // 1 and 2 have type int; o == true if ints are 32bits in size
-//     p = 1<<s == 1 /* ERROR "overflows" */ <<33  // illegal if ints are 32bits in size: 1 has type int, but 1<<33 overflows int
-       u = 1.0 /* ERROR "must be integer" */ <<s         // illegal: 1.0 has type float64, cannot shift
-       v float32 = 1 /* ERROR "must be integer" */ <<s   // illegal: 1 has type float32, cannot shift
-       w int64 = 1.0<<33  // 1.0<<33 is a constant shift expression
-)
+func shifts1() {
+       var (
+               i0 int
+               u0 uint
+       )
+
+       var (
+               v0 = 1<<0
+               v1 = 1<<i0 /* ERROR "must be unsigned" */
+               v2 = 1<<u0
+               v3 = 1<<"foo" /* ERROR "must be unsigned" */
+               v4 = 1<<- /* ERROR "stupid shift" */ 1
+               v5 = 1<<1025 /* ERROR "stupid shift" */
+               v6 = 1 /* ERROR "overflows" */ <<100
+
+               v10 uint = 1 << 0
+               v11 uint = 1 << u0
+               v12 float32 = 1 /* ERROR "must be integer" */ << u0
+       )
+}
+
+func shifts2() {
+       // TODO(gri) enable commented out tests below.
+       var (
+               s uint = 33
+               i = 1<<s           // 1 has type int
+               j int32 = 1<<s     // 1 has type int32; j == 0
+               k = uint64(1<<s)   // 1 has type uint64; k == 1<<33
+               m int = 1.0<<s     // 1.0 has type int
+       //      n = 1.0<<s != 0    // 1.0 has type int; n == false if ints are 32bits in size
+               o = 1<<s == 2<<s   // 1 and 2 have type int; o == true if ints are 32bits in size
+       //      p = 1<<s == 1 /* ERROR "overflows" */ <<33  // illegal if ints are 32bits in size: 1 has type int, but 1<<33 overflows int
+               u = 1.0 /* ERROR "must be integer" */ <<s         // illegal: 1.0 has type float64, cannot shift
+               v float32 = 1 /* ERROR "must be integer" */ <<s   // illegal: 1 has type float32, cannot shift
+               w int64 = 1.0<<33  // 1.0<<33 is a constant shift expression
+       )
+}
 
 // TODO(gri) The error messages below depond on adjusting the spec
 //           to reflect what gc is doing at the moment (the spec
@@ -67,11 +67,24 @@ func indexes() {
        a1 = a /* ERROR "cannot assign" */ [1] 
        _ = a[9]
        _ = a[10 /* ERROR "index .* out of bounds" */ ]
+       _ = a[1 /* ERROR "stupid index" */ <<100]
        _ = a[10:]
        _ = a[:10]
        _ = a[10:10]
        _ = a[11 /* ERROR "index .* out of bounds" */ :]
        _ = a[: 11 /* ERROR "index .* out of bounds" */ ]
+       _ = a[: 1 /* ERROR "stupid index" */ <<100]
+
+       pa := &a
+       _ = pa[9]
+       _ = pa[10 /* ERROR "index .* out of bounds" */ ]
+       _ = pa[1 /* ERROR "stupid index" */ <<100]
+       _ = pa[10:]
+       _ = pa[:10]
+       _ = pa[10:10]
+       _ = pa[11 /* ERROR "index .* out of bounds" */ :]
+       _ = pa[: 11 /* ERROR "index .* out of bounds" */ ]
+       _ = pa[: 1 /* ERROR "stupid index" */ <<100]
 
        var b [0]int
        _ = b[0 /* ERROR "index .* out of bounds" */ ]
@@ -88,11 +101,9 @@ func indexes() {
        _ = s[1 : 2]
        _ = s[2 /* ERROR "inverted slice range" */ : 1]
        _ = s[2 :]
-       _ = s[: 1<<100]
-       _ = s[1<<100 :]
-       _ = s[1<<100 : 1<<100]
-       _ = s[1 /* ERROR "inverted slice range" */ <<100+1 : 1<<100]
-       _ = s[1 /* ERROR "inverted slice range" */ <<100+1 : 10]
+       _ = s[: 1 /* ERROR "stupid index" */ <<100]
+       _ = s[1 /* ERROR "stupid index" */ <<100 :]
+       _ = s[1 /* ERROR "stupid index" */ <<100 : 1 /* ERROR "stupid index" */ <<100]
 
        var t string
        _ = t[- /* ERROR "index .* negative" */ 1]
@@ -126,9 +137,152 @@ type T struct {
 func (*T) m() {}
 
 func method_expressions() {
-       _ = T /* ERROR "no field or method" */ .a
+       _ = T /* ERROR "no single field or method" */ .a
        _ = T /* ERROR "has no method" */ .x
        _ = T.m
        var f func(*T) = (*T).m
        var g func(*T) = ( /* ERROR "cannot assign" */ T).m
-}
\ No newline at end of file
+}
+
+func struct_literals() {
+       type T0 struct {
+               a, b, c int
+       }
+
+       type T1 struct {
+               T0
+               a, b int
+               u float64
+               s string
+       }
+
+       // keyed elements
+       _ = T1{}
+       _ = T1{a: 0, 1 /* ERROR "mixture of .* elements" */ }
+       _ = T1{aa /* ERROR "unknown field" */ : 0}
+       _ = T1{1 /* ERROR "invalid field name" */ : 0}
+       _ = T1{a: 0, s: "foo", u: 0, a /* ERROR "duplicate field" */: 10}
+       _ = T1{a: "foo" /* ERROR "cannot use" */ }
+       _ = T1{c /* ERROR "unknown field" */ : 0}
+       _ = T1{T0: { /* ERROR "missing type" */ }}
+       _ = T1{T0: T0{}}
+       _ = T1{T0 /* ERROR "invalid field name" */ .a: 0}
+
+       // unkeyed elements
+       _ = T0{1, 2, 3}
+       _ = T0{1, b /* ERROR "mixture" */ : 2, 3}
+       _ = T0{1, 2} /* ERROR "too few values" */
+       _ = T0{1, 2, 3, 4  /* ERROR "too many values" */ }
+       _ = T0{1, "foo" /* ERROR "cannot use" */, 3.4  /* ERROR "cannot use" */}
+}
+
+func array_literals() {
+       type A0 [0]int
+       _ = A0{}
+       _ = A0{0 /* ERROR "index .* out of bounds" */}
+       _ = A0{0 /* ERROR "index .* out of bounds" */ : 0}
+
+       type A1 [10]int
+       _ = A1{}
+       _ = A1{0, 1, 2}
+       _ = A1{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
+       _ = A1{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 /* ERROR "index .* out of bounds" */ }
+       _ = A1{- /* ERROR "index .* negative" */ 1: 0}
+       _ = A1{8: 8, 9}
+       _ = A1{8: 8, 9, 10 /* ERROR "index .* out of bounds" */ }
+       _ = A1{0, 1, 2, 0 /* ERROR "duplicate index" */ : 0, 3: 3, 4}
+       _ = A1{5: 5, 6, 7, 3: 3, 4}
+       _ = A1{5: 5, 6, 7, 3: 3, 4, 5 /* ERROR "duplicate index" */ }
+       _ = A1{10 /* ERROR "index .* out of bounds" */ : 10, 10 /* ERROR "index .* out of bounds" */ : 10}
+       _ = A1{5: 5, 6, 7, 3: 3, 1 /* ERROR "stupid index" */ <<100: 4, 5 /* ERROR "duplicate index" */ }
+       _ = A1{5: 5, 6, 7, 4: 4, 1 /* ERROR "stupid index" */ <<100: 4}
+       _ = A1{2.0}
+       _ = A1{2.1 /* ERROR "cannot use" */ }
+       _ = A1{"foo" /* ERROR "cannot use" */ }
+
+       a0 := [...]int{}
+       assert(len(a0) == 0)
+       
+       a1 := [...]int{0, 1, 2}
+       assert(len(a1) == 3)
+       var a13 [3]int
+       var a14 [4]int
+       a13 = a1
+       a14 = a1 /* ERROR "cannot assign" */
+       
+       a2 := [...]int{- /* ERROR "index .* negative" */ 1: 0}
+
+       a3 := [...]int{0, 1, 2, 0 /* ERROR "duplicate index" */ : 0, 3: 3, 4}
+       assert(len(a3) == 5) // somewhat arbitrary
+
+       a4 := [...]complex128{0, 1, 2, 1<<10-2: -1i, 1i, 400: 10, 12, 14}
+       assert(len(a4) == 1024)
+
+       // from the spec
+       type Point struct { x, y float32 }
+       _ = [...]Point{Point{1.5, -3.5}, Point{0, 0}}
+       _ = [...]Point{{1.5, -3.5}, {0, 0}}
+       _ = [][]int{[]int{1, 2, 3}, []int{4, 5}}
+       _ = [][]int{{1, 2, 3}, {4, 5}}
+       _ = [...]*Point{&Point{1.5, -3.5}, &Point{0, 0}}
+       _ = [...]*Point{{1.5, -3.5}, {0, 0}}
+}
+
+func slice_literals() {
+       type S0 []int
+       _ = S0{}
+       _ = S0{0, 1, 2}
+       _ = S0{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
+       _ = S0{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
+       _ = S0{- /* ERROR "index .* negative" */ 1: 0}
+       _ = S0{8: 8, 9}
+       _ = S0{8: 8, 9, 10}
+       _ = S0{0, 1, 2, 0 /* ERROR "duplicate index" */ : 0, 3: 3, 4}
+       _ = S0{5: 5, 6, 7, 3: 3, 4}
+       _ = S0{5: 5, 6, 7, 3: 3, 4, 5 /* ERROR "duplicate index" */ }
+       _ = S0{10: 10, 10 /* ERROR "duplicate index" */ : 10}
+       _ = S0{5: 5, 6, 7, 3: 3, 1 /* ERROR "stupid index" */ <<100: 4, 5 /* ERROR "duplicate index" */ }
+       _ = S0{5: 5, 6, 7, 4: 4, 1 /* ERROR "stupid index" */ <<100: 4}
+       _ = S0{2.0}
+       _ = S0{2.1 /* ERROR "cannot use" */ }
+       _ = S0{"foo" /* ERROR "cannot use" */ }
+}
+
+func map_literals() {
+       type M0 map[string]int
+
+       _ = M0{}
+       _ = M0{1 /* ERROR "missing key" */ }
+       _ = M0{1 /* ERROR "cannot use .* as string key" */ : 2}
+       _ = M0{"foo": "bar" /* ERROR "cannot use .* as int value" */ }
+       _ = M0{"foo": 1, "bar": 2, "foo" /* ERROR "duplicate key" */ : 3 }
+}
+
+type I interface {
+       m()
+}
+
+type I2 interface {
+       m(int)
+}
+
+type T1 struct{}
+type T2 struct{}
+
+func (T2) m(int) {}
+
+func type_asserts() {
+       var x int
+       _ = x /* ERROR "not an interface" */ .(int)
+
+       var e interface{}
+       var ok bool
+       x, ok = e.(int)
+
+       var t I
+       _ = t /* ERROR "use of .* outside type switch" */ .(type)
+       _ = t.(T)
+       _ = t.(T1 /* ERROR "missing method m" */ )
+       _ = t.(T2 /* ERROR "wrong type for method m" */ )
+       _ = t.(I2 /* ERROR "wrong type for method m" */ )
+}
index e3436bc41d9af813621ffc8322403bb90a6da8b5..d3cc3acce4f6bd75bd0863f9d338d688bc5ca9f1 100644 (file)
@@ -71,4 +71,170 @@ func _selects() {
                x = t
        case <-sc /* ERROR "cannot receive from send-only channel" */ :
        }
+       select {
+       default:
+       default /* ERROR "multiple defaults" */ :
+       }
+}
+
+func _gos() {
+       go 1 /* ERROR "expected function/method call" */
+       go _gos()
+       var c chan int
+       go close(c)
+       go len(c) // TODO(gri) this should not be legal
+}
+
+func _defers() {
+       defer 1 /* ERROR "expected function/method call" */
+       defer _defers()
+       var c chan int
+       defer close(c)
+       defer len(c) // TODO(gri) this should not be legal
+}
+
+func _switches() {
+       var x int
+
+       switch x {
+       default:
+       default /* ERROR "multiple defaults" */ :
+       }
+
+       // TODO(gri) more tests
+}
+
+type I interface {
+       m()
+}
+
+type I2 interface {
+       m(int)
+}
+
+type T struct{}
+type T1 struct{}
+type T2 struct{}
+
+func (T) m() {}
+func (T2) m(int) {}
+
+func _typeswitches() {
+       var i int
+       var x interface{}
+
+       switch x.(type) {}
+       switch (x /* ERROR "outside type switch" */ .(type)) {}
+
+       switch x.(type) {
+       default:
+       default /* ERROR "multiple defaults" */ :
+       }
+
+       switch x := x.(type) {}
+
+       switch x := x.(type) {
+       case int:
+               var y int = x
+       }
+
+       switch x := i /* ERROR "not an interface" */ .(type) {}
+
+       switch t := x.(type) {
+       case nil:
+               var v bool = t /* ERROR "cannot assign" */
+       case int:
+               var v int = t
+       case float32, complex64:
+               var v float32 = t /* ERROR "cannot assign" */
+       default:
+               var v float32 = t /* ERROR "cannot assign" */
+       }
+
+       var t I
+       switch t.(type) {
+       case T:
+       case T1 /* ERROR "missing method m" */ :
+       case T2 /* ERROR "wrong type for method m" */ :
+       case I2 /* ERROR "wrong type for method m" */ :
+       }
+}
+
+func _rangeloops() {
+       var (
+               x int
+               a [10]float32
+               b []string
+               p *[10]complex128
+               pp **[10]complex128
+               s string
+               m map[int]bool
+               c chan int
+               sc chan<- int
+               rc <-chan int
+       )
+
+       for _ = range x /* ERROR "cannot range over" */ {}
+       for i := range x /* ERROR "cannot range over" */ {}
+
+       for i := range a {
+               var ii int
+               ii = i
+       }
+       for i, x := range a {
+               var ii int
+               ii = i
+               var xx float64
+               xx = x /* ERROR "cannot assign" */
+       }
+       var ii int
+       var xx float32
+       for ii, xx := range a {}
+
+       for i := range b {
+               var ii int
+               ii = i
+       }
+       for i, x := range b {
+               var ii int
+               ii = i
+               var xx string
+               xx = x
+       }
+
+       for i := range s {
+               var ii int
+               ii = i
+       }
+       for i, x := range s {
+               var ii int
+               ii = i
+               var xx rune
+               xx = x
+       }
+
+       for _, x := range p {
+               var xx complex128
+               xx = x
+       }
+
+       for _, x := range pp /* ERROR "cannot range over" */ {}
+
+       for k := range m {
+               var kk int32
+               kk = k /* ERROR "cannot assign" */
+       }
+       for k, v := range m {
+               var kk int
+               kk = k
+               if v {}
+       }
+
+       for _, _ /* ERROR "only one iteration variable" */ = range c {}
+       for e := range c {
+               var ee int
+               ee = e
+       }
+       for _ = range sc /* ERROR "cannot range over send-only channel" */ {}
+       for _ = range rc {}
 }
\ No newline at end of file
index eed0c8a6c3468ce8dc146bd4b302efa02abaaedb..83a08266dd30fad71c3be8edfbdea83e2fa3eeb8 100644 (file)
@@ -126,6 +126,15 @@ type Struct struct {
        Fields []*StructField
 }
 
+func (typ *Struct) fieldIndex(name string) int {
+       for i, f := range typ.Fields {
+               if f.Name == name {
+                       return i
+               }
+       }
+       return -1
+}
+
 // A Pointer represents a pointer type *Base.
 type Pointer struct {
        implementsType
index bb8b6a2bdacab89d7702a0999a3daecb4c7d45f5..0fbaa3329d44c8175787857573f50a2275436747 100644 (file)
@@ -116,10 +116,12 @@ func init() {
 
        // error type
        {
+               res := ast.NewObj(ast.Var, "")
+               res.Type = Typ[String]
+               err := ast.NewObj(ast.Fun, "Error")
+               err.Type = &Signature{Results: ObjList{res}}
                obj := def(ast.Typ, "error")
-               // TODO(gri) set up correct interface type
-               typ := &NamedType{Underlying: &Interface{}, Obj: obj}
-               obj.Type = typ
+               obj.Type = &NamedType{Underlying: &Interface{Methods: ObjList{err}}, Obj: obj}
        }
 
        // predeclared constants
index 4a1929a839715c01bd865124f3fc9b3a6a47ca68..a0bd4327fb5ebf958ca143b531323c17bdd44e26 100644 (file)
@@ -9,6 +9,7 @@ package winfsnotify
 import (
        "io/ioutil"
        "os"
+       "sync/atomic"
        "testing"
        "time"
 )
@@ -105,14 +106,14 @@ func TestNotifyClose(t *testing.T) {
        watcher, _ := NewWatcher()
        watcher.Close()
 
-       done := false
+       var done int32
        go func() {
                watcher.Close()
-               done = true
+               atomic.StoreInt32(&done, 1)
        }()
 
        time.Sleep(50 * time.Millisecond)
-       if !done {
+       if atomic.LoadInt32(&done) == 0 {
                t.Fatal("double Close() test failed: second Close() call didn't return")
        }
 
index f704b7d03e8c005f1aa184bd40bb95ab51e6b5ec..34061135a11f603171b283bf42a814248766d516 100644 (file)
@@ -583,6 +583,7 @@ var mallocTest = []struct {
 var _ bytes.Buffer
 
 func TestCountMallocs(t *testing.T) {
+       defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
        for _, mt := range mallocTest {
                const N = 100
                memstats := new(runtime.MemStats)
index 2d4f69aaea6ca0c1776e509006a665ea8d89f51b..a68a4840f8e9131fd3720bc33e96d6bcc63bec1d 100644 (file)
@@ -20,7 +20,7 @@ func SortImports(fset *token.FileSet, f *File) {
                        break
                }
 
-               if d.Lparen == token.NoPos {
+               if !d.Lparen.IsValid() {
                        // Not a block: sorted by default.
                        continue
                }
diff --git a/libgo/go/go/format/format.go b/libgo/go/go/format/format.go
new file mode 100644 (file)
index 0000000..286296e
--- /dev/null
@@ -0,0 +1,200 @@
+// 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 format implements standard formatting of Go source.
+package format
+
+import (
+       "bytes"
+       "fmt"
+       "go/ast"
+       "go/parser"
+       "go/printer"
+       "go/token"
+       "io"
+       "strings"
+)
+
+var config = printer.Config{Mode: printer.UseSpaces | printer.TabIndent, Tabwidth: 8}
+
+// Node formats node in canonical gofmt style and writes the result to dst.
+//
+// The node type must be *ast.File, *printer.CommentedNode, []ast.Decl,
+// []ast.Stmt, or assignment-compatible to ast.Expr, ast.Decl, ast.Spec,
+// or ast.Stmt. Node does not modify node. Imports are not sorted for
+// nodes representing partial source files (i.e., if the node is not an
+// *ast.File or a *printer.CommentedNode not wrapping an *ast.File).
+//
+// The function may return early (before the entire result is written)
+// and return a formatting error, for instance due to an incorrect AST.
+//
+func Node(dst io.Writer, fset *token.FileSet, node interface{}) error {
+       // Determine if we have a complete source file (file != nil).
+       var file *ast.File
+       var cnode *printer.CommentedNode
+       switch n := node.(type) {
+       case *ast.File:
+               file = n
+       case *printer.CommentedNode:
+               if f, ok := n.Node.(*ast.File); ok {
+                       file = f
+                       cnode = n
+               }
+       }
+
+       // Sort imports if necessary.
+       if file != nil && hasUnsortedImports(file) {
+               // Make a copy of the AST because ast.SortImports is destructive.
+               // TODO(gri) Do this more efficently.
+               var buf bytes.Buffer
+               err := config.Fprint(&buf, fset, file)
+               if err != nil {
+                       return err
+               }
+               file, err = parser.ParseFile(fset, "", buf.Bytes(), parser.ParseComments)
+               if err != nil {
+                       // We should never get here. If we do, provide good diagnostic.
+                       return fmt.Errorf("format.Node internal error (%s)", err)
+               }
+               ast.SortImports(fset, file)
+
+               // Use new file with sorted imports.
+               node = file
+               if cnode != nil {
+                       node = &printer.CommentedNode{Node: file, Comments: cnode.Comments}
+               }
+       }
+
+       return config.Fprint(dst, fset, node)
+}
+
+// Source formats src in canonical gofmt style and writes the result to dst
+// or returns an I/O or syntax error. src is expected to be a syntactically
+// correct Go source file, or a list of Go declarations or statements.
+//
+// If src is a partial source file, the leading and trailing space of src
+// is applied to the result (such that it has the same leading and trailing
+// space as src), and the formatted src is indented by the same amount as
+// the first line of src containing code. Imports are not sorted for partial
+// source files.
+//
+func Source(src []byte) ([]byte, error) {
+       fset := token.NewFileSet()
+       node, err := parse(fset, src)
+       if err != nil {
+               return nil, err
+       }
+
+       var buf bytes.Buffer
+       if file, ok := node.(*ast.File); ok {
+               // Complete source file.
+               ast.SortImports(fset, file)
+               err := config.Fprint(&buf, fset, file)
+               if err != nil {
+                       return nil, err
+               }
+
+       } else {
+               // Partial source file.
+               // Determine and prepend leading space.
+               i, j := 0, 0
+               for j < len(src) && isSpace(src[j]) {
+                       if src[j] == '\n' {
+                               i = j + 1 // index of last line in leading space
+                       }
+                       j++
+               }
+               buf.Write(src[:i])
+
+               // Determine indentation of first code line.
+               // Spaces are ignored unless there are no tabs,
+               // in which case spaces count as one tab.
+               indent := 0
+               hasSpace := false
+               for _, b := range src[i:j] {
+                       switch b {
+                       case ' ':
+                               hasSpace = true
+                       case '\t':
+                               indent++
+                       }
+               }
+               if indent == 0 && hasSpace {
+                       indent = 1
+               }
+
+               // Format the source.
+               cfg := config
+               cfg.Indent = indent
+               err := cfg.Fprint(&buf, fset, node)
+               if err != nil {
+                       return nil, err
+               }
+
+               // Determine and append trailing space.
+               i = len(src)
+               for i > 0 && isSpace(src[i-1]) {
+                       i--
+               }
+               buf.Write(src[i:])
+       }
+
+       return buf.Bytes(), nil
+}
+
+func hasUnsortedImports(file *ast.File) bool {
+       for _, d := range file.Decls {
+               d, ok := d.(*ast.GenDecl)
+               if !ok || d.Tok != token.IMPORT {
+                       // Not an import declaration, so we're done.
+                       // Imports are always first.
+                       return false
+               }
+               if d.Lparen.IsValid() {
+                       // For now assume all grouped imports are unsorted.
+                       // TODO(gri) Should check if they are sorted already.
+                       return true
+               }
+               // Ungrouped imports are sorted by default.
+       }
+       return false
+}
+
+func isSpace(b byte) bool {
+       return b == ' ' || b == '\t' || b == '\n' || b == '\r'
+}
+
+func parse(fset *token.FileSet, src []byte) (interface{}, error) {
+       // Try as a complete source file.
+       file, err := parser.ParseFile(fset, "", src, parser.ParseComments)
+       if err == nil {
+               return file, nil
+       }
+       // If the source is missing a package clause, try as a source fragment; otherwise fail.
+       if !strings.Contains(err.Error(), "expected 'package'") {
+               return nil, err
+       }
+
+       // Try as a declaration list by prepending a package clause in front of src.
+       // Use ';' not '\n' to keep line numbers intact.
+       psrc := append([]byte("package p;"), src...)
+       file, err = parser.ParseFile(fset, "", psrc, parser.ParseComments)
+       if err == nil {
+               return file.Decls, nil
+       }
+       // If the source is missing a declaration, try as a statement list; otherwise fail.
+       if !strings.Contains(err.Error(), "expected declaration") {
+               return nil, err
+       }
+
+       // Try as statement list by wrapping a function around src.
+       fsrc := append(append([]byte("package p; func _() {"), src...), '}')
+       file, err = parser.ParseFile(fset, "", fsrc, parser.ParseComments)
+       if err == nil {
+               return file.Decls[0].(*ast.FuncDecl).Body.List, nil
+       }
+
+       // Failed, and out of options.
+       return nil, err
+}
diff --git a/libgo/go/go/format/format_test.go b/libgo/go/go/format/format_test.go
new file mode 100644 (file)
index 0000000..7d7940b
--- /dev/null
@@ -0,0 +1,125 @@
+// 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 format
+
+import (
+       "bytes"
+       "go/parser"
+       "go/token"
+       "io/ioutil"
+       "strings"
+       "testing"
+)
+
+const testfile = "format_test.go"
+
+func diff(t *testing.T, dst, src []byte) {
+       line := 1
+       offs := 0 // line offset
+       for i := 0; i < len(dst) && i < len(src); i++ {
+               d := dst[i]
+               s := src[i]
+               if d != s {
+                       t.Errorf("dst:%d: %s\n", line, dst[offs:i+1])
+                       t.Errorf("src:%d: %s\n", line, src[offs:i+1])
+                       return
+               }
+               if s == '\n' {
+                       line++
+                       offs = i + 1
+               }
+       }
+       if len(dst) != len(src) {
+               t.Errorf("len(dst) = %d, len(src) = %d\nsrc = %q", len(dst), len(src), src)
+       }
+}
+
+func TestNode(t *testing.T) {
+       src, err := ioutil.ReadFile(testfile)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       fset := token.NewFileSet()
+       file, err := parser.ParseFile(fset, testfile, src, parser.ParseComments)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       var buf bytes.Buffer
+
+       if err = Node(&buf, fset, file); err != nil {
+               t.Fatal("Node failed:", err)
+       }
+
+       diff(t, buf.Bytes(), src)
+}
+
+func TestSource(t *testing.T) {
+       src, err := ioutil.ReadFile(testfile)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       res, err := Source(src)
+       if err != nil {
+               t.Fatal("Source failed:", err)
+       }
+
+       diff(t, res, src)
+}
+
+// Test cases that are expected to fail are marked by the prefix "ERROR".
+var tests = []string{
+       // declaration lists
+       `import "go/format"`,
+       "var x int",
+       "var x int\n\ntype T struct{}",
+
+       // statement lists
+       "x := 0",
+       "f(a, b, c)\nvar x int = f(1, 2, 3)",
+
+       // indentation, leading and trailing space
+       "\tx := 0\n\tgo f()",
+       "\tx := 0\n\tgo f()\n\n\n",
+       "\n\t\t\n\n\tx := 0\n\tgo f()\n\n\n",
+       "\n\t\t\n\n\t\t\tx := 0\n\t\t\tgo f()\n\n\n",
+       "\n\t\t\n\n\t\t\tx := 0\n\t\t\tconst s = `\nfoo\n`\n\n\n", // no indentation inside raw strings
+
+       // erroneous programs
+       "ERRORvar x",
+       "ERROR1 + 2 +",
+       "ERRORx :=  0",
+}
+
+func String(s string) (string, error) {
+       res, err := Source([]byte(s))
+       if err != nil {
+               return "", err
+       }
+       return string(res), nil
+}
+
+func TestPartial(t *testing.T) {
+       for _, src := range tests {
+               if strings.HasPrefix(src, "ERROR") {
+                       // test expected to fail
+                       src = src[5:] // remove ERROR prefix
+                       res, err := String(src)
+                       if err == nil && res == src {
+                               t.Errorf("formatting succeeded but was expected to fail:\n%q", src)
+                       }
+               } else {
+                       // test expected to succeed
+                       res, err := String(src)
+                       if err != nil {
+                               t.Errorf("formatting failed (%s):\n%q", err, src)
+                       } else if res != src {
+                               t.Errorf("formatting incorrect:\nsource: %q\nresult: %q", src, res)
+                       }
+               }
+       }
+}
index 26b31b247ad01d3cdb7e4ad1fc8a2ea968e90995..00757e0d753300944ef057777a751273852c6ee5 100644 (file)
@@ -578,14 +578,15 @@ func (p *parser) parseTypeName() ast.Expr {
        return ident
 }
 
-func (p *parser) parseArrayType(ellipsisOk bool) ast.Expr {
+func (p *parser) parseArrayType() ast.Expr {
        if p.trace {
                defer un(trace(p, "ArrayType"))
        }
 
        lbrack := p.expect(token.LBRACK)
        var len ast.Expr
-       if ellipsisOk && p.tok == token.ELLIPSIS {
+       // always permit ellipsis for more fault-tolerant parsing
+       if p.tok == token.ELLIPSIS {
                len = &ast.Ellipsis{Ellipsis: p.pos}
                p.next()
        } else if p.tok != token.RBRACK {
@@ -697,7 +698,7 @@ func (p *parser) tryVarType(isParam bool) ast.Expr {
        if isParam && p.tok == token.ELLIPSIS {
                pos := p.pos
                p.next()
-               typ := p.tryIdentOrType(isParam) // don't use parseType so we can provide better error message
+               typ := p.tryIdentOrType() // don't use parseType so we can provide better error message
                if typ != nil {
                        p.resolve(typ)
                } else {
@@ -706,7 +707,7 @@ func (p *parser) tryVarType(isParam bool) ast.Expr {
                }
                return &ast.Ellipsis{Ellipsis: pos, Elt: typ}
        }
-       return p.tryIdentOrType(false)
+       return p.tryIdentOrType()
 }
 
 // If the result is an identifier, it is not resolved.
@@ -943,12 +944,12 @@ func (p *parser) parseChanType() *ast.ChanType {
 }
 
 // If the result is an identifier, it is not resolved.
-func (p *parser) tryIdentOrType(ellipsisOk bool) ast.Expr {
+func (p *parser) tryIdentOrType() ast.Expr {
        switch p.tok {
        case token.IDENT:
                return p.parseTypeName()
        case token.LBRACK:
-               return p.parseArrayType(ellipsisOk)
+               return p.parseArrayType()
        case token.STRUCT:
                return p.parseStructType()
        case token.MUL:
@@ -975,7 +976,7 @@ func (p *parser) tryIdentOrType(ellipsisOk bool) ast.Expr {
 }
 
 func (p *parser) tryType() ast.Expr {
-       typ := p.tryIdentOrType(false)
+       typ := p.tryIdentOrType()
        if typ != nil {
                p.resolve(typ)
        }
@@ -1083,7 +1084,7 @@ func (p *parser) parseOperand(lhs bool) ast.Expr {
                return p.parseFuncTypeOrLit()
        }
 
-       if typ := p.tryIdentOrType(true); typ != nil {
+       if typ := p.tryIdentOrType(); typ != nil {
                // could be type for composite literal or conversion
                _, isIdent := typ.(*ast.Ident)
                assert(!isIdent, "type cannot be identifier")
@@ -1802,7 +1803,7 @@ func (p *parser) parseSwitchStmt() ast.Stmt {
                                //
                                //      switch t := 0; t := x.(T) { ... }
                                //
-                               // (this code is not valid Go because the first t will
+                               // (this code is not valid Go because the first t
                                // cannot be accessed and thus is never used, the extra
                                // scope is needed for the correct error message).
                                //
index e99a2e36d4fd8cb0d56093a98a98589118a3e329..cd5b67b82df3e005aaf775e211d13b91732dd5de 100644 (file)
@@ -730,7 +730,7 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int) {
 
        case *ast.FuncLit:
                p.expr(x.Type)
-               p.funcBody(x.Body, p.distance(x.Type.Pos(), p.pos), true)
+               p.adjBlock(p.distanceFrom(x.Type.Pos()), blank, x.Body)
 
        case *ast.ParenExpr:
                if _, hasParens := x.X.(*ast.ParenExpr); hasParens {
@@ -900,7 +900,11 @@ func (p *printer) stmtList(list []ast.Stmt, nindent int, nextIsRBrace bool) {
                if _, isEmpty := s.(*ast.EmptyStmt); !isEmpty {
                        // _indent == 0 only for lists of switch/select case clauses;
                        // in those cases each clause is a new section
-                       p.linebreak(p.lineFor(s.Pos()), 1, ignore, i == 0 || nindent == 0 || multiLine)
+                       if len(p.output) > 0 {
+                               // only print line break if we are not at the beginning of the output
+                               // (i.e., we are not printing only a partial program)
+                               p.linebreak(p.lineFor(s.Pos()), 1, ignore, i == 0 || nindent == 0 || multiLine)
+                       }
                        p.stmt(s, nextIsRBrace && i == len(list)-1)
                        multiLine = p.isMultiLine(s)
                        i++
@@ -912,11 +916,11 @@ func (p *printer) stmtList(list []ast.Stmt, nindent int, nextIsRBrace bool) {
 }
 
 // block prints an *ast.BlockStmt; it always spans at least two lines.
-func (p *printer) block(s *ast.BlockStmt, nindent int) {
-       p.print(s.Pos(), token.LBRACE)
-       p.stmtList(s.List, nindent, true)
-       p.linebreak(p.lineFor(s.Rbrace), 1, ignore, true)
-       p.print(s.Rbrace, token.RBRACE)
+func (p *printer) block(b *ast.BlockStmt, nindent int) {
+       p.print(b.Lbrace, token.LBRACE)
+       p.stmtList(b.List, nindent, true)
+       p.linebreak(p.lineFor(b.Rbrace), 1, ignore, true)
+       p.print(b.Rbrace, token.RBRACE)
 }
 
 func isTypeName(x ast.Expr) bool {
@@ -1421,19 +1425,19 @@ func (p *printer) nodeSize(n ast.Node, maxSize int) (size int) {
        return
 }
 
-func (p *printer) isOneLineFunc(b *ast.BlockStmt, headerSize int) bool {
+// bodySize is like nodeSize but it is specialized for *ast.BlockStmt's.
+func (p *printer) bodySize(b *ast.BlockStmt, maxSize int) int {
        pos1 := b.Pos()
        pos2 := b.Rbrace
        if pos1.IsValid() && pos2.IsValid() && p.lineFor(pos1) != p.lineFor(pos2) {
                // opening and closing brace are on different lines - don't make it a one-liner
-               return false
+               return maxSize + 1
        }
        if len(b.List) > 5 || p.commentBefore(p.posFor(pos2)) {
                // too many statements or there is a comment inside - don't make it a one-liner
-               return false
+               return maxSize + 1
        }
        // otherwise, estimate body size
-       const maxSize = 100
        bodySize := 0
        for i, s := range b.List {
                if i > 0 {
@@ -1441,19 +1445,23 @@ func (p *printer) isOneLineFunc(b *ast.BlockStmt, headerSize int) bool {
                }
                bodySize += p.nodeSize(s, maxSize)
        }
-       return headerSize+bodySize <= maxSize
+       return bodySize
 }
 
-func (p *printer) funcBody(b *ast.BlockStmt, headerSize int, isLit bool) {
+// adjBlock prints an "adjacent" block (e.g., a for-loop or function body) following
+// a header (e.g., a for-loop control clause or function signature) of given headerSize.
+// If the header's and block's size are "small enough" and the block is "simple enough",
+// the block is printed on the current line, without line breaks, spaced from the header
+// by sep. Otherwise the block's opening "{" is printed on the current line, followed by
+// lines for the block's statements and its closing "}".
+//
+func (p *printer) adjBlock(headerSize int, sep whiteSpace, b *ast.BlockStmt) {
        if b == nil {
                return
        }
 
-       if p.isOneLineFunc(b, headerSize) {
-               sep := vtab
-               if isLit {
-                       sep = blank
-               }
+       const maxSize = 100
+       if headerSize+p.bodySize(b, maxSize) <= maxSize {
                p.print(sep, b.Lbrace, token.LBRACE)
                if len(b.List) > 0 {
                        p.print(blank)
@@ -1469,17 +1477,20 @@ func (p *printer) funcBody(b *ast.BlockStmt, headerSize int, isLit bool) {
                return
        }
 
-       p.print(blank)
+       if sep != ignore {
+               p.print(blank) // always use blank
+       }
        p.block(b, 1)
 }
 
-// distance returns the column difference between from and to if both
-// are on the same line; if they are on different lines (or unknown)
-// the result is infinity.
-func (p *printer) distance(from0 token.Pos, to token.Position) int {
-       from := p.posFor(from0)
-       if from.IsValid() && to.IsValid() && from.Line == to.Line {
-               return to.Column - from.Column
+// distanceFrom returns the column difference between from and p.pos (the current
+// estimated position) if both are on the same line; if they are on different lines
+// (or unknown) the result is infinity.
+func (p *printer) distanceFrom(from token.Pos) int {
+       if from.IsValid() && p.pos.IsValid() {
+               if f := p.posFor(from); f.Line == p.pos.Line {
+                       return p.pos.Column - f.Column
+               }
        }
        return infinity
 }
@@ -1493,7 +1504,7 @@ func (p *printer) funcDecl(d *ast.FuncDecl) {
        }
        p.expr(d.Name)
        p.signature(d.Type.Params, d.Type.Results)
-       p.funcBody(d.Body, p.distance(d.Pos(), p.pos), false)
+       p.adjBlock(p.distanceFrom(d.Pos()), vtab, d.Body)
 }
 
 func (p *printer) decl(decl ast.Decl) {
@@ -1523,31 +1534,35 @@ func declToken(decl ast.Decl) (tok token.Token) {
        return
 }
 
-func (p *printer) file(src *ast.File) {
-       p.setComment(src.Doc)
-       p.print(src.Pos(), token.PACKAGE, blank)
-       p.expr(src.Name)
-
-       if len(src.Decls) > 0 {
-               tok := token.ILLEGAL
-               for _, d := range src.Decls {
-                       prev := tok
-                       tok = declToken(d)
-                       // if the declaration token changed (e.g., from CONST to TYPE)
-                       // or the next declaration has documentation associated with it,
-                       // print an empty line between top-level declarations
-                       // (because p.linebreak is called with the position of d, which
-                       // is past any documentation, the minimum requirement is satisfied
-                       // even w/o the extra getDoc(d) nil-check - leave it in case the
-                       // linebreak logic improves - there's already a TODO).
+func (p *printer) declList(list []ast.Decl) {
+       tok := token.ILLEGAL
+       for _, d := range list {
+               prev := tok
+               tok = declToken(d)
+               // If the declaration token changed (e.g., from CONST to TYPE)
+               // or the next declaration has documentation associated with it,
+               // print an empty line between top-level declarations.
+               // (because p.linebreak is called with the position of d, which
+               // is past any documentation, the minimum requirement is satisfied
+               // even w/o the extra getDoc(d) nil-check - leave it in case the
+               // linebreak logic improves - there's already a TODO).
+               if len(p.output) > 0 {
+                       // only print line break if we are not at the beginning of the output
+                       // (i.e., we are not printing only a partial program)
                        min := 1
                        if prev != tok || getDoc(d) != nil {
                                min = 2
                        }
                        p.linebreak(p.lineFor(d.Pos()), min, ignore, false)
-                       p.decl(d)
                }
+               p.decl(d)
        }
+}
 
+func (p *printer) file(src *ast.File) {
+       p.setComment(src.Doc)
+       p.print(src.Pos(), token.PACKAGE, blank)
+       p.expr(src.Name)
+       p.declList(src.Decls)
        p.print(newline)
 }
index 31f5ef0883c1b64e1f48983438fb3bd091dec16a..5b29affcb7db1aef9dac200d177e6373a11296de 100644 (file)
@@ -20,7 +20,7 @@ import (
 var testfile *ast.File
 
 func testprint(out io.Writer, file *ast.File) {
-       if err := (&Config{TabIndent | UseSpaces, 8}).Fprint(out, fset, file); err != nil {
+       if err := (&Config{TabIndent | UseSpaces, 8, 0}).Fprint(out, fset, file); err != nil {
                log.Fatalf("print error: %s", err)
        }
 }
index e79e3ffda2629fa15d9374339188a9abf6b8a8d3..5d75f09167d6d29d0902861de6e1727107710ae8 100644 (file)
@@ -165,15 +165,15 @@ func (p *printer) atLineBegin(pos token.Position) {
        // write indentation
        // use "hard" htabs - indentation columns
        // must not be discarded by the tabwriter
-       for i := 0; i < p.indent; i++ {
+       n := p.Config.Indent + p.indent // include base indentation
+       for i := 0; i < n; i++ {
                p.output = append(p.output, '\t')
        }
 
        // update positions
-       i := p.indent
-       p.pos.Offset += i
-       p.pos.Column += i
-       p.out.Column += i
+       p.pos.Offset += n
+       p.pos.Column += n
+       p.out.Column += n
 }
 
 // writeByte writes ch n times to p.output and updates p.pos.
@@ -452,7 +452,7 @@ func trimRight(s string) string {
 
 // stripCommonPrefix removes a common prefix from /*-style comment lines (unless no
 // comment line is indented, all but the first line have some form of space prefix).
-// The prefix is computed using heuristics such that is is likely that the comment
+// The prefix is computed using heuristics such that is likely that the comment
 // contents are nicely laid out after re-printing each line using the printer's
 // current indentation.
 //
@@ -1032,9 +1032,9 @@ func (p *printer) printNode(node interface{}) error {
        case ast.Expr:
                p.expr(n)
        case ast.Stmt:
-               // A labeled statement will un-indent to position the
-               // label. Set indent to 1 so we don't get indent "underflow".
-               if _, labeledStmt := n.(*ast.LabeledStmt); labeledStmt {
+               // A labeled statement will un-indent to position the label.
+               // Set p.indent to 1 so we don't get indent "underflow".
+               if _, ok := n.(*ast.LabeledStmt); ok {
                        p.indent = 1
                }
                p.stmt(n, false)
@@ -1042,6 +1042,17 @@ func (p *printer) printNode(node interface{}) error {
                p.decl(n)
        case ast.Spec:
                p.spec(n, 1, false)
+       case []ast.Stmt:
+               // A labeled statement will un-indent to position the label.
+               // Set p.indent to 1 so we don't get indent "underflow".
+               for _, s := range n {
+                       if _, ok := s.(*ast.LabeledStmt); ok {
+                               p.indent = 1
+                       }
+               }
+               p.stmtList(n, 0, false)
+       case []ast.Decl:
+               p.declList(n)
        case *ast.File:
                p.file(n)
        default:
@@ -1174,6 +1185,7 @@ const (
 type Config struct {
        Mode     Mode // default: 0
        Tabwidth int  // default: 8
+       Indent   int  // default: 0 (all code is indented at least by this much)
 }
 
 // fprint implements Fprint and takes a nodesSizes map for setting up the printer state.
@@ -1235,8 +1247,8 @@ type CommentedNode struct {
 
 // Fprint "pretty-prints" an AST node to output for a given configuration cfg.
 // Position information is interpreted relative to the file set fset.
-// The node type must be *ast.File, *CommentedNode, or assignment-compatible
-// to ast.Expr, ast.Decl, ast.Spec, or ast.Stmt.
+// The node type must be *ast.File, *CommentedNode, []ast.Decl, []ast.Stmt,
+// or assignment-compatible to ast.Expr, ast.Decl, ast.Spec, or ast.Stmt.
 //
 func (cfg *Config) Fprint(output io.Writer, fset *token.FileSet, node interface{}) error {
        return cfg.fprint(output, fset, node, make(map[ast.Node]int))
index 36d1bf74d3ffe748f42b3ebaebd3e390aa456a93..8454ac12b9e7e45afa45d52ef0b25dd782d5d83e 100644 (file)
@@ -434,6 +434,98 @@ func (t *t) foo(a, b, c int) int {
        }
 }
 
+var decls = []string{
+       `import "fmt"`,
+       "const pi = 3.1415\nconst e = 2.71828\n\nvar x = pi",
+       "func sum(x, y int) int\t{ return x + y }",
+}
+
+func TestDeclLists(t *testing.T) {
+       for _, src := range decls {
+               file, err := parser.ParseFile(fset, "", "package p;"+src, parser.ParseComments)
+               if err != nil {
+                       panic(err) // error in test
+               }
+
+               var buf bytes.Buffer
+               err = Fprint(&buf, fset, file.Decls) // only print declarations
+               if err != nil {
+                       panic(err) // error in test
+               }
+
+               out := buf.String()
+               if out != src {
+                       t.Errorf("\ngot : %q\nwant: %q\n", out, src)
+               }
+       }
+}
+
+var stmts = []string{
+       "i := 0",
+       "select {}\nvar a, b = 1, 2\nreturn a + b",
+       "go f()\ndefer func() {}()",
+}
+
+func TestStmtLists(t *testing.T) {
+       for _, src := range stmts {
+               file, err := parser.ParseFile(fset, "", "package p; func _() {"+src+"}", parser.ParseComments)
+               if err != nil {
+                       panic(err) // error in test
+               }
+
+               var buf bytes.Buffer
+               err = Fprint(&buf, fset, file.Decls[0].(*ast.FuncDecl).Body.List) // only print statements
+               if err != nil {
+                       panic(err) // error in test
+               }
+
+               out := buf.String()
+               if out != src {
+                       t.Errorf("\ngot : %q\nwant: %q\n", out, src)
+               }
+       }
+}
+
+func TestBaseIndent(t *testing.T) {
+       // The testfile must not contain multi-line raw strings since those
+       // are not indented (because their values must not change) and make
+       // this test fail.
+       const filename = "printer.go"
+       src, err := ioutil.ReadFile(filename)
+       if err != nil {
+               panic(err) // error in test
+       }
+
+       file, err := parser.ParseFile(fset, filename, src, 0)
+       if err != nil {
+               panic(err) // error in test
+       }
+
+       var buf bytes.Buffer
+       for indent := 0; indent < 4; indent++ {
+               buf.Reset()
+               (&Config{Tabwidth: tabwidth, Indent: indent}).Fprint(&buf, fset, file)
+               // all code must be indented by at least 'indent' tabs
+               lines := bytes.Split(buf.Bytes(), []byte{'\n'})
+               for i, line := range lines {
+                       if len(line) == 0 {
+                               continue // empty lines don't have indentation
+                       }
+                       n := 0
+                       for j, b := range line {
+                               if b != '\t' {
+                                       // end of indentation
+                                       n = j
+                                       break
+                               }
+                       }
+                       if n < indent {
+                               t.Errorf("line %d: got only %d tabs; want at least %d: %q", i, n, indent, line)
+                       }
+               }
+       }
+}
+
 // TestFuncType tests that an ast.FuncType with a nil Params field
 // can be printed (per go/ast specification). Test case for issue 3870.
 func TestFuncType(t *testing.T) {
index 1f3cabe75ecf26221f3d1c579ea4af51524abbcb..3b298f95ef1afe111977de90b1d8cdb37290d46e 100644 (file)
@@ -241,7 +241,7 @@ func _() {
        }
 }
 
-// Formatting of for-statement headers.
+// Formatting of for-statement headers for single-line for-loops.
 func _() {
        for {
        }
@@ -279,6 +279,86 @@ func _() {
        }       // no parens printed
 }
 
+// Formatting of for-statement headers for multi-line for-loops.
+func _() {
+       for {
+       }
+       for expr {
+       }
+       for expr {
+       }       // no parens printed
+       for {
+       }       // no semicolons printed
+       for x := expr; ; {
+               use(x)
+       }
+       for expr {
+       }       // no semicolons printed
+       for expr {
+       }       // no semicolons and parens printed
+       for ; ; expr = false {
+       }
+       for x := expr; expr; {
+               use(x)
+       }
+       for x := expr; ; expr = false {
+               use(x)
+       }
+       for ; expr; expr = false {
+       }
+       for x := expr; expr; expr = false {
+               use(x)
+       }
+       for x := range []int{} {
+               use(x)
+       }
+       for x := range []int{} {
+               use(x)
+       }       // no parens printed
+}
+
+// Formatting of selected short single- and multi-line statements.
+func _() {
+       if cond {
+       }
+       if cond {
+       }       // multiple lines
+       if cond {
+       } else {
+       }       // else clause always requires multiple lines
+
+       for {
+       }
+       for i := 0; i < len(a); 1++ {
+       }
+       for i := 0; i < len(a); 1++ {
+               a[i] = i
+       }
+       for i := 0; i < len(a); 1++ {
+               a[i] = i
+       }       // multiple lines
+
+       for i := range a {
+       }
+       for i := range a {
+               a[i] = i
+       }
+       for i := range a {
+               a[i] = i
+       }       // multiple lines
+
+       go func() {
+               for {
+                       a <- <-b
+               }
+       }()
+       defer func() {
+               if x := recover(); x != nil {
+                       err = fmt.Sprintf("error: %s", x.msg)
+               }
+       }()
+}
+
 // Don't remove mandatory parentheses around composite literals in control clauses.
 func _() {
        // strip parentheses - no composite literals or composite literals don't start with a type name
index f93eea8925347f923bb1b6a12f9d1bce665b7c39..e7fcc0e5409927f48d5b6f4d079cd539bdfd651e 100644 (file)
@@ -223,7 +223,7 @@ func _() {
 }
 
 
-// Formatting of for-statement headers.
+// Formatting of for-statement headers for single-line for-loops.
 func _() {
        for{}
        for expr {}
@@ -235,14 +235,70 @@ func _() {
        for; ; expr = false {}
        for x :=expr; expr; {use(x)}
        for x := expr;; expr=false {use(x)}
-       for;expr;expr =false {
-       }
+       for;expr;expr =false {}
        for x := expr;expr;expr = false { use(x) }
        for x := range []int{} { use(x) }
        for x := range (([]int{})) { use(x) }  // no parens printed
 }
 
 
+// Formatting of for-statement headers for multi-line for-loops.
+func _() {
+       for{
+       }
+       for expr {
+       }
+       for (expr) {
+       }  // no parens printed
+       for;;{
+       }  // no semicolons printed
+       for x :=expr;; {use( x)
+       }
+       for; expr;{
+       }  // no semicolons printed
+       for; ((expr));{
+       }  // no semicolons and parens printed
+       for; ; expr = false {
+       }
+       for x :=expr; expr; {use(x)
+       }
+       for x := expr;; expr=false {use(x)
+       }
+       for;expr;expr =false {
+       }
+       for x := expr;expr;expr = false {
+       use(x)
+       }
+       for x := range []int{} {
+       use(x) }
+       for x := range (([]int{})) {
+       use(x) }  // no parens printed
+}
+
+
+// Formatting of selected short single- and multi-line statements.
+func _() {
+       if cond {}
+       if cond {
+       } // multiple lines
+       if cond {} else {} // else clause always requires multiple lines
+
+       for {}
+       for i := 0; i < len(a); 1++ {}
+       for i := 0; i < len(a); 1++ { a[i] = i }
+       for i := 0; i < len(a); 1++ { a[i] = i
+       } // multiple lines
+
+       for i := range a {}
+       for i := range a { a[i] = i }
+       for i := range a { a[i] = i
+       } // multiple lines
+
+       go func() { for { a <- <-b } }()
+       defer func() { if x := recover(); x != nil { err = fmt.Sprintf("error: %s", x.msg) } }()
+}
+
+
 // Don't remove mandatory parentheses around composite literals in control clauses.
 func _() {
        // strip parentheses - no composite literals or composite literals don't start with a type name
index a895a50aa9025da8b72482ad9045d84fb5a52e67..a9740931fc2aaee247cb4986dc0bf8ac8dd2057f 100644 (file)
@@ -14,7 +14,7 @@ import (
 )
 
 // nextJSCtx returns the context that determines whether a slash after the
-// given run of tokens tokens starts a regular expression instead of a division
+// given run of tokens starts a regular expression instead of a division
 // operator: / or /=.
 //
 // This assumes that the token run does not include any string tokens, comment
index 2fc64ade546dd82db9a4a0eb7c5557391de76533..9b731fdc4fa590eddbacf00cc078037870f82d23 100644 (file)
@@ -163,7 +163,7 @@ func (d *decoder) processDHT(n int) error {
 
 // Returns the next Huffman-coded value from the bit stream, decoded according to h.
 // TODO(nigeltao): This decoding algorithm is simple, but slow. A lookahead table, instead of always
-// peeling off only 1 bit at at time, ought to be faster.
+// peeling off only 1 bit at time, ought to be faster.
 func (d *decoder) decodeHuffman(h *huffman) (uint8, error) {
        if h.length == 0 {
                return 0, FormatError("uninitialized Huffman table")
index e5620e1aa2a27296792381896be77d32e34ea354..c4ad12ffcd12dd9d641906b4b073c9f6467a3688 100644 (file)
@@ -4,9 +4,9 @@
 
 // +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.
+// 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.
 package syslog
 
 import (
@@ -15,11 +15,21 @@ import (
        "log"
        "net"
        "os"
+       "time"
 )
 
+// The Priority is a combination of the syslog facility and
+// severity. For example, LOG_ALERT | LOG_FTP sends an alert severity
+// message from the FTP facility. The default severity is LOG_EMERG;
+// the default facility is LOG_KERN.
 type Priority int
 
+const severityMask = 0x07
+const facilityMask = 0xf8
+
 const (
+       // Severity.
+
        // From /usr/include/sys/syslog.h.
        // These are the same on Linux, BSD, and OS X.
        LOG_EMERG Priority = iota
@@ -32,16 +42,47 @@ const (
        LOG_DEBUG
 )
 
+const (
+       // Facility.
+
+       // From /usr/include/sys/syslog.h.
+       // These are the same up to LOG_FTP on Linux, BSD, and OS X.
+       LOG_KERN Priority = iota << 3
+       LOG_USER
+       LOG_MAIL
+       LOG_DAEMON
+       LOG_AUTH
+       LOG_SYSLOG
+       LOG_LPR
+       LOG_NEWS
+       LOG_UUCP
+       LOG_CRON
+       LOG_AUTHPRIV
+       LOG_FTP
+       _ // unused
+       _ // unused
+       _ // unused
+       _ // unused
+       LOG_LOCAL0
+       LOG_LOCAL1
+       LOG_LOCAL2
+       LOG_LOCAL3
+       LOG_LOCAL4
+       LOG_LOCAL5
+       LOG_LOCAL6
+       LOG_LOCAL7
+)
+
 // A Writer is a connection to a syslog server.
 type Writer struct {
        priority Priority
-       prefix   string
+       tag      string
+       hostname string
        conn     serverConn
 }
 
 type serverConn interface {
-       writeBytes(p Priority, prefix string, b []byte) (int, error)
-       writeString(p Priority, prefix string, s string) (int, error)
+       writeString(p Priority, hostname, tag, s string) (int, error)
        close() error
 }
 
@@ -49,116 +90,130 @@ type netConn struct {
        conn net.Conn
 }
 
-// New establishes a new connection to the system log daemon.
-// Each write to the returned writer sends a log message with
-// the given priority and prefix.
-func New(priority Priority, prefix string) (w *Writer, err error) {
-       return Dial("", "", priority, prefix)
+// New establishes a new connection to the system log daemon.  Each
+// write to the returned writer sends a log message with the given
+// priority and prefix.
+func New(priority Priority, tag string) (w *Writer, err error) {
+       return Dial("", "", priority, tag)
 }
 
-// Dial establishes a connection to a log daemon by connecting
-// to address raddr on the network net.
-// Each write to the returned writer sends a log message with
-// the given priority and prefix.
-func Dial(network, raddr string, priority Priority, prefix string) (w *Writer, err error) {
-       if prefix == "" {
-               prefix = os.Args[0]
+// Dial establishes a connection to a log daemon by connecting to
+// address raddr on the network net.  Each write to the returned
+// writer sends a log message with the given facility, severity and
+// tag.
+func Dial(network, raddr string, priority Priority, tag string) (w *Writer, err error) {
+       if priority < 0 || priority > LOG_LOCAL7|LOG_DEBUG {
+               return nil, errors.New("log/syslog: invalid priority")
        }
+
+       if tag == "" {
+               tag = os.Args[0]
+       }
+
+       hostname, _ := os.Hostname()
+
        var conn serverConn
        if network == "" {
                conn, err = unixSyslog()
+               if hostname == "" {
+                       hostname = "localhost"
+               }
        } else {
                var c net.Conn
                c, err = net.Dial(network, raddr)
                conn = netConn{c}
+               if hostname == "" {
+                       hostname = c.LocalAddr().String()
+               }
+       }
+       if err != nil {
+               return nil, err
        }
-       return &Writer{priority, prefix, conn}, err
+
+       return &Writer{priority: priority, tag: tag, hostname: hostname, conn: conn}, nil
 }
 
 // Write sends a log message to the syslog daemon.
 func (w *Writer) Write(b []byte) (int, error) {
-       if w.priority > LOG_DEBUG || w.priority < LOG_EMERG {
-               return 0, errors.New("log/syslog: invalid priority")
-       }
-       return w.conn.writeBytes(w.priority, w.prefix, b)
-}
-
-func (w *Writer) writeString(p Priority, s string) (int, error) {
-       return w.conn.writeString(p, w.prefix, s)
+       return w.writeString(w.priority, string(b))
 }
 
 func (w *Writer) Close() error { return w.conn.close() }
 
-// Emerg logs a message using the LOG_EMERG priority.
+// Emerg logs a message with severity LOG_EMERG, ignoring the severity
+// passed to New.
 func (w *Writer) Emerg(m string) (err error) {
        _, err = w.writeString(LOG_EMERG, m)
        return err
 }
 
-// Alert logs a message using the LOG_ALERT priority.
+// Alert logs a message with severity LOG_ALERT, ignoring the severity
+// passed to New.
 func (w *Writer) Alert(m string) (err error) {
        _, err = w.writeString(LOG_ALERT, m)
        return err
 }
 
-// Crit logs a message using the LOG_CRIT priority.
+// Crit logs a message with severity LOG_CRIT, ignoring the severity
+// passed to New.
 func (w *Writer) Crit(m string) (err error) {
        _, err = w.writeString(LOG_CRIT, m)
        return err
 }
 
-// Err logs a message using the LOG_ERR priority.
+// Err logs a message with severity LOG_ERR, ignoring the severity
+// passed to New.
 func (w *Writer) Err(m string) (err error) {
        _, err = w.writeString(LOG_ERR, m)
        return err
 }
 
-// Warning logs a message using the LOG_WARNING priority.
+// Wanring logs a message with severity LOG_WARNING, ignoring the
+// severity passed to New.
 func (w *Writer) Warning(m string) (err error) {
        _, err = w.writeString(LOG_WARNING, m)
        return err
 }
 
-// Notice logs a message using the LOG_NOTICE priority.
+// Notice logs a message with severity LOG_NOTICE, ignoring the
+// severity passed to New.
 func (w *Writer) Notice(m string) (err error) {
        _, err = w.writeString(LOG_NOTICE, m)
        return err
 }
 
-// Info logs a message using the LOG_INFO priority.
+// Info logs a message with severity LOG_INFO, ignoring the severity
+// passed to New.
 func (w *Writer) Info(m string) (err error) {
        _, err = w.writeString(LOG_INFO, m)
        return err
 }
 
-// Debug logs a message using the LOG_DEBUG priority.
+// Debug logs a message with severity LOG_DEBUG, ignoring the severity
+// passed to New.
 func (w *Writer) Debug(m string) (err error) {
        _, err = w.writeString(LOG_DEBUG, m)
        return err
 }
 
-func (n netConn) writeBytes(p Priority, prefix string, b []byte) (int, error) {
-       nl := ""
-       if len(b) == 0 || b[len(b)-1] != '\n' {
-               nl = "\n"
-       }
-       _, err := fmt.Fprintf(n.conn, "<%d>%s: %s%s", p, prefix, b, nl)
-       if err != nil {
-               return 0, err
-       }
-       return len(b), nil
+func (w *Writer) writeString(p Priority, s string) (int, error) {
+       return w.conn.writeString((w.priority&facilityMask)|(p&severityMask),
+               w.hostname, w.tag, s)
 }
 
-func (n netConn) writeString(p Priority, prefix string, s string) (int, error) {
+// writeString: generates and writes a syslog formatted string. The
+// format is as follows: <PRI>1 TIMESTAMP HOSTNAME TAG[PID]: MSG
+func (n netConn) writeString(p Priority, hostname, tag, msg string) (int, error) {
        nl := ""
-       if len(s) == 0 || s[len(s)-1] != '\n' {
+       if len(msg) == 0 || msg[len(msg)-1] != '\n' {
                nl = "\n"
        }
-       _, err := fmt.Fprintf(n.conn, "<%d>%s: %s%s", p, prefix, s, nl)
-       if err != nil {
+       timestamp := time.Now().Format(time.RFC3339)
+       if _, err := fmt.Fprintf(n.conn, "<%d>1 %s %s %s[%d]: %s%s", p, timestamp, hostname,
+               tag, os.Getpid(), msg, nl); err != nil {
                return 0, err
        }
-       return len(s), nil
+       return len(msg), nil
 }
 
 func (n netConn) close() error {
index b7579c363d32d8c8b10731384b83d09b49bd8af7..4c0bf1f4e7cd2752c7155d600e92871aaa9f5b7a 100644 (file)
@@ -7,9 +7,11 @@
 package syslog
 
 import (
+       "fmt"
        "io"
        "log"
        "net"
+       "os"
        "testing"
        "time"
 )
@@ -49,10 +51,14 @@ func skipNetTest(t *testing.T) bool {
 }
 
 func TestNew(t *testing.T) {
+       if LOG_LOCAL7 != 23<<3 {
+               t.Fatalf("LOG_LOCAL7 has wrong value")
+       }
        if skipNetTest(t) {
                return
        }
-       s, err := New(LOG_INFO, "")
+
+       s, err := New(LOG_INFO|LOG_USER, "")
        if err != nil {
                t.Fatalf("New() failed: %s", err)
        }
@@ -64,7 +70,7 @@ func TestNewLogger(t *testing.T) {
        if skipNetTest(t) {
                return
        }
-       f, err := NewLogger(LOG_INFO, 0)
+       f, err := NewLogger(LOG_USER|LOG_INFO, 0)
        if f == nil {
                t.Error(err)
        }
@@ -74,7 +80,15 @@ func TestDial(t *testing.T) {
        if skipNetTest(t) {
                return
        }
-       l, err := Dial("", "", LOG_ERR, "syslog_test")
+       f, err := Dial("", "", (LOG_LOCAL7|LOG_DEBUG)+1, "syslog_test")
+       if f != nil {
+               t.Fatalf("Should have trapped bad priority")
+       }
+       f, err = Dial("", "", -1, "syslog_test")
+       if f != nil {
+               t.Fatalf("Should have trapped bad priority")
+       }
+       l, err := Dial("", "", LOG_USER|LOG_ERR, "syslog_test")
        if err != nil {
                t.Fatalf("Dial() failed: %s", err)
        }
@@ -84,16 +98,23 @@ func TestDial(t *testing.T) {
 func TestUDPDial(t *testing.T) {
        done := make(chan string)
        startServer(done)
-       l, err := Dial("udp", serverAddr, LOG_INFO, "syslog_test")
+       l, err := Dial("udp", serverAddr, LOG_USER|LOG_INFO, "syslog_test")
        if err != nil {
                t.Fatalf("syslog.Dial() failed: %s", err)
        }
        msg := "udp test"
        l.Info(msg)
-       expected := "<6>syslog_test: udp test\n"
+       expected := fmt.Sprintf("<%d>1 ", LOG_USER+LOG_INFO) + "%s %s syslog_test[%d]: udp test\n"
        rcvd := <-done
-       if rcvd != expected {
-               t.Fatalf("s.Info() = '%q', but wanted '%q'", rcvd, expected)
+       var parsedHostname, timestamp string
+       var pid int
+       if hostname, err := os.Hostname(); err != nil {
+               t.Fatalf("Error retrieving hostname")
+       } else {
+               if n, err := fmt.Sscanf(rcvd, expected, &timestamp, &parsedHostname, &pid); n != 3 ||
+                       err != nil || hostname != parsedHostname {
+                       t.Fatalf("s.Info() = '%q', didn't match '%q'", rcvd, expected)
+               }
        }
 }
 
@@ -104,26 +125,34 @@ func TestWrite(t *testing.T) {
                msg string
                exp string
        }{
-               {LOG_ERR, "syslog_test", "", "<3>syslog_test: \n"},
-               {LOG_ERR, "syslog_test", "write test", "<3>syslog_test: write test\n"},
+               {LOG_USER | LOG_ERR, "syslog_test", "", "%s %s syslog_test[%d]: \n"},
+               {LOG_USER | LOG_ERR, "syslog_test", "write test", "%s %s syslog_test[%d]: write test\n"},
                // Write should not add \n if there already is one
-               {LOG_ERR, "syslog_test", "write test 2\n", "<3>syslog_test: write test 2\n"},
+               {LOG_USER | LOG_ERR, "syslog_test", "write test 2\n", "%s %s syslog_test[%d]: write test 2\n"},
        }
 
-       for _, test := range tests {
-               done := make(chan string)
-               startServer(done)
-               l, err := Dial("udp", serverAddr, test.pri, test.pre)
-               if err != nil {
-                       t.Fatalf("syslog.Dial() failed: %s", err)
-               }
-               _, err = io.WriteString(l, test.msg)
-               if err != nil {
-                       t.Fatalf("WriteString() failed: %s", err)
-               }
-               rcvd := <-done
-               if rcvd != test.exp {
-                       t.Fatalf("s.Info() = '%q', but wanted '%q'", rcvd, test.exp)
+       if hostname, err := os.Hostname(); err != nil {
+               t.Fatalf("Error retrieving hostname")
+       } else {
+               for _, test := range tests {
+                       done := make(chan string)
+                       startServer(done)
+                       l, err := Dial("udp", serverAddr, test.pri, test.pre)
+                       if err != nil {
+                               t.Fatalf("syslog.Dial() failed: %s", err)
+                       }
+                       _, err = io.WriteString(l, test.msg)
+                       if err != nil {
+                               t.Fatalf("WriteString() failed: %s", err)
+                       }
+                       rcvd := <-done
+                       test.exp = fmt.Sprintf("<%d>1 ", test.pri) + test.exp
+                       var parsedHostname, timestamp string
+                       var pid int
+                       if n, err := fmt.Sscanf(rcvd, test.exp, &timestamp, &parsedHostname, &pid); n != 3 ||
+                               err != nil || hostname != parsedHostname {
+                               t.Fatalf("s.Info() = '%q', didn't match '%q'", rcvd, test.exp)
+                       }
                }
        }
 }
index 6244eeefc91af70ae9292d34e4af305f19e50227..2dd7bf63968976f0c87f16fb2c6092bcf8e8ff67 100644 (file)
@@ -180,6 +180,7 @@ func allocBytes(f func()) uint64 {
 // does not cause deep recursion and in turn allocate too much memory.
 // Test case for issue 3807.
 func TestMulUnbalanced(t *testing.T) {
+       defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
        x := rndNat(50000)
        y := rndNat(40)
        allocSize := allocBytes(func() {
index fb07e1a56d52985ede360e57750cbc9e8e6f23c8..77e969b41b0ea7b924308572cf6bc3e5d941bee1 100644 (file)
@@ -37,6 +37,11 @@ type Part struct {
 
        disposition       string
        dispositionParams map[string]string
+
+       // r is either a reader directly reading from mr, or it's a
+       // wrapper around such a reader, decoding the
+       // Content-Transfer-Encoding
+       r io.Reader
 }
 
 // FormName returns the name parameter if p has a Content-Disposition
@@ -94,6 +99,12 @@ func newPart(mr *Reader) (*Part, error) {
        if err := bp.populateHeaders(); err != nil {
                return nil, err
        }
+       bp.r = partReader{bp}
+       const cte = "Content-Transfer-Encoding"
+       if bp.Header.Get(cte) == "quoted-printable" {
+               bp.Header.Del(cte)
+               bp.r = newQuotedPrintableReader(bp.r)
+       }
        return bp, nil
 }
 
@@ -109,6 +120,17 @@ func (bp *Part) populateHeaders() error {
 // Read reads the body of a part, after its headers and before the
 // next part (if any) begins.
 func (p *Part) Read(d []byte) (n int, err error) {
+       return p.r.Read(d)
+}
+
+// partReader implements io.Reader by reading raw bytes directly from the
+// wrapped *Part, without doing any Transfer-Encoding decoding.
+type partReader struct {
+       p *Part
+}
+
+func (pr partReader) Read(d []byte) (n int, err error) {
+       p := pr.p
        defer func() {
                p.bytesRead += n
        }()
index cd65e177e852e3a80539ec6c688ef0b14338ebbd..d662e834059d80afee347aa0214889f7a77f16cd 100644 (file)
@@ -339,9 +339,10 @@ func TestLineContinuation(t *testing.T) {
                if err != nil {
                        t.Fatalf("didn't get a part")
                }
-               n, err := io.Copy(ioutil.Discard, part)
+               var buf bytes.Buffer
+               n, err := io.Copy(&buf, part)
                if err != nil {
-                       t.Errorf("error reading part: %v", err)
+                       t.Errorf("error reading part: %v\nread so far: %q", err, buf.String())
                }
                if n <= 0 {
                        t.Errorf("read %d bytes; expected >0", n)
@@ -349,6 +350,29 @@ func TestLineContinuation(t *testing.T) {
        }
 }
 
+func TestQuotedPrintableEncoding(t *testing.T) {
+       // From http://golang.org/issue/4411
+       body := "--0016e68ee29c5d515f04cedf6733\r\nContent-Type: text/plain; charset=ISO-8859-1\r\nContent-Disposition: form-data; name=text\r\nContent-Transfer-Encoding: quoted-printable\r\n\r\nwords words words words words words words words words words words words wor=\r\nds words words words words words words words words words words words words =\r\nwords words words words words words words words words words words words wor=\r\nds words words words words words words words words words words words words =\r\nwords words words words words words words words words\r\n--0016e68ee29c5d515f04cedf6733\r\nContent-Type: text/plain; charset=ISO-8859-1\r\nContent-Disposition: form-data; name=submit\r\n\r\nSubmit\r\n--0016e68ee29c5d515f04cedf6733--"
+       r := NewReader(strings.NewReader(body), "0016e68ee29c5d515f04cedf6733")
+       part, err := r.NextPart()
+       if err != nil {
+               t.Fatal(err)
+       }
+       if te, ok := part.Header["Content-Transfer-Encoding"]; ok {
+               t.Errorf("unexpected Content-Transfer-Encoding of %q", te)
+       }
+       var buf bytes.Buffer
+       _, err = io.Copy(&buf, part)
+       if err != nil {
+               t.Error(err)
+       }
+       got := buf.String()
+       want := "words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words words"
+       if got != want {
+               t.Errorf("wrong part value:\n got: %q\nwant: %q", got, want)
+       }
+}
+
 // Test parsing an image attachment from gmail, which previously failed.
 func TestNested(t *testing.T) {
        // nested-mime is the body part of a multipart/mixed email
diff --git a/libgo/go/mime/multipart/quotedprintable.go b/libgo/go/mime/multipart/quotedprintable.go
new file mode 100644 (file)
index 0000000..0a60a6e
--- /dev/null
@@ -0,0 +1,92 @@
+// 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.
+
+// The file define a quoted-printable decoder, as specified in RFC 2045.
+
+package multipart
+
+import (
+       "bufio"
+       "bytes"
+       "fmt"
+       "io"
+)
+
+type qpReader struct {
+       br   *bufio.Reader
+       rerr error  // last read error
+       line []byte // to be consumed before more of br
+}
+
+func newQuotedPrintableReader(r io.Reader) io.Reader {
+       return &qpReader{
+               br: bufio.NewReader(r),
+       }
+}
+
+func fromHex(b byte) (byte, error) {
+       switch {
+       case b >= '0' && b <= '9':
+               return b - '0', nil
+       case b >= 'A' && b <= 'F':
+               return b - 'A' + 10, nil
+       }
+       return 0, fmt.Errorf("multipart: invalid quoted-printable hex byte 0x%02x", b)
+}
+
+func (q *qpReader) readHexByte(v []byte) (b byte, err error) {
+       if len(v) < 2 {
+               return 0, io.ErrUnexpectedEOF
+       }
+       var hb, lb byte
+       if hb, err = fromHex(v[0]); err != nil {
+               return 0, err
+       }
+       if lb, err = fromHex(v[1]); err != nil {
+               return 0, err
+       }
+       return hb<<4 | lb, nil
+}
+
+func isQPDiscardWhitespace(r rune) bool {
+       switch r {
+       case '\n', '\r', ' ', '\t':
+               return true
+       }
+       return false
+}
+
+func (q *qpReader) Read(p []byte) (n int, err error) {
+       for len(p) > 0 {
+               if len(q.line) == 0 {
+                       if q.rerr != nil {
+                               return n, q.rerr
+                       }
+                       q.line, q.rerr = q.br.ReadSlice('\n')
+                       q.line = bytes.TrimRightFunc(q.line, isQPDiscardWhitespace)
+                       continue
+               }
+               if len(q.line) == 1 && q.line[0] == '=' {
+                       // Soft newline; skipped.
+                       q.line = nil
+                       continue
+               }
+               b := q.line[0]
+               switch {
+               case b == '=':
+                       b, err = q.readHexByte(q.line[1:])
+                       if err != nil {
+                               return n, err
+                       }
+                       q.line = q.line[2:] // 2 of the 3; other 1 is done below
+               case b != '\t' && (b < ' ' || b > '~'):
+                       return n, fmt.Errorf("multipart: invalid unescaped byte 0x%02x in quoted-printable body", b)
+               }
+               p[0] = b
+               p = p[1:]
+               q.line = q.line[1:]
+               n++
+       }
+       return n, nil
+}
diff --git a/libgo/go/mime/multipart/quotedprintable_test.go b/libgo/go/mime/multipart/quotedprintable_test.go
new file mode 100644 (file)
index 0000000..796a41f
--- /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 multipart
+
+import (
+       "bytes"
+       "fmt"
+       "io"
+       "strings"
+       "testing"
+)
+
+func TestQuotedPrintable(t *testing.T) {
+       tests := []struct {
+               in, want string
+               err      interface{}
+       }{
+               {in: "foo bar", want: "foo bar"},
+               {in: "foo bar=3D", want: "foo bar="},
+               {in: "foo bar=0", want: "foo bar", err: io.ErrUnexpectedEOF},
+               {in: "foo bar=ab", want: "foo bar", err: "multipart: invalid quoted-printable hex byte 0x61"},
+               {in: "foo bar=0D=0A", want: "foo bar\r\n"},
+               {in: "foo bar=\r\n baz", want: "foo bar baz"},
+               {in: "foo=\nbar", want: "foobar"},
+               {in: "foo\x00bar", want: "foo", err: "multipart: invalid unescaped byte 0x00 in quoted-printable body"},
+               {in: "foo bar\xff", want: "foo bar", err: "multipart: invalid unescaped byte 0xff in quoted-printable body"},
+       }
+       for _, tt := range tests {
+               var buf bytes.Buffer
+               _, err := io.Copy(&buf, newQuotedPrintableReader(strings.NewReader(tt.in)))
+               if got := buf.String(); got != tt.want {
+                       t.Errorf("for %q, got %q; want %q", tt.in, got, tt.want)
+               }
+               switch verr := tt.err.(type) {
+               case nil:
+                       if err != nil {
+                               t.Errorf("for %q, got unexpected error: %v", tt.in, err)
+                       }
+               case string:
+                       if got := fmt.Sprint(err); got != verr {
+                               t.Errorf("for %q, got error %q; want %q", tt.in, got, verr)
+                       }
+               case error:
+                       if err != verr {
+                               t.Errorf("for %q, got error %q; want %q", tt.in, err, verr)
+                       }
+               }
+       }
+
+}
index ec70be492f5c5f6bc8a0a597457af1a5862183d3..e13a956afeed0c12a6029520d663eab50f549cf5 100644 (file)
@@ -30,11 +30,38 @@ func NewWriter(w io.Writer) *Writer {
        }
 }
 
-// Boundary returns the Writer's randomly selected boundary string.
+// Boundary returns the Writer's boundary.
 func (w *Writer) Boundary() string {
        return w.boundary
 }
 
+// SetBoundary overrides the Writer's default randomly-generated
+// boundary separator with an explicit value.
+//
+// SetBoundary must be called before any parts are created, may only
+// contain certain ASCII characters, and must be 1-69 bytes long.
+func (w *Writer) SetBoundary(boundary string) error {
+       if w.lastpart != nil {
+               return errors.New("mime: SetBoundary called after write")
+       }
+       // rfc2046#section-5.1.1
+       if len(boundary) < 1 || len(boundary) > 69 {
+               return errors.New("mime: invalid boundary length")
+       }
+       for _, b := range boundary {
+               if 'A' <= b && b <= 'Z' || 'a' <= b && b <= 'z' || '0' <= b && b <= '9' {
+                       continue
+               }
+               switch b {
+               case '\'', '(', ')', '+', '_', ',', '-', '.', '/', ':', '=', '?':
+                       continue
+               }
+               return errors.New("mime: invalid boundary character")
+       }
+       w.boundary = boundary
+       return nil
+}
+
 // FormDataContentType returns the Content-Type for an HTTP
 // multipart/form-data with this Writer's Boundary.
 func (w *Writer) FormDataContentType() string {
index 494e936c4c6de686360378fd7772906d0ff78d46..52d68bcb68c580415affd3d59c1906d80330985a 100644 (file)
@@ -7,6 +7,7 @@ package multipart
 import (
        "bytes"
        "io/ioutil"
+       "strings"
        "testing"
 )
 
@@ -76,3 +77,37 @@ func TestWriter(t *testing.T) {
                t.Fatalf("expected end of parts; got %v, %v", part, err)
        }
 }
+
+func TestWriterSetBoundary(t *testing.T) {
+       var b bytes.Buffer
+       w := NewWriter(&b)
+       tests := []struct {
+               b  string
+               ok bool
+       }{
+               {"abc", true},
+               {"", false},
+               {"ungültig", false},
+               {"!", false},
+               {strings.Repeat("x", 69), true},
+               {strings.Repeat("x", 70), false},
+               {"bad!ascii!", false},
+               {"my-separator", true},
+       }
+       for i, tt := range tests {
+               err := w.SetBoundary(tt.b)
+               got := err == nil
+               if got != tt.ok {
+                       t.Errorf("%d. boundary %q = %v (%v); want %v", i, tt.b, got, err, tt.ok)
+               } else if tt.ok {
+                       got := w.Boundary()
+                       if got != tt.b {
+                               t.Errorf("boundary = %q; want %q", got, tt.b)
+                       }
+               }
+       }
+       w.Close()
+       if got := b.String(); !strings.Contains(got, "\r\n--my-separator--\r\n") {
+               t.Errorf("expected my-separator in output. got: %q", got)
+       }
+}
index a85e3c673bfe835f096a86b5a5497e1dcf93d43b..0c4608462e0e216837131c9e3918c272fdebfeb3 100644 (file)
@@ -15,6 +15,7 @@ func parseDialNetwork(net string) (afnet string, proto int, err error) {
                switch net {
                case "tcp", "tcp4", "tcp6":
                case "udp", "udp4", "udp6":
+               case "ip", "ip4", "ip6":
                case "unix", "unixgram", "unixpacket":
                default:
                        return "", 0, UnknownNetworkError(net)
@@ -54,12 +55,8 @@ func resolveAfnetAddr(afnet, addr string, deadline time.Time) (Addr, error) {
                return nil, nil
        }
        switch afnet {
-       case "tcp", "tcp4", "tcp6":
-               return resolveTCPAddr(afnet, addr, deadline)
-       case "udp", "udp4", "udp6":
-               return resolveUDPAddr(afnet, addr, deadline)
-       case "ip", "ip4", "ip6":
-               return resolveIPAddr(afnet, addr, deadline)
+       case "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6", "ip", "ip4", "ip6":
+               return resolveInternetAddr(afnet, addr, deadline)
        case "unix", "unixgram", "unixpacket":
                return ResolveUnixAddr(afnet, addr)
        }
@@ -218,8 +215,8 @@ func Listen(net, laddr string) (Listener, error) {
 // ListenPacket announces on the local network address laddr.
 // The network string net must be a packet-oriented network:
 // "udp", "udp4", "udp6", "ip", "ip4", "ip6" or "unixgram".
-func ListenPacket(net, addr string) (PacketConn, error) {
-       afnet, a, err := resolveNetAddr("listen", net, addr, noDeadline)
+func ListenPacket(net, laddr string) (PacketConn, error) {
+       afnet, a, err := resolveNetAddr("listen", net, laddr, noDeadline)
        if err != nil {
                return nil, err
        }
diff --git a/libgo/go/net/fd_posix_test.go b/libgo/go/net/fd_posix_test.go
new file mode 100644 (file)
index 0000000..8be0335
--- /dev/null
@@ -0,0 +1,57 @@
+// 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 darwin freebsd linux netbsd openbsd windows
+
+package net
+
+import (
+       "testing"
+       "time"
+)
+
+var deadlineSetTimeTests = []struct {
+       input    time.Time
+       expected int64
+}{
+       {time.Time{}, 0},
+       {time.Date(2009, 11, 10, 23, 00, 00, 00, time.UTC), 1257894000000000000}, // 2009-11-10 23:00:00 +0000 UTC
+}
+
+func TestDeadlineSetTime(t *testing.T) {
+       for _, tt := range deadlineSetTimeTests {
+               var d deadline
+               d.setTime(tt.input)
+               actual := d.value()
+               expected := int64(0)
+               if !tt.input.IsZero() {
+                       expected = tt.input.UnixNano()
+               }
+               if actual != expected {
+                       t.Errorf("set/value failed: expected %v, actual %v", expected, actual)
+               }
+       }
+}
+
+var deadlineExpiredTests = []struct {
+       deadline time.Time
+       expired  bool
+}{
+       // note, times are relative to the start of the test run, not
+       // the start of TestDeadlineExpired
+       {time.Now().Add(5 * time.Minute), false},
+       {time.Now().Add(-5 * time.Minute), true},
+       {time.Time{}, false}, // no deadline set
+}
+
+func TestDeadlineExpired(t *testing.T) {
+       for _, tt := range deadlineExpiredTests {
+               var d deadline
+               d.set(tt.deadline.UnixNano())
+               expired := d.expired()
+               if expired != tt.expired {
+                       t.Errorf("expire failed: expected %v, actual %v", tt.expired, expired)
+               }
+       }
+}
index 096ad41bbfad340f7f4ffe8572ab3e8913eb4aaa..6d8af0ab2e26b6b7abb58462d4733e24b73bb74a 100644 (file)
@@ -37,11 +37,11 @@ type netFD struct {
        laddr       Addr
        raddr       Addr
 
-       // owned by client
-       rdeadline int64
-       rio       sync.Mutex
-       wdeadline int64
-       wio       sync.Mutex
+       // serialize access to Read and Write methods
+       rio, wio sync.Mutex
+
+       // read and write deadlines
+       rdeadline, wdeadline deadline
 
        // owned by fd wait server
        ncr, ncw int
@@ -82,11 +82,11 @@ func (s *pollServer) AddFD(fd *netFD, mode int) error {
        key := intfd << 1
        if mode == 'r' {
                fd.ncr++
-               t = fd.rdeadline
+               t = fd.rdeadline.value()
        } else {
                fd.ncw++
                key++
-               t = fd.wdeadline
+               t = fd.wdeadline.value()
        }
        s.pending[key] = fd
        doWakeup := false
@@ -153,12 +153,8 @@ func (s *pollServer) WakeFD(fd *netFD, mode int, err error) {
        }
 }
 
-func (s *pollServer) Now() int64 {
-       return time.Now().UnixNano()
-}
-
 func (s *pollServer) CheckDeadlines() {
-       now := s.Now()
+       now := time.Now().UnixNano()
        // TODO(rsc): This will need to be handled more efficiently,
        // probably with a heap indexed by wakeup time.
 
@@ -172,21 +168,19 @@ func (s *pollServer) CheckDeadlines() {
                        mode = 'w'
                }
                if mode == 'r' {
-                       t = fd.rdeadline
+                       t = fd.rdeadline.value()
                } else {
-                       t = fd.wdeadline
+                       t = fd.wdeadline.value()
                }
                if t > 0 {
                        if t <= now {
                                delete(s.pending, key)
                                if mode == 'r' {
                                        s.poll.DelFD(fd.sysfd, mode)
-                                       fd.rdeadline = -1
                                } else {
                                        s.poll.DelFD(fd.sysfd, mode)
-                                       fd.wdeadline = -1
                                }
-                               s.WakeFD(fd, mode, nil)
+                               s.WakeFD(fd, mode, errTimeout)
                        } else if nextDeadline == 0 || t < nextDeadline {
                                nextDeadline = t
                        }
@@ -200,15 +194,15 @@ func (s *pollServer) Run() {
        s.Lock()
        defer s.Unlock()
        for {
-               var t = s.deadline
-               if t > 0 {
-                       t = t - s.Now()
-                       if t <= 0 {
+               var timeout int64 // nsec to wait for or 0 for none
+               if s.deadline > 0 {
+                       timeout = s.deadline - time.Now().UnixNano()
+                       if timeout <= 0 {
                                s.CheckDeadlines()
                                continue
                        }
                }
-               fd, mode, err := s.poll.WaitFD(s, t)
+               fd, mode, err := s.poll.WaitFD(s, timeout)
                if err != nil {
                        print("pollServer WaitFD: ", err.Error(), "\n")
                        return
@@ -329,14 +323,10 @@ func (fd *netFD) name() string {
 
 func (fd *netFD) connect(ra syscall.Sockaddr) error {
        err := syscall.Connect(fd.sysfd, ra)
-       hadTimeout := fd.wdeadline > 0
        if err == syscall.EINPROGRESS {
                if err = fd.pollServer.WaitWrite(fd); err != nil {
                        return err
                }
-               if hadTimeout && fd.wdeadline < 0 {
-                       return errTimeout
-               }
                var e int
                e, err = syscall.GetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_ERROR)
                if err != nil {
@@ -381,8 +371,8 @@ func (fd *netFD) decref() {
 
 func (fd *netFD) Close() error {
        fd.pollServer.Lock() // needed for both fd.incref(true) and pollserver.Evict
-       defer fd.pollServer.Unlock()
        if err := fd.incref(true); err != nil {
+               fd.pollServer.Unlock()
                return err
        }
        // Unblock any I/O.  Once it all unblocks and returns,
@@ -391,6 +381,7 @@ func (fd *netFD) Close() error {
        // fairly quickly, since all the I/O is non-blocking, and any
        // attempts to block in the pollserver will return errClosing.
        fd.pollServer.Evict(fd)
+       fd.pollServer.Unlock()
        fd.decref()
        return nil
 }
@@ -423,20 +414,20 @@ func (fd *netFD) Read(p []byte) (n int, err error) {
        }
        defer fd.decref()
        for {
-               n, err = syscall.Read(int(fd.sysfd), p)
-               if err == syscall.EAGAIN {
+               if fd.rdeadline.expired() {
                        err = errTimeout
-                       if fd.rdeadline >= 0 {
+                       break
+               }
+               n, err = syscall.Read(int(fd.sysfd), p)
+               if err != nil {
+                       n = 0
+                       if err == syscall.EAGAIN {
                                if err = fd.pollServer.WaitRead(fd); err == nil {
                                        continue
                                }
                        }
                }
-               if err != nil {
-                       n = 0
-               } else if n == 0 && err == nil && fd.sotype != syscall.SOCK_DGRAM {
-                       err = io.EOF
-               }
+               err = chkReadErr(n, err, fd)
                break
        }
        if err != nil && err != io.EOF {
@@ -453,18 +444,20 @@ func (fd *netFD) ReadFrom(p []byte) (n int, sa syscall.Sockaddr, err error) {
        }
        defer fd.decref()
        for {
-               n, sa, err = syscall.Recvfrom(fd.sysfd, p, 0)
-               if err == syscall.EAGAIN {
+               if fd.rdeadline.expired() {
                        err = errTimeout
-                       if fd.rdeadline >= 0 {
+                       break
+               }
+               n, sa, err = syscall.Recvfrom(fd.sysfd, p, 0)
+               if err != nil {
+                       n = 0
+                       if err == syscall.EAGAIN {
                                if err = fd.pollServer.WaitRead(fd); err == nil {
                                        continue
                                }
                        }
                }
-               if err != nil {
-                       n = 0
-               }
+               err = chkReadErr(n, err, fd)
                break
        }
        if err != nil && err != io.EOF {
@@ -481,41 +474,47 @@ func (fd *netFD) ReadMsg(p []byte, oob []byte) (n, oobn, flags int, sa syscall.S
        }
        defer fd.decref()
        for {
-               n, oobn, flags, sa, err = syscall.Recvmsg(fd.sysfd, p, oob, 0)
-               if err == syscall.EAGAIN {
+               if fd.rdeadline.expired() {
                        err = errTimeout
-                       if fd.rdeadline >= 0 {
+                       break
+               }
+               n, oobn, flags, sa, err = syscall.Recvmsg(fd.sysfd, p, oob, 0)
+               if err != nil {
+                       // TODO(dfc) should n and oobn be set to 0
+                       if err == syscall.EAGAIN {
                                if err = fd.pollServer.WaitRead(fd); err == nil {
                                        continue
                                }
                        }
                }
-               if err == nil && n == 0 {
-                       err = io.EOF
-               }
+               err = chkReadErr(n, err, fd)
                break
        }
        if err != nil && err != io.EOF {
                err = &OpError{"read", fd.net, fd.laddr, err}
-               return
        }
        return
 }
 
-func (fd *netFD) Write(p []byte) (int, error) {
+func chkReadErr(n int, err error, fd *netFD) error {
+       if n == 0 && err == nil && fd.sotype != syscall.SOCK_DGRAM && fd.sotype != syscall.SOCK_RAW {
+               return io.EOF
+       }
+       return err
+}
+
+func (fd *netFD) Write(p []byte) (nn int, err error) {
        fd.wio.Lock()
        defer fd.wio.Unlock()
        if err := fd.incref(false); err != nil {
                return 0, err
        }
        defer fd.decref()
-       if fd.sysfile == nil {
-               return 0, syscall.EINVAL
-       }
-
-       var err error
-       nn := 0
        for {
+               if fd.wdeadline.expired() {
+                       err = errTimeout
+                       break
+               }
                var n int
                n, err = syscall.Write(int(fd.sysfd), p[nn:])
                if n > 0 {
@@ -525,11 +524,8 @@ func (fd *netFD) Write(p []byte) (int, error) {
                        break
                }
                if err == syscall.EAGAIN {
-                       err = errTimeout
-                       if fd.wdeadline >= 0 {
-                               if err = fd.pollServer.WaitWrite(fd); err == nil {
-                                       continue
-                               }
+                       if err = fd.pollServer.WaitWrite(fd); err == nil {
+                               continue
                        }
                }
                if err != nil {
@@ -555,13 +551,14 @@ func (fd *netFD) WriteTo(p []byte, sa syscall.Sockaddr) (n int, err error) {
        }
        defer fd.decref()
        for {
+               if fd.wdeadline.expired() {
+                       err = errTimeout
+                       break
+               }
                err = syscall.Sendto(fd.sysfd, p, 0, sa)
                if err == syscall.EAGAIN {
-                       err = errTimeout
-                       if fd.wdeadline >= 0 {
-                               if err = fd.pollServer.WaitWrite(fd); err == nil {
-                                       continue
-                               }
+                       if err = fd.pollServer.WaitWrite(fd); err == nil {
+                               continue
                        }
                }
                break
@@ -582,13 +579,14 @@ func (fd *netFD) WriteMsg(p []byte, oob []byte, sa syscall.Sockaddr) (n int, oob
        }
        defer fd.decref()
        for {
+               if fd.wdeadline.expired() {
+                       err = errTimeout
+                       break
+               }
                err = syscall.Sendmsg(fd.sysfd, p, oob, sa, 0)
                if err == syscall.EAGAIN {
-                       err = errTimeout
-                       if fd.wdeadline >= 0 {
-                               if err = fd.pollServer.WaitWrite(fd); err == nil {
-                                       continue
-                               }
+                       if err = fd.pollServer.WaitWrite(fd); err == nil {
+                               continue
                        }
                }
                break
@@ -619,11 +617,8 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (netfd *netFD, err e
                if err != nil {
                        syscall.ForkLock.RUnlock()
                        if err == syscall.EAGAIN {
-                               err = errTimeout
-                               if fd.rdeadline >= 0 {
-                                       if err = fd.pollServer.WaitRead(fd); err == nil {
-                                               continue
-                                       }
+                               if err = fd.pollServer.WaitRead(fd); err == nil {
+                                       continue
                                }
                        } else if err == syscall.ECONNABORTED {
                                // This means that a socket on the listen queue was closed
index d1eb573d0040f0cae2078dcba9e42f1ed41123b9..fd1385ef9342d65e3bbb1a71f04b10614cb636f2 100644 (file)
@@ -7,33 +7,34 @@
 package net
 
 import (
+       "io"
+       "syscall"
        "testing"
 )
 
 // Issue 3590. netFd.AddFD should return an error
 // from the underlying pollster rather than panicing.
 func TestAddFDReturnsError(t *testing.T) {
-       l, err := Listen("tcp", "127.0.0.1:0")
-       if err != nil {
-               t.Fatal(err)
-       }
-       defer l.Close()
-
+       ln := newLocalListener(t).(*TCPListener)
+       defer ln.Close()
+       connected := make(chan bool)
        go func() {
                for {
-                       c, err := l.Accept()
+                       c, err := ln.Accept()
                        if err != nil {
                                return
                        }
+                       connected <- true
                        defer c.Close()
                }
        }()
 
-       c, err := Dial("tcp", l.Addr().String())
+       c, err := DialTCP("tcp", nil, ln.Addr().(*TCPAddr))
        if err != nil {
                t.Fatal(err)
        }
        defer c.Close()
+       <-connected
 
        // replace c's pollServer with a closed version.
        ps, err := newPollServer()
@@ -41,7 +42,7 @@ func TestAddFDReturnsError(t *testing.T) {
                t.Fatal(err)
        }
        ps.poll.Close()
-       c.(*TCPConn).conn.fd.pollServer = ps
+       c.conn.fd.pollServer = ps
 
        var b [1]byte
        _, err = c.Read(b[:])
@@ -56,5 +57,50 @@ func TestAddFDReturnsError(t *testing.T) {
                        }
                }
        }
-       t.Error(err)
+       t.Error("unexpected error:", err)
+}
+
+var chkReadErrTests = []struct {
+       n        int
+       err      error
+       fd       *netFD
+       expected error
+}{
+
+       {100, nil, &netFD{sotype: syscall.SOCK_STREAM}, nil},
+       {100, io.EOF, &netFD{sotype: syscall.SOCK_STREAM}, io.EOF},
+       {100, errClosing, &netFD{sotype: syscall.SOCK_STREAM}, errClosing},
+       {0, nil, &netFD{sotype: syscall.SOCK_STREAM}, io.EOF},
+       {0, io.EOF, &netFD{sotype: syscall.SOCK_STREAM}, io.EOF},
+       {0, errClosing, &netFD{sotype: syscall.SOCK_STREAM}, errClosing},
+
+       {100, nil, &netFD{sotype: syscall.SOCK_DGRAM}, nil},
+       {100, io.EOF, &netFD{sotype: syscall.SOCK_DGRAM}, io.EOF},
+       {100, errClosing, &netFD{sotype: syscall.SOCK_DGRAM}, errClosing},
+       {0, nil, &netFD{sotype: syscall.SOCK_DGRAM}, nil},
+       {0, io.EOF, &netFD{sotype: syscall.SOCK_DGRAM}, io.EOF},
+       {0, errClosing, &netFD{sotype: syscall.SOCK_DGRAM}, errClosing},
+
+       {100, nil, &netFD{sotype: syscall.SOCK_SEQPACKET}, nil},
+       {100, io.EOF, &netFD{sotype: syscall.SOCK_SEQPACKET}, io.EOF},
+       {100, errClosing, &netFD{sotype: syscall.SOCK_SEQPACKET}, errClosing},
+       {0, nil, &netFD{sotype: syscall.SOCK_SEQPACKET}, io.EOF},
+       {0, io.EOF, &netFD{sotype: syscall.SOCK_SEQPACKET}, io.EOF},
+       {0, errClosing, &netFD{sotype: syscall.SOCK_SEQPACKET}, errClosing},
+
+       {100, nil, &netFD{sotype: syscall.SOCK_RAW}, nil},
+       {100, io.EOF, &netFD{sotype: syscall.SOCK_RAW}, io.EOF},
+       {100, errClosing, &netFD{sotype: syscall.SOCK_RAW}, errClosing},
+       {0, nil, &netFD{sotype: syscall.SOCK_RAW}, nil},
+       {0, io.EOF, &netFD{sotype: syscall.SOCK_RAW}, io.EOF},
+       {0, errClosing, &netFD{sotype: syscall.SOCK_RAW}, errClosing},
+}
+
+func TestChkReadErr(t *testing.T) {
+       for _, tt := range chkReadErrTests {
+               actual := chkReadErr(tt.n, tt.err, tt.fd)
+               if actual != tt.expected {
+                       t.Errorf("chkReadError(%v, %v, %v): expected %v, actual %v", tt.n, tt.err, tt.fd.sotype, tt.expected, actual)
+               }
+       }
 }
index 5338def92205be12bad90de2b038eb14d132e63d..18712191fee18b5c03bde470ae374c39283b53d1 100644 (file)
@@ -169,6 +169,15 @@ func (s *ioSrv) ProcessRemoteIO() {
 func (s *ioSrv) ExecIO(oi anOpIface, deadline int64) (int, error) {
        var err error
        o := oi.Op()
+       // Calculate timeout delta.
+       var delta int64
+       if deadline != 0 {
+               delta = deadline - time.Now().UnixNano()
+               if delta <= 0 {
+                       return 0, &OpError{oi.Name(), o.fd.net, o.fd.laddr, errTimeout}
+               }
+       }
+       // Start IO.
        if canCancelIO {
                err = oi.Submit()
        } else {
@@ -188,12 +197,8 @@ func (s *ioSrv) ExecIO(oi anOpIface, deadline int64) (int, error) {
        }
        // Setup timer, if deadline is given.
        var timer <-chan time.Time
-       if deadline != 0 {
-               dt := deadline - time.Now().UnixNano()
-               if dt < 1 {
-                       dt = 1
-               }
-               t := time.NewTimer(time.Duration(dt) * time.Nanosecond)
+       if delta > 0 {
+               t := time.NewTimer(time.Duration(delta) * time.Nanosecond)
                defer t.Stop()
                timer = t.C
        }
@@ -280,11 +285,11 @@ type netFD struct {
        errnoc      [2]chan error    // read/write submit or cancel operation errors
        closec      chan bool        // used by Close to cancel pending IO
 
-       // owned by client
-       rdeadline int64
-       rio       sync.Mutex
-       wdeadline int64
-       wio       sync.Mutex
+       // serialize access to Read and Write methods
+       rio, wio sync.Mutex
+
+       // read and write deadlines
+       rdeadline, wdeadline deadline
 }
 
 func allocFD(fd syscall.Handle, family, sotype int, net string) *netFD {
@@ -295,7 +300,6 @@ func allocFD(fd syscall.Handle, family, sotype int, net string) *netFD {
                net:    net,
                closec: make(chan bool),
        }
-       runtime.SetFinalizer(netfd, (*netFD).Close)
        return netfd
 }
 
@@ -314,6 +318,7 @@ func newFD(fd syscall.Handle, family, proto int, net string) (*netFD, error) {
 func (fd *netFD) setAddr(laddr, raddr Addr) {
        fd.laddr = laddr
        fd.raddr = raddr
+       runtime.SetFinalizer(fd, (*netFD).closesocket)
 }
 
 func (fd *netFD) connect(ra syscall.Sockaddr) error {
@@ -393,6 +398,10 @@ func (fd *netFD) CloseWrite() error {
        return fd.shutdown(syscall.SHUT_WR)
 }
 
+func (fd *netFD) closesocket() error {
+       return closesocket(fd.sysfd)
+}
+
 // Read from network.
 
 type readOp struct {
@@ -417,7 +426,7 @@ func (fd *netFD) Read(buf []byte) (int, error) {
        defer fd.rio.Unlock()
        var o readOp
        o.Init(fd, buf, 'r')
-       n, err := iosrv.ExecIO(&o, fd.rdeadline)
+       n, err := iosrv.ExecIO(&o, fd.rdeadline.value())
        if err == nil && n == 0 {
                err = io.EOF
        }
@@ -454,7 +463,7 @@ func (fd *netFD) ReadFrom(buf []byte) (n int, sa syscall.Sockaddr, err error) {
        var o readFromOp
        o.Init(fd, buf, 'r')
        o.rsan = int32(unsafe.Sizeof(o.rsa))
-       n, err = iosrv.ExecIO(&o, fd.rdeadline)
+       n, err = iosrv.ExecIO(&o, fd.rdeadline.value())
        if err != nil {
                return 0, nil, err
        }
@@ -486,7 +495,7 @@ func (fd *netFD) Write(buf []byte) (int, error) {
        defer fd.wio.Unlock()
        var o writeOp
        o.Init(fd, buf, 'w')
-       return iosrv.ExecIO(&o, fd.wdeadline)
+       return iosrv.ExecIO(&o, fd.wdeadline.value())
 }
 
 // WriteTo to network.
@@ -518,7 +527,7 @@ func (fd *netFD) WriteTo(buf []byte, sa syscall.Sockaddr) (int, error) {
        var o writeToOp
        o.Init(fd, buf, 'w')
        o.sa = sa
-       return iosrv.ExecIO(&o, fd.wdeadline)
+       return iosrv.ExecIO(&o, fd.wdeadline.value())
 }
 
 // Accept new network connections.
@@ -552,7 +561,7 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (*netFD, error) {
        s, err := syscall.Socket(fd.family, fd.sotype, 0)
        if err != nil {
                syscall.ForkLock.RUnlock()
-               return nil, err
+               return nil, &OpError{"socket", fd.net, fd.laddr, err}
        }
        syscall.CloseOnExec(s)
        syscall.ForkLock.RUnlock()
@@ -560,6 +569,7 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (*netFD, error) {
        // Associate our new socket with IOCP.
        onceStartServer.Do(startServer)
        if _, err := syscall.CreateIoCompletionPort(s, resultsrv.iocp, 0, 0); err != nil {
+               closesocket(s)
                return nil, &OpError{"CreateIoCompletionPort", fd.net, fd.laddr, err}
        }
 
@@ -567,7 +577,7 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (*netFD, error) {
        var o acceptOp
        o.Init(fd, 'r')
        o.newsock = s
-       _, err = iosrv.ExecIO(&o, fd.rdeadline)
+       _, err = iosrv.ExecIO(&o, fd.rdeadline.value())
        if err != nil {
                closesocket(s)
                return nil, err
@@ -577,7 +587,7 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (*netFD, error) {
        err = syscall.Setsockopt(s, syscall.SOL_SOCKET, syscall.SO_UPDATE_ACCEPT_CONTEXT, (*byte)(unsafe.Pointer(&fd.sysfd)), int32(unsafe.Sizeof(fd.sysfd)))
        if err != nil {
                closesocket(s)
-               return nil, err
+               return nil, &OpError{"Setsockopt", fd.net, fd.laddr, err}
        }
 
        // Get local and peer addr out of AcceptEx buffer.
index 04f7ee0401b7f86a7fe0e6b71b00705aa00dffea..ae3ac156b9878d02dc23ada735475e07f4a7d67c 100644 (file)
@@ -19,8 +19,8 @@ func FileConn(f *os.File) (c Conn, err error) {
 
 // FileListener returns a copy of the network listener corresponding
 // to the open file f.  It is the caller's responsibility to close l
-// when finished.  Closing c does not affect l, and closing l does not
-// affect c.
+// when finished.  Closing l does not affect f, and closing f does not
+// affect l.
 func FileListener(f *os.File) (l Listener, err error) {
        return nil, syscall.EPLAN9
 }
index 859911f9805bede12c5a99ad19a17339be252db4..85b52c9fdbc09a3ba30fce6aeed635199e190889 100644 (file)
@@ -405,7 +405,8 @@ func TestDirUnix(t *testing.T) {
 }
 
 func TestDirWindows(t *testing.T) {
-       if skipTest(t) || runtime.GOOS != "windows" {
+       if runtime.GOOS != "windows" {
+               t.Logf("Skipping windows specific test.")
                return
        }
 
@@ -415,6 +416,7 @@ func TestDirWindows(t *testing.T) {
        var err error
        perl, err = exec.LookPath("perl")
        if err != nil {
+               t.Logf("Skipping test: perl not found.")
                return
        }
        perl, _ = filepath.Abs(perl)
@@ -457,6 +459,7 @@ func TestEnvOverride(t *testing.T) {
        var err error
        perl, err = exec.LookPath("perl")
        if err != nil {
+               t.Logf("Skipping test: perl not found.")
                return
        }
        perl, _ = filepath.Abs(perl)
index dcefe4e7af55c29d69f6e8157ddf8068c1419b3b..1b25bc29996993cd3f3127ec11173f62c0903db6 100644 (file)
@@ -10,23 +10,6 @@ use Cwd;
 
 binmode STDOUT;
 
-sub on_windows {
-    return $^O eq 'MSWin32' || $^O eq 'msys';
-}
-
-# normalize_windows_path normalizes the various Windows Perl path
-# formats into Go's format.
-sub normalize_windows_path {
-    my $dir = shift;
-    return $dir unless on_windows();
-    $dir =~ s!^[a-z]:!uc($&)!e;
-    if ($dir =~ s!^/([a-zA-Z])/!!) {
-        $dir = uc($1) . ":\\$dir";
-    }
-    $dir =~ s!/!\\!g;
-    return $dir;
-}
-
 my $q = MiniCGI->new;
 my $params = $q->Vars;
 
@@ -35,40 +18,43 @@ if ($params->{"loc"}) {
     exit(0);
 }
 
-my $NL = "\r\n";
-$NL = "\n" if $params->{mode} eq "NL";
-
-my $p = sub {
-    print "$_[0]$NL";
-};
-
-# With carriage returns
-$p->("Content-Type: text/html");
-$p->("X-CGI-Pid: $$");
-$p->("X-Test-Header: X-Test-Value");
-$p->("");
+print "Content-Type: text/html\r\n";
+print "X-CGI-Pid: $$\r\n";
+print "X-Test-Header: X-Test-Value\r\n";
+print "\r\n";
 
 if ($params->{"bigresponse"}) {
     for (1..1024) {
-        print "A" x 1024, "\n";
+        print "A" x 1024, "\r\n";
     }
     exit 0;
 }
 
-print "test=Hello CGI\n";
+print "test=Hello CGI\r\n";
 
 foreach my $k (sort keys %$params) {
-    print "param-$k=$params->{$k}\n";
+    print "param-$k=$params->{$k}\r\n";
 }
 
 foreach my $k (sort keys %ENV) {
     my $clean_env = $ENV{$k};
     $clean_env =~ s/[\n\r]//g;
-    print "env-$k=$clean_env\n";
+    print "env-$k=$clean_env\r\n";
 }
 
-my $dir = normalize_windows_path(getcwd());
-print "cwd=$dir\n";
+# NOTE: msys perl returns /c/go/src/... not C:\go\....
+my $dir = getcwd();
+if ($^O eq 'MSWin32' || $^O eq 'msys') {
+    if ($dir =~ /^.:/) {
+        $dir =~ s!/!\\!g;
+    } else {
+        my $cmd = $ENV{'COMSPEC'} || 'c:\\windows\\system32\\cmd.exe';
+        $cmd =~ s!\\!/!g;
+        $dir = `$cmd /c cd`;
+        chomp $dir;
+    }
+}
+print "cwd=$dir\r\n";
 
 # A minimal version of CGI.pm, for people without the perl-modules
 # package installed.  (CGI.pm used to be part of the Perl core, but
@@ -102,24 +88,3 @@ sub _urldecode {
     $v =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
     return $v;
 }
-
-package Tests;
-
-sub test_normalize_windows_paths {
-    my @tests = (
-        {in => "C:\\foo\\bar", want => "C:\\foo\\bar"},
-        {in => "C:/foo/bar", want => "C:\\foo\\bar"},
-        {in => "c:/foo/bar", want => "C:\\foo\\bar"},
-        {in => "/c/foo/bar", want => "C:\\foo\\bar"},
-    );
-    foreach my $tt (@tests) {
-        my $got = ::normalize_windows_path($tt->{in});
-        unless ($got eq $tt->{want}) {
-            die "For path $tt->{in}, normalize = $got; want $tt->{want}\n";
-        }
-    }
-}
-
-BEGIN {
-    test_normalize_windows_paths() if ::on_windows();
-}
index 7cf39cfa5fcee812534f5ca96c898ee9066e6707..91db017245656824884e4c4eb294913026d80094 100644 (file)
@@ -11,11 +11,9 @@ package http
 
 import (
        "bufio"
-       "bytes"
        "errors"
        "fmt"
        "io"
-       "strconv"
 )
 
 const maxLineLength = 4096 // assumed <= bufio.defaultBufSize
@@ -45,12 +43,12 @@ type chunkedReader struct {
 
 func (cr *chunkedReader) beginChunk() {
        // chunk-size CRLF
-       var line string
+       var line []byte
        line, cr.err = readLine(cr.r)
        if cr.err != nil {
                return
        }
-       cr.n, cr.err = strconv.ParseUint(line, 16, 64)
+       cr.n, cr.err = parseHexUint(line)
        if cr.err != nil {
                return
        }
@@ -89,7 +87,7 @@ func (cr *chunkedReader) Read(b []uint8) (n int, err error) {
 // Give up if the line exceeds maxLineLength.
 // The returned bytes are a pointer into storage in
 // the bufio, so they are only valid until the next bufio read.
-func readLineBytes(b *bufio.Reader) (p []byte, err error) {
+func readLine(b *bufio.Reader) (p []byte, err error) {
        if p, err = b.ReadSlice('\n'); err != nil {
                // We always know when EOF is coming.
                // If the caller asked for a line, there should be a line.
@@ -103,20 +101,18 @@ func readLineBytes(b *bufio.Reader) (p []byte, err error) {
        if len(p) >= maxLineLength {
                return nil, ErrLineTooLong
        }
-
-       // Chop off trailing white space.
-       p = bytes.TrimRight(p, " \r\t\n")
-
-       return p, nil
+       return trimTrailingWhitespace(p), nil
 }
 
-// readLineBytes, but convert the bytes into a string.
-func readLine(b *bufio.Reader) (s string, err error) {
-       p, e := readLineBytes(b)
-       if e != nil {
-               return "", e
+func trimTrailingWhitespace(b []byte) []byte {
+       for len(b) > 0 && isASCIISpace(b[len(b)-1]) {
+               b = b[:len(b)-1]
        }
-       return string(p), nil
+       return b
+}
+
+func isASCIISpace(b byte) bool {
+       return b == ' ' || b == '\t' || b == '\n' || b == '\r'
 }
 
 // newChunkedWriter returns a new chunkedWriter that translates writes into HTTP
@@ -167,3 +163,21 @@ func (cw *chunkedWriter) Close() error {
        _, err := io.WriteString(cw.Wire, "0\r\n")
        return err
 }
+
+func parseHexUint(v []byte) (n uint64, err error) {
+       for _, b := range v {
+               n <<= 4
+               switch {
+               case '0' <= b && b <= '9':
+                       b = b - '0'
+               case 'a' <= b && b <= 'f':
+                       b = b - 'a' + 10
+               case 'A' <= b && b <= 'F':
+                       b = b - 'A' + 10
+               default:
+                       return 0, errors.New("invalid byte in chunk length")
+               }
+               n |= uint64(b)
+       }
+       return
+}
index b77ee2ff26ce492e83581ad19252b45462fd906c..0b18c7b55ec23da26dee90affa869da71449dbae 100644 (file)
@@ -9,7 +9,10 @@ package http
 
 import (
        "bytes"
+       "fmt"
+       "io"
        "io/ioutil"
+       "runtime"
        "testing"
 )
 
@@ -37,3 +40,54 @@ func TestChunk(t *testing.T) {
                t.Errorf("chunk reader read %q; want %q", g, e)
        }
 }
+
+func TestChunkReaderAllocs(t *testing.T) {
+       // temporarily set GOMAXPROCS to 1 as we are testing memory allocations
+       defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
+       var buf bytes.Buffer
+       w := newChunkedWriter(&buf)
+       a, b, c := []byte("aaaaaa"), []byte("bbbbbbbbbbbb"), []byte("cccccccccccccccccccccccc")
+       w.Write(a)
+       w.Write(b)
+       w.Write(c)
+       w.Close()
+
+       r := newChunkedReader(&buf)
+       readBuf := make([]byte, len(a)+len(b)+len(c)+1)
+
+       var ms runtime.MemStats
+       runtime.ReadMemStats(&ms)
+       m0 := ms.Mallocs
+
+       n, err := io.ReadFull(r, readBuf)
+
+       runtime.ReadMemStats(&ms)
+       mallocs := ms.Mallocs - m0
+       if mallocs > 1 {
+               t.Errorf("%d mallocs; want <= 1", mallocs)
+       }
+
+       if n != len(readBuf)-1 {
+               t.Errorf("read %d bytes; want %d", n, len(readBuf)-1)
+       }
+       if err != io.ErrUnexpectedEOF {
+               t.Errorf("read error = %v; want ErrUnexpectedEOF", err)
+       }
+}
+
+func TestParseHexUint(t *testing.T) {
+       for i := uint64(0); i <= 1234; i++ {
+               line := []byte(fmt.Sprintf("%x", i))
+               got, err := parseHexUint(line)
+               if err != nil {
+                       t.Fatalf("on %d: %v", i, err)
+               }
+               if got != i {
+                       t.Errorf("for input %q = %d; want %d", line, got, i)
+               }
+       }
+       _, err := parseHexUint([]byte("bogus"))
+       if err == nil {
+               t.Error("expected error on bogus input")
+       }
+}
index 9a45b147ef1894f1f5405395ddab6e23cf241838..f4ba6a9e652ac7d2d611c4c16b8c15f2a811930d 100644 (file)
@@ -527,3 +527,38 @@ func TestClientWithIncorrectTLSServerName(t *testing.T) {
                t.Errorf("wanted error mentioning 127.0.0.1 and badserver; got error: %v", err)
        }
 }
+
+// Verify Response.ContentLength is populated. http://golang.org/issue/4126
+func TestClientHeadContentLength(t *testing.T) {
+       ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+               if v := r.FormValue("cl"); v != "" {
+                       w.Header().Set("Content-Length", v)
+               }
+       }))
+       defer ts.Close()
+       tests := []struct {
+               suffix string
+               want   int64
+       }{
+               {"/?cl=1234", 1234},
+               {"/?cl=0", 0},
+               {"", -1},
+       }
+       for _, tt := range tests {
+               req, _ := NewRequest("HEAD", ts.URL+tt.suffix, nil)
+               res, err := DefaultClient.Do(req)
+               if err != nil {
+                       t.Fatal(err)
+               }
+               if res.ContentLength != tt.want {
+                       t.Errorf("Content-Length = %d; want %d", res.ContentLength, tt.want)
+               }
+               bs, err := ioutil.ReadAll(res.Body)
+               if err != nil {
+                       t.Fatal(err)
+               }
+               if len(bs) != 0 {
+                       t.Errorf("Unexpected content: %q", bs)
+               }
+       }
+}
index 313c6af7a8258c84fced2e274a7ecbc8a84b4607..a7a07852d18b7dac221a694e1be0991292b9f484 100644 (file)
@@ -7,7 +7,14 @@
 
 package http
 
-import "time"
+import (
+       "net"
+       "time"
+)
+
+func NewLoggingConn(baseName string, c net.Conn) net.Conn {
+       return newLoggingConn(baseName, c)
+}
 
 func (t *Transport) IdleConnKeysForTesting() (keys []string) {
        keys = make([]string, 0)
index fd971a61d054d0416d9e3a520e5ab367f6a0b2eb..01bb4dce00002fad0a3426722ff8a4a12dd1d75f 100644 (file)
@@ -188,6 +188,7 @@ type errorfer interface {
 }
 
 func doHeaderWriteSubset(n int, t errorfer) {
+       defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
        h := Header(map[string][]string{
                "Content-Length": {"123"},
                "Content-Type":   {"text/plain"},
@@ -204,7 +205,7 @@ func doHeaderWriteSubset(n int, t errorfer) {
        var m1 runtime.MemStats
        runtime.ReadMemStats(&m1)
        if mallocs := m1.Mallocs - m0.Mallocs; n >= 100 && mallocs >= uint64(n) {
-               // TODO(bradfitz,rsc): once we can sort with allocating,
+               // TODO(bradfitz,rsc): once we can sort without allocating,
                // make this an error.  See http://golang.org/issue/3761
                // t.Errorf("did %d mallocs (>= %d iterations); should have avoided mallocs", mallocs, n)
        }
index 165600e52beba14cb12610f98ba73538d5bb0f02..fc52c9a2efce7b0d7f46399969b92ddf603d0a56 100644 (file)
@@ -21,7 +21,7 @@ import (
 type Server struct {
        URL      string // base URL of form http://ipaddr:port with no trailing slash
        Listener net.Listener
-       TLS      *tls.Config // nil if not using using TLS
+       TLS      *tls.Config // nil if not using TLS
 
        // Config may be changed after calling NewUnstartedServer and
        // before Start or StartTLS.
@@ -36,13 +36,16 @@ type Server struct {
 // accepted.
 type historyListener struct {
        net.Listener
-       history []net.Conn
+       sync.Mutex // protects history
+       history    []net.Conn
 }
 
 func (hs *historyListener) Accept() (c net.Conn, err error) {
        c, err = hs.Listener.Accept()
        if err == nil {
+               hs.Lock()
                hs.history = append(hs.history, c)
+               hs.Unlock()
        }
        return
 }
@@ -96,7 +99,7 @@ func (s *Server) Start() {
        if s.URL != "" {
                panic("Server already started")
        }
-       s.Listener = &historyListener{s.Listener, make([]net.Conn, 0)}
+       s.Listener = &historyListener{Listener: s.Listener}
        s.URL = "http://" + s.Listener.Addr().String()
        s.wrapHandler()
        go s.Config.Serve(s.Listener)
@@ -122,7 +125,7 @@ func (s *Server) StartTLS() {
        }
        tlsListener := tls.NewListener(s.Listener, s.TLS)
 
-       s.Listener = &historyListener{tlsListener, make([]net.Conn, 0)}
+       s.Listener = &historyListener{Listener: tlsListener}
        s.URL = "https://" + s.Listener.Addr().String()
        s.wrapHandler()
        go s.Config.Serve(s.Listener)
@@ -152,6 +155,10 @@ func NewTLSServer(handler http.Handler) *Server {
 func (s *Server) Close() {
        s.Listener.Close()
        s.wg.Wait()
+       s.CloseClientConnections()
+       if t, ok := http.DefaultTransport.(*http.Transport); ok {
+               t.CloseIdleConnections()
+       }
 }
 
 // CloseClientConnections closes any currently open HTTP connections
@@ -161,9 +168,11 @@ func (s *Server) CloseClientConnections() {
        if !ok {
                return
        }
+       hl.Lock()
        for _, conn := range hl.history {
                conn.Close()
        }
+       hl.Unlock()
 }
 
 // waitGroupHandler wraps a handler, incrementing and decrementing a
index 26daee5f2c74f80ebb822c24072fe3917cb24396..b66d4095153f91fe18343317ee92f20ead9ff849 100644 (file)
@@ -13,11 +13,9 @@ package httputil
 
 import (
        "bufio"
-       "bytes"
        "errors"
        "fmt"
        "io"
-       "strconv"
 )
 
 const maxLineLength = 4096 // assumed <= bufio.defaultBufSize
@@ -47,12 +45,12 @@ type chunkedReader struct {
 
 func (cr *chunkedReader) beginChunk() {
        // chunk-size CRLF
-       var line string
+       var line []byte
        line, cr.err = readLine(cr.r)
        if cr.err != nil {
                return
        }
-       cr.n, cr.err = strconv.ParseUint(line, 16, 64)
+       cr.n, cr.err = parseHexUint(line)
        if cr.err != nil {
                return
        }
@@ -91,7 +89,7 @@ func (cr *chunkedReader) Read(b []uint8) (n int, err error) {
 // Give up if the line exceeds maxLineLength.
 // The returned bytes are a pointer into storage in
 // the bufio, so they are only valid until the next bufio read.
-func readLineBytes(b *bufio.Reader) (p []byte, err error) {
+func readLine(b *bufio.Reader) (p []byte, err error) {
        if p, err = b.ReadSlice('\n'); err != nil {
                // We always know when EOF is coming.
                // If the caller asked for a line, there should be a line.
@@ -105,20 +103,18 @@ func readLineBytes(b *bufio.Reader) (p []byte, err error) {
        if len(p) >= maxLineLength {
                return nil, ErrLineTooLong
        }
-
-       // Chop off trailing white space.
-       p = bytes.TrimRight(p, " \r\t\n")
-
-       return p, nil
+       return trimTrailingWhitespace(p), nil
 }
 
-// readLineBytes, but convert the bytes into a string.
-func readLine(b *bufio.Reader) (s string, err error) {
-       p, e := readLineBytes(b)
-       if e != nil {
-               return "", e
+func trimTrailingWhitespace(b []byte) []byte {
+       for len(b) > 0 && isASCIISpace(b[len(b)-1]) {
+               b = b[:len(b)-1]
        }
-       return string(p), nil
+       return b
+}
+
+func isASCIISpace(b byte) bool {
+       return b == ' ' || b == '\t' || b == '\n' || b == '\r'
 }
 
 // NewChunkedWriter returns a new chunkedWriter that translates writes into HTTP
@@ -169,3 +165,21 @@ func (cw *chunkedWriter) Close() error {
        _, err := io.WriteString(cw.Wire, "0\r\n")
        return err
 }
+
+func parseHexUint(v []byte) (n uint64, err error) {
+       for _, b := range v {
+               n <<= 4
+               switch {
+               case '0' <= b && b <= '9':
+                       b = b - '0'
+               case 'a' <= b && b <= 'f':
+                       b = b - 'a' + 10
+               case 'A' <= b && b <= 'F':
+                       b = b - 'A' + 10
+               default:
+                       return 0, errors.New("invalid byte in chunk length")
+               }
+               n |= uint64(b)
+       }
+       return
+}
index 155a32bdf9a2aca3a371e503699cd5f5165ac186..a06bffad5b30f7032cc7bd24a52ed537dbd40f29 100644 (file)
@@ -11,7 +11,10 @@ package httputil
 
 import (
        "bytes"
+       "fmt"
+       "io"
        "io/ioutil"
+       "runtime"
        "testing"
 )
 
@@ -39,3 +42,54 @@ func TestChunk(t *testing.T) {
                t.Errorf("chunk reader read %q; want %q", g, e)
        }
 }
+
+func TestChunkReaderAllocs(t *testing.T) {
+       // temporarily set GOMAXPROCS to 1 as we are testing memory allocations
+       defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
+       var buf bytes.Buffer
+       w := NewChunkedWriter(&buf)
+       a, b, c := []byte("aaaaaa"), []byte("bbbbbbbbbbbb"), []byte("cccccccccccccccccccccccc")
+       w.Write(a)
+       w.Write(b)
+       w.Write(c)
+       w.Close()
+
+       r := NewChunkedReader(&buf)
+       readBuf := make([]byte, len(a)+len(b)+len(c)+1)
+
+       var ms runtime.MemStats
+       runtime.ReadMemStats(&ms)
+       m0 := ms.Mallocs
+
+       n, err := io.ReadFull(r, readBuf)
+
+       runtime.ReadMemStats(&ms)
+       mallocs := ms.Mallocs - m0
+       if mallocs > 1 {
+               t.Errorf("%d mallocs; want <= 1", mallocs)
+       }
+
+       if n != len(readBuf)-1 {
+               t.Errorf("read %d bytes; want %d", n, len(readBuf)-1)
+       }
+       if err != io.ErrUnexpectedEOF {
+               t.Errorf("read error = %v; want ErrUnexpectedEOF", err)
+       }
+}
+
+func TestParseHexUint(t *testing.T) {
+       for i := uint64(0); i <= 1234; i++ {
+               line := []byte(fmt.Sprintf("%x", i))
+               got, err := parseHexUint(line)
+               if err != nil {
+                       t.Fatalf("on %d: %v", i, err)
+               }
+               if got != i {
+                       t.Errorf("for input %q = %d; want %d", line, got, i)
+               }
+       }
+       _, err := parseHexUint([]byte("bogus"))
+       if err == nil {
+               t.Error("expected error on bogus input")
+       }
+}
index 61557ff83020849c15fec40cb74c4cfe8cbfc342..0b6e6cbab58697eaab8441bb27a5d29735ae919f 100644 (file)
@@ -124,6 +124,7 @@ type Request struct {
        // The host on which the URL is sought.
        // Per RFC 2616, this is either the value of the Host: header
        // or the host name given in the URL itself.
+       // It may be of the form "host:port".
        Host string
 
        // Form contains the parsed form data, including both the URL
@@ -643,16 +644,20 @@ func parsePostForm(r *Request) (vs url.Values, err error) {
        return
 }
 
-// ParseForm parses the raw query from the URL.
+// ParseForm parses the raw query from the URL and updates r.Form.
+//
+// For POST or PUT requests, it also parses the request body as a form and
+// put the results into both r.PostForm and r.Form.
+// POST and PUT body parameters take precedence over URL query string values
+// in r.Form.
 //
-// For POST or PUT requests, it also parses the request body as a form.
-// POST and PUT body parameters take precedence over URL query string values.
 // If the request Body's size has not already been limited by MaxBytesReader,
 // the size is capped at 10MB.
 //
 // ParseMultipartForm calls ParseForm automatically.
 // It is idempotent.
-func (r *Request) ParseForm() (err error) {
+func (r *Request) ParseForm() error {
+       var err error
        if r.PostForm == nil {
                if r.Method == "POST" || r.Method == "PUT" {
                        r.PostForm, err = parsePostForm(r)
@@ -728,6 +733,7 @@ func (r *Request) ParseMultipartForm(maxMemory int64) error {
 // FormValue returns the first value for the named component of the query.
 // POST and PUT body parameters take precedence over URL query string values.
 // FormValue calls ParseMultipartForm and ParseForm if necessary.
+// To access multiple values of the same key use ParseForm.
 func (r *Request) FormValue(key string) string {
        if r.Form == nil {
                r.ParseMultipartForm(defaultMaxMemory)
index c0b738c6e6569828f666496b0cff5e2db4baec3b..2f34d124128d47de91cd9f73d2f9fc8782e92c7b 100644 (file)
@@ -228,6 +228,16 @@ func TestReadRequestErrors(t *testing.T) {
        }
 }
 
+func TestNewRequestHost(t *testing.T) {
+       req, err := NewRequest("GET", "http://localhost:1234/", nil)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if req.Host != "localhost:1234" {
+               t.Errorf("Host = %q; want localhost:1234", req.Host)
+       }
+}
+
 func testMissingFile(t *testing.T, req *Request) {
        f, fh, err := req.FormFile("missing")
        if f != nil {
index 92d2f499839946b63a4d728aedbd7ec9d7651f4a..7901c49f5a50db51866afd31b7f264e8bf02c4bd 100644 (file)
@@ -49,7 +49,7 @@ type Response struct {
        Body io.ReadCloser
 
        // ContentLength records the length of the associated content.  The
-       // value -1 indicates that the length is unknown.  Unless RequestMethod
+       // value -1 indicates that the length is unknown.  Unless Request.Method
        // is "HEAD", values >= 0 indicate that the given number of bytes may
        // be read from Body.
        ContentLength int64
@@ -178,7 +178,7 @@ func (r *Response) ProtoAtLeast(major, minor int) bool {
 //  StatusCode
 //  ProtoMajor
 //  ProtoMinor
-//  RequestMethod
+//  Request.Method
 //  TransferEncoding
 //  Trailer
 //  Body
index 6eed4887ddceb8620cc434fd9be18f34413d480e..f31e5d09fe5d9133edc10d7fe299255bc79319e1 100644 (file)
@@ -193,7 +193,7 @@ var respTests = []respTest{
                        Request:       dummyReq("HEAD"),
                        Header:        Header{},
                        Close:         true,
-                       ContentLength: 0,
+                       ContentLength: -1,
                },
 
                "",
index 355efb2cac9f3b5d9beb59eb3f30718b127c1ec2..8ca227f9dec71cc7e233c1218690638c3f945134 100644 (file)
@@ -1252,6 +1252,42 @@ func TestContentLengthZero(t *testing.T) {
        }
 }
 
+func TestCloseNotifier(t *testing.T) {
+       gotReq := make(chan bool, 1)
+       sawClose := make(chan bool, 1)
+       ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, req *Request) {
+               gotReq <- true
+               cc := rw.(CloseNotifier).CloseNotify()
+               <-cc
+               sawClose <- true
+       }))
+       conn, err := net.Dial("tcp", ts.Listener.Addr().String())
+       if err != nil {
+               t.Fatalf("error dialing: %v", err)
+       }
+       diec := make(chan bool)
+       go func() {
+               _, err = fmt.Fprintf(conn, "GET / HTTP/1.1\r\nConnection: keep-alive\r\nHost: foo\r\n\r\n")
+               if err != nil {
+                       t.Fatal(err)
+               }
+               <-diec
+               conn.Close()
+       }()
+For:
+       for {
+               select {
+               case <-gotReq:
+                       diec <- true
+               case <-sawClose:
+                       break For
+               case <-time.After(5 * time.Second):
+                       t.Fatal("timeout")
+               }
+       }
+       ts.Close()
+}
+
 // goTimeout runs f, failing t if f takes more than ns to complete.
 func goTimeout(t *testing.T, d time.Duration, f func()) {
        ch := make(chan bool, 2)
index 719cecfbdaade59addf7d8015dbead7ac66eca39..c4ddbec54f1ce86d806f4dcc64d683e96884c9ac 100644 (file)
@@ -11,7 +11,6 @@ package http
 
 import (
        "bufio"
-       "bytes"
        "crypto/tls"
        "errors"
        "fmt"
@@ -21,7 +20,7 @@ import (
        "net"
        "net/url"
        "path"
-       "runtime/debug"
+       "runtime"
        "strconv"
        "strings"
        "sync"
@@ -94,16 +93,104 @@ type Hijacker interface {
        Hijack() (net.Conn, *bufio.ReadWriter, error)
 }
 
+// The CloseNotifier interface is implemented by ResponseWriters which
+// allow detecting when the underlying connection has gone away.
+//
+// This mechanism can be used to cancel long operations on the server
+// if the client has disconnected before the response is ready.
+type CloseNotifier interface {
+       // CloseNotify returns a channel that receives a single value
+       // when the client connection has gone away.
+       CloseNotify() <-chan bool
+}
+
 // A conn represents the server side of an HTTP connection.
 type conn struct {
        remoteAddr string               // network address of remote side
        server     *Server              // the Server on which the connection arrived
        rwc        net.Conn             // i/o connection
-       lr         *io.LimitedReader    // io.LimitReader(rwc)
-       buf        *bufio.ReadWriter    // buffered(lr,rwc), reading from bufio->limitReader->rwc
-       hijacked   bool                 // connection has been hijacked by handler
+       sr         switchReader         // where the LimitReader reads from; usually the rwc
+       lr         *io.LimitedReader    // io.LimitReader(sr)
+       buf        *bufio.ReadWriter    // buffered(lr,rwc), reading from bufio->limitReader->sr->rwc
        tlsState   *tls.ConnectionState // or nil when not using TLS
        body       []byte
+
+       mu           sync.Mutex // guards the following
+       clientGone   bool       // if client has disconnected mid-request
+       closeNotifyc chan bool  // made lazily
+       hijackedv    bool       // connection has been hijacked by handler
+}
+
+func (c *conn) hijacked() bool {
+       c.mu.Lock()
+       defer c.mu.Unlock()
+       return c.hijackedv
+}
+
+func (c *conn) hijack() (rwc net.Conn, buf *bufio.ReadWriter, err error) {
+       c.mu.Lock()
+       defer c.mu.Unlock()
+       if c.hijackedv {
+               return nil, nil, ErrHijacked
+       }
+       if c.closeNotifyc != nil {
+               return nil, nil, errors.New("http: Hijack is incompatible with use of CloseNotifier")
+       }
+       c.hijackedv = true
+       rwc = c.rwc
+       buf = c.buf
+       c.rwc = nil
+       c.buf = nil
+       return
+}
+
+func (c *conn) closeNotify() <-chan bool {
+       c.mu.Lock()
+       defer c.mu.Unlock()
+       if c.closeNotifyc == nil {
+               c.closeNotifyc = make(chan bool)
+               if c.hijackedv {
+                       // to obey the function signature, even though
+                       // it'll never receive a value.
+                       return c.closeNotifyc
+               }
+               pr, pw := io.Pipe()
+
+               readSource := c.sr.r
+               c.sr.Lock()
+               c.sr.r = pr
+               c.sr.Unlock()
+               go func() {
+                       _, err := io.Copy(pw, readSource)
+                       if err == nil {
+                               err = io.EOF
+                       }
+                       pw.CloseWithError(err)
+                       c.noteClientGone()
+               }()
+       }
+       return c.closeNotifyc
+}
+
+func (c *conn) noteClientGone() {
+       c.mu.Lock()
+       defer c.mu.Unlock()
+       if c.closeNotifyc != nil && !c.clientGone {
+               c.closeNotifyc <- true
+       }
+       c.clientGone = true
+}
+
+type switchReader struct {
+       sync.Mutex
+       r io.Reader
+}
+
+func (sr *switchReader) Read(p []byte) (n int, err error) {
+       sr.Lock()
+       r := sr.r
+       sr.Unlock()
+       return r.Read(p)
 }
 
 // A response represents the server side of an HTTP response.
@@ -127,7 +214,7 @@ type response struct {
 
        // requestBodyLimitHit is set by requestTooLarge when
        // maxBytesReader hits its max size. It is checked in
-       // WriteHeader, to make sure we don't consume the the
+       // WriteHeader, to make sure we don't consume the
        // remaining request body to try to advance to the next HTTP
        // request. Instead, when this is set, we stop reading
        // subsequent requests on this connection and stop reading
@@ -171,16 +258,24 @@ func (w *response) ReadFrom(src io.Reader) (n int64, err error) {
 // noLimit is an effective infinite upper bound for io.LimitedReader
 const noLimit int64 = (1 << 63) - 1
 
+// debugServerConnections controls whether all server connections are wrapped
+// with a verbose logging wrapper.
+const debugServerConnections = false
+
 // Create new connection from rwc.
 func (srv *Server) newConn(rwc net.Conn) (c *conn, err error) {
        c = new(conn)
        c.remoteAddr = rwc.RemoteAddr().String()
        c.server = srv
        c.rwc = rwc
+       if debugServerConnections {
+               c.rwc = newLoggingConn("server", c.rwc)
+       }
+       c.sr = switchReader{r: c.rwc}
        c.body = make([]byte, sniffLen)
-       c.lr = io.LimitReader(rwc, noLimit).(*io.LimitedReader)
+       c.lr = io.LimitReader(&c.sr, noLimit).(*io.LimitedReader)
        br := bufio.NewReader(c.lr)
-       bw := bufio.NewWriter(rwc)
+       bw := bufio.NewWriter(c.rwc)
        c.buf = bufio.NewReadWriter(br, bw)
        return c, nil
 }
@@ -207,9 +302,9 @@ type expectContinueReader struct {
 
 func (ecr *expectContinueReader) Read(p []byte) (n int, err error) {
        if ecr.closed {
-               return 0, errors.New("http: Read after Close on request Body")
+               return 0, ErrBodyReadAfterClose
        }
-       if !ecr.resp.wroteContinue && !ecr.resp.conn.hijacked {
+       if !ecr.resp.wroteContinue && !ecr.resp.conn.hijacked() {
                ecr.resp.wroteContinue = true
                io.WriteString(ecr.resp.conn.buf, "HTTP/1.1 100 Continue\r\n\r\n")
                ecr.resp.conn.buf.Flush()
@@ -232,7 +327,7 @@ var errTooLarge = errors.New("http: request too large")
 
 // Read next request from connection.
 func (c *conn) readRequest() (w *response, err error) {
-       if c.hijacked {
+       if c.hijacked() {
                return nil, ErrHijacked
        }
        c.lr.N = int64(c.server.maxHeaderBytes()) + 4096 /* bufio slop */
@@ -273,7 +368,7 @@ func (w *response) Header() Header {
 const maxPostHandlerReadBytes = 256 << 10
 
 func (w *response) WriteHeader(code int) {
-       if w.conn.hijacked {
+       if w.conn.hijacked() {
                log.Print("http: response.WriteHeader on hijacked connection")
                return
        }
@@ -363,6 +458,8 @@ func (w *response) WriteHeader(code int) {
 
        if w.req.Method == "HEAD" || code == StatusNotModified {
                // do nothing
+       } else if code == StatusNoContent {
+               w.header.Del("Transfer-Encoding")
        } else if hasCL {
                w.contentLength = contentLength
                w.header.Del("Transfer-Encoding")
@@ -447,7 +544,7 @@ func (w *response) bodyAllowed() bool {
 }
 
 func (w *response) Write(data []byte) (n int, err error) {
-       if w.conn.hijacked {
+       if w.conn.hijacked() {
                log.Print("http: response.Write on hijacked connection")
                return 0, ErrHijacked
        }
@@ -496,7 +593,7 @@ func (w *response) Write(data []byte) (n int, err error) {
        // then there would be fewer chunk headers.
        // On the other hand, it would make hijacking more difficult.
        if w.chunking {
-               fmt.Fprintf(w.conn.buf, "%x\r\n", len(data)) // TODO(rsc): use strconv not fmt
+               fmt.Fprintf(w.conn.buf, "%x\r\n", len(data))
        }
        n, err = w.conn.buf.Write(data)
        if err == nil && w.chunking {
@@ -517,7 +614,7 @@ func (w *response) finishRequest() {
        // HTTP/1.0 clients keep their "keep-alive" connections alive, and for
        // HTTP/1.1 clients is just as good as the alternative: sending a
        // chunked response and immediately sending the zero-length EOF chunk.
-       if w.written == 0 && w.header.get("Content-Length") == "" {
+       if w.written == 0 && w.header.get("Content-Length") == "" && w.req.Method != "HEAD" {
                w.header.Set("Content-Length", "0")
        }
        // If this was an HTTP/1.0 request with keep-alive and we sent a
@@ -610,10 +707,10 @@ func (c *conn) serve() {
                        return
                }
 
-               var buf bytes.Buffer
-               fmt.Fprintf(&buf, "http: panic serving %v: %v\n", c.remoteAddr, err)
-               buf.Write(debug.Stack())
-               log.Print(buf.String())
+               const size = 4096
+               buf := make([]byte, size)
+               buf = buf[:runtime.Stack(buf, false)]
+               log.Printf("http: panic serving %v: %v\n%s", c.remoteAddr, err, buf)
 
                if c.rwc != nil { // may be nil if connection hijacked
                        c.rwc.Close()
@@ -665,21 +762,7 @@ func (c *conn) serve() {
                        }
                        req.Header.Del("Expect")
                } else if req.Header.get("Expect") != "" {
-                       // TODO(bradfitz): let ServeHTTP handlers handle
-                       // requests with non-standard expectation[s]? Seems
-                       // theoretical at best, and doesn't fit into the
-                       // current ServeHTTP model anyway.  We'd need to
-                       // make the ResponseWriter an optional
-                       // "ExpectReplier" interface or something.
-                       //
-                       // For now we'll just obey RFC 2616 14.20 which says
-                       // "If a server receives a request containing an
-                       // Expect field that includes an expectation-
-                       // extension that it does not support, it MUST
-                       // respond with a 417 (Expectation Failed) status."
-                       w.Header().Set("Connection", "close")
-                       w.WriteHeader(StatusExpectationFailed)
-                       w.finishRequest()
+                       w.sendExpectationFailed()
                        break
                }
 
@@ -694,7 +777,7 @@ func (c *conn) serve() {
                // [*] Not strictly true: HTTP pipelining.  We could let them all process
                // in parallel even if their responses need to be serialized.
                handler.ServeHTTP(w, w.req)
-               if c.hijacked {
+               if c.hijacked() {
                        return
                }
                w.finishRequest()
@@ -708,18 +791,32 @@ func (c *conn) serve() {
        c.close()
 }
 
+func (w *response) sendExpectationFailed() {
+       // TODO(bradfitz): let ServeHTTP handlers handle
+       // requests with non-standard expectation[s]? Seems
+       // theoretical at best, and doesn't fit into the
+       // current ServeHTTP model anyway.  We'd need to
+       // make the ResponseWriter an optional
+       // "ExpectReplier" interface or something.
+       //
+       // For now we'll just obey RFC 2616 14.20 which says
+       // "If a server receives a request containing an
+       // Expect field that includes an expectation-
+       // extension that it does not support, it MUST
+       // respond with a 417 (Expectation Failed) status."
+       w.Header().Set("Connection", "close")
+       w.WriteHeader(StatusExpectationFailed)
+       w.finishRequest()
+}
+
 // Hijack implements the Hijacker.Hijack method. Our response is both a ResponseWriter
 // and a Hijacker.
 func (w *response) Hijack() (rwc net.Conn, buf *bufio.ReadWriter, err error) {
-       if w.conn.hijacked {
-               return nil, nil, ErrHijacked
-       }
-       w.conn.hijacked = true
-       rwc = w.conn.rwc
-       buf = w.conn.buf
-       w.conn.rwc = nil
-       w.conn.buf = nil
-       return
+       return w.conn.hijack()
+}
+
+func (w *response) CloseNotify() <-chan bool {
+       return w.conn.closeNotify()
 }
 
 // The HandlerFunc type is an adapter to allow the use of
@@ -1310,3 +1407,45 @@ func (tw *timeoutWriter) WriteHeader(code int) {
        tw.mu.Unlock()
        tw.w.WriteHeader(code)
 }
+
+// loggingConn is used for debugging.
+type loggingConn struct {
+       name string
+       net.Conn
+}
+
+var (
+       uniqNameMu   sync.Mutex
+       uniqNameNext = make(map[string]int)
+)
+
+func newLoggingConn(baseName string, c net.Conn) net.Conn {
+       uniqNameMu.Lock()
+       defer uniqNameMu.Unlock()
+       uniqNameNext[baseName]++
+       return &loggingConn{
+               name: fmt.Sprintf("%s-%d", baseName, uniqNameNext[baseName]),
+               Conn: c,
+       }
+}
+
+func (c *loggingConn) Write(p []byte) (n int, err error) {
+       log.Printf("%s.Write(%d) = ....", c.name, len(p))
+       n, err = c.Conn.Write(p)
+       log.Printf("%s.Write(%d) = %d, %v", c.name, len(p), n, err)
+       return
+}
+
+func (c *loggingConn) Read(p []byte) (n int, err error) {
+       log.Printf("%s.Read(%d) = ....", c.name, len(p))
+       n, err = c.Conn.Read(p)
+       log.Printf("%s.Read(%d) = %d, %v", c.name, len(p), n, err)
+       return
+}
+
+func (c *loggingConn) Close() (err error) {
+       log.Printf("%s.Close() = ...", c.name)
+       err = c.Conn.Close()
+       log.Printf("%s.Close() = %v", c.name, err)
+       return
+}
index 9833dddf2b65b4dce25267112955e92c053011ef..70ea15b8e4a346c8e255a102b0fb43347e80c104 100644 (file)
@@ -294,10 +294,19 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err error) {
                return err
        }
 
-       t.ContentLength, err = fixLength(isResponse, t.StatusCode, t.RequestMethod, t.Header, t.TransferEncoding)
+       realLength, err := fixLength(isResponse, t.StatusCode, t.RequestMethod, t.Header, t.TransferEncoding)
        if err != nil {
                return err
        }
+       if isResponse && t.RequestMethod == "HEAD" {
+               if n, err := parseContentLength(t.Header.get("Content-Length")); err != nil {
+                       return err
+               } else {
+                       t.ContentLength = n
+               }
+       } else {
+               t.ContentLength = realLength
+       }
 
        // Trailer
        t.Trailer, err = fixTrailer(t.Header, t.TransferEncoding)
@@ -310,7 +319,7 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err error) {
        // See RFC2616, section 4.4.
        switch msg.(type) {
        case *Response:
-               if t.ContentLength == -1 &&
+               if realLength == -1 &&
                        !chunked(t.TransferEncoding) &&
                        bodyAllowedForStatus(t.StatusCode) {
                        // Unbounded body.
@@ -323,11 +332,11 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err error) {
        switch {
        case chunked(t.TransferEncoding):
                t.Body = &body{Reader: newChunkedReader(r), hdr: msg, r: r, closing: t.Close}
-       case t.ContentLength >= 0:
+       case realLength >= 0:
                // TODO: limit the Content-Length. This is an easy DoS vector.
-               t.Body = &body{Reader: io.LimitReader(r, t.ContentLength), closing: t.Close}
+               t.Body = &body{Reader: io.LimitReader(r, realLength), closing: t.Close}
        default:
-               // t.ContentLength < 0, i.e. "Content-Length" not mentioned in header
+               // realLength < 0, i.e. "Content-Length" not mentioned in header
                if t.Close {
                        // Close semantics (i.e. HTTP/1.0)
                        t.Body = &body{Reader: r, closing: t.Close}
@@ -434,9 +443,9 @@ func fixLength(isResponse bool, status int, requestMethod string, header Header,
        // Logic based on Content-Length
        cl := strings.TrimSpace(header.get("Content-Length"))
        if cl != "" {
-               n, err := strconv.ParseInt(cl, 10, 64)
-               if err != nil || n < 0 {
-                       return -1, &badStringError{"bad Content-Length", cl}
+               n, err := parseContentLength(cl)
+               if err != nil {
+                       return -1, err
                }
                return n, nil
        } else {
@@ -525,11 +534,11 @@ type body struct {
        res *response // response writer for server requests, else nil
 }
 
-// ErrBodyReadAfterClose is returned when reading a Request Body after
-// the body has been closed. This typically happens when the body is
+// ErrBodyReadAfterClose is returned when reading a Request or Response
+// Body after the body has been closed. This typically happens when the body is
 // read after an HTTP Handler calls WriteHeader or Write on its
 // ResponseWriter.
-var ErrBodyReadAfterClose = errors.New("http: invalid Read on closed request Body")
+var ErrBodyReadAfterClose = errors.New("http: invalid Read on closed Body")
 
 func (b *body) Read(p []byte) (n int, err error) {
        if b.closed {
@@ -641,3 +650,18 @@ func (b *body) Close() error {
        }
        return nil
 }
+
+// parseContentLength trims whitespace from s and returns -1 if no value
+// is set, or the value if it's >= 0.
+func parseContentLength(cl string) (int64, error) {
+       cl = strings.TrimSpace(cl)
+       if cl == "" {
+               return -1, nil
+       }
+       n, err := strconv.ParseInt(cl, 10, 64)
+       if err != nil || n < 0 {
+               return 0, &badStringError{"bad Content-Length", cl}
+       }
+       return n, nil
+
+}
index 38ea6f7ba8288f6769aa02a8e829976c6d28c202..7b4afeb8efca88dfc7cf4896bdefb64e7340af60 100644 (file)
@@ -24,15 +24,14 @@ import (
        "os"
        "strings"
        "sync"
-       "sync/atomic"
        "time"
 )
 
 // DefaultTransport is the default implementation of Transport and is
-// used by DefaultClient.  It establishes a new network connection for
-// each call to Do and uses HTTP proxies as directed by the
-// $HTTP_PROXY and $NO_PROXY (or $http_proxy and $no_proxy)
-// environment variables.
+// used by DefaultClient. It establishes network connections as needed
+// and caches them for reuse by subsequent calls. It uses HTTP proxies
+// as directed by the $HTTP_PROXY and $NO_PROXY (or $http_proxy and
+// $no_proxy) environment variables.
 var DefaultTransport RoundTripper = &Transport{Proxy: ProxyFromEnvironment}
 
 // DefaultMaxIdleConnsPerHost is the default value of Transport's
@@ -71,7 +70,7 @@ type Transport struct {
        DisableCompression bool
 
        // MaxIdleConnsPerHost, if non-zero, controls the maximum idle
-       // (keep-alive) to keep to keep per-host.  If zero,
+       // (keep-alive) to keep per-host.  If zero,
        // DefaultMaxIdleConnsPerHost is used.
        MaxIdleConnsPerHost int
 }
@@ -91,7 +90,7 @@ func ProxyFromEnvironment(req *Request) (*url.URL, error) {
                return nil, nil
        }
        proxyURL, err := url.Parse(proxy)
-       if err != nil || proxyURL.Scheme == "" {
+       if err != nil || !strings.HasPrefix(proxyURL.Scheme, "http") {
                if u, err := url.Parse("http://" + proxy); err == nil {
                        proxyURL = u
                        err = nil
@@ -605,22 +604,23 @@ func (pc *persistConn) readLoop() {
                        alive = false
                }
 
-               // TODO(bradfitz): this hasBody conflicts with the defition
-               // above which excludes HEAD requests.  Is this one
-               // incomplete?
-               hasBody := resp != nil && resp.ContentLength != 0
+               hasBody := resp != nil && rc.req.Method != "HEAD" && resp.ContentLength != 0
                var waitForBodyRead chan bool
                if hasBody {
                        lastbody = resp.Body
                        waitForBodyRead = make(chan bool, 1)
-                       resp.Body.(*bodyEOFSignal).fn = func() {
-                               if alive && !pc.t.putIdleConn(pc) {
-                                       alive = false
+                       resp.Body.(*bodyEOFSignal).fn = func(err error) {
+                               alive1 := alive
+                               if err != nil {
+                                       alive1 = false
                                }
-                               if !alive || pc.isBroken() {
+                               if alive1 && !pc.t.putIdleConn(pc) {
+                                       alive1 = false
+                               }
+                               if !alive1 || pc.isBroken() {
                                        pc.close()
                                }
-                               waitForBodyRead <- true
+                               waitForBodyRead <- alive1
                        }
                }
 
@@ -644,7 +644,7 @@ func (pc *persistConn) readLoop() {
                // Wait for the just-returned response body to be fully consumed
                // before we race and peek on the underlying bufio reader.
                if waitForBodyRead != nil {
-                       <-waitForBodyRead
+                       alive = <-waitForBodyRead
                }
 
                if !alive {
@@ -810,50 +810,61 @@ func canonicalAddr(url *url.URL) string {
 }
 
 // bodyEOFSignal wraps a ReadCloser but runs fn (if non-nil) at most
-// once, right before the final Read() or Close() call returns, but after
-// EOF has been seen.
+// once, right before its final (error-producing) Read or Close call
+// returns.
 type bodyEOFSignal struct {
-       body     io.ReadCloser
-       fn       func()
-       isClosed uint32 // atomic bool, non-zero if true
-       once     sync.Once
+       body   io.ReadCloser
+       mu     sync.Mutex  // guards closed, rerr and fn
+       closed bool        // whether Close has been called
+       rerr   error       // sticky Read error
+       fn     func(error) // error will be nil on Read io.EOF
 }
 
 func (es *bodyEOFSignal) Read(p []byte) (n int, err error) {
-       n, err = es.body.Read(p)
-       if es.closed() && n > 0 {
-               panic("http: unexpected bodyEOFSignal Read after Close; see issue 1725")
+       es.mu.Lock()
+       closed, rerr := es.closed, es.rerr
+       es.mu.Unlock()
+       if closed {
+               return 0, errors.New("http: read on closed response body")
        }
-       if err == io.EOF {
-               es.condfn()
+       if rerr != nil {
+               return 0, rerr
        }
-       return
-}
 
-func (es *bodyEOFSignal) Close() (err error) {
-       if !es.setClosed() {
-               // already closed
-               return nil
-       }
-       err = es.body.Close()
-       if err == nil {
-               es.condfn()
+       n, err = es.body.Read(p)
+       if err != nil {
+               es.mu.Lock()
+               defer es.mu.Unlock()
+               if es.rerr == nil {
+                       es.rerr = err
+               }
+               es.condfn(err)
        }
        return
 }
 
-func (es *bodyEOFSignal) condfn() {
-       if es.fn != nil {
-               es.once.Do(es.fn)
+func (es *bodyEOFSignal) Close() error {
+       es.mu.Lock()
+       defer es.mu.Unlock()
+       if es.closed {
+               return nil
        }
+       es.closed = true
+       err := es.body.Close()
+       es.condfn(err)
+       return err
 }
 
-func (es *bodyEOFSignal) closed() bool {
-       return atomic.LoadUint32(&es.isClosed) != 0
-}
-
-func (es *bodyEOFSignal) setClosed() bool {
-       return atomic.CompareAndSwapUint32(&es.isClosed, 0, 1)
+// caller must hold es.mu.
+func (es *bodyEOFSignal) condfn(err error) {
+       if es.fn == nil {
+               return
+       }
+       if err == io.EOF {
+               err = nil
+       }
+       es.fn(err)
+       es.fn = nil
 }
 
 type readFirstCloseBoth struct {
index e4072e88fed3af54e08fa5652d9cd125a44ca617..f1d415888ca870e407a9d519b2aa0fab88933c7a 100644 (file)
@@ -281,7 +281,7 @@ func TestTransportMaxPerHostIdleConns(t *testing.T) {
        c := &Client{Transport: tr}
 
        // Start 3 outstanding requests and wait for the server to get them.
-       // Their responses will hang until we we write to resch, though.
+       // Their responses will hang until we write to resch, though.
        donech := make(chan bool)
        doReq := func() {
                resp, err := c.Get(ts.URL)
@@ -464,7 +464,7 @@ func TestTransportHeadResponses(t *testing.T) {
                if e, g := "123", res.Header.Get("Content-Length"); e != g {
                        t.Errorf("loop %d: expected Content-Length header of %q, got %q", i, e, g)
                }
-               if e, g := int64(0), res.ContentLength; e != g {
+               if e, g := int64(123), res.ContentLength; e != g {
                        t.Errorf("loop %d: expected res.ContentLength of %v, got %v", i, e, g)
                }
        }
@@ -857,6 +857,30 @@ func TestIssue3595(t *testing.T) {
        }
 }
 
+// From http://golang.org/issue/4454 ,
+// "client fails to handle requests with no body and chunked encoding"
+func TestChunkedNoContent(t *testing.T) {
+       ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+               w.WriteHeader(StatusNoContent)
+       }))
+       defer ts.Close()
+
+       for _, closeBody := range []bool{true, false} {
+               c := &Client{Transport: &Transport{}}
+               const n = 4
+               for i := 1; i <= n; i++ {
+                       res, err := c.Get(ts.URL)
+                       if err != nil {
+                               t.Errorf("closingBody=%v, req %d/%d: %v", closeBody, i, n, err)
+                       } else {
+                               if closeBody {
+                                       res.Body.Close()
+                               }
+                       }
+               }
+       }
+}
+
 func TestTransportConcurrency(t *testing.T) {
        const maxProcs = 16
        const numReqs = 500
@@ -901,6 +925,113 @@ func TestTransportConcurrency(t *testing.T) {
        wg.Wait()
 }
 
+func TestIssue4191_InfiniteGetTimeout(t *testing.T) {
+       const debug = false
+       mux := NewServeMux()
+       mux.HandleFunc("/get", func(w ResponseWriter, r *Request) {
+               io.Copy(w, neverEnding('a'))
+       })
+       ts := httptest.NewServer(mux)
+
+       client := &Client{
+               Transport: &Transport{
+                       Dial: func(n, addr string) (net.Conn, error) {
+                               conn, err := net.Dial(n, addr)
+                               if err != nil {
+                                       return nil, err
+                               }
+                               conn.SetDeadline(time.Now().Add(100 * time.Millisecond))
+                               if debug {
+                                       conn = NewLoggingConn("client", conn)
+                               }
+                               return conn, nil
+                       },
+                       DisableKeepAlives: true,
+               },
+       }
+
+       nRuns := 5
+       if testing.Short() {
+               nRuns = 1
+       }
+       for i := 0; i < nRuns; i++ {
+               if debug {
+                       println("run", i+1, "of", nRuns)
+               }
+               sres, err := client.Get(ts.URL + "/get")
+               if err != nil {
+                       t.Errorf("Error issuing GET: %v", err)
+                       break
+               }
+               _, err = io.Copy(ioutil.Discard, sres.Body)
+               if err == nil {
+                       t.Errorf("Unexpected successful copy")
+                       break
+               }
+       }
+       if debug {
+               println("tests complete; waiting for handlers to finish")
+       }
+       ts.Close()
+}
+
+func TestIssue4191_InfiniteGetToPutTimeout(t *testing.T) {
+       const debug = false
+       mux := NewServeMux()
+       mux.HandleFunc("/get", func(w ResponseWriter, r *Request) {
+               io.Copy(w, neverEnding('a'))
+       })
+       mux.HandleFunc("/put", func(w ResponseWriter, r *Request) {
+               defer r.Body.Close()
+               io.Copy(ioutil.Discard, r.Body)
+       })
+       ts := httptest.NewServer(mux)
+
+       client := &Client{
+               Transport: &Transport{
+                       Dial: func(n, addr string) (net.Conn, error) {
+                               conn, err := net.Dial(n, addr)
+                               if err != nil {
+                                       return nil, err
+                               }
+                               conn.SetDeadline(time.Now().Add(100 * time.Millisecond))
+                               if debug {
+                                       conn = NewLoggingConn("client", conn)
+                               }
+                               return conn, nil
+                       },
+                       DisableKeepAlives: true,
+               },
+       }
+
+       nRuns := 5
+       if testing.Short() {
+               nRuns = 1
+       }
+       for i := 0; i < nRuns; i++ {
+               if debug {
+                       println("run", i+1, "of", nRuns)
+               }
+               sres, err := client.Get(ts.URL + "/get")
+               if err != nil {
+                       t.Errorf("Error issuing GET: %v", err)
+                       break
+               }
+               req, _ := NewRequest("PUT", ts.URL+"/put", sres.Body)
+               _, err = client.Do(req)
+               if err == nil {
+                       sres.Body.Close()
+                       t.Errorf("Unexpected successful PUT")
+                       break
+               }
+               sres.Body.Close()
+       }
+       if debug {
+               println("tests complete; waiting for handlers to finish")
+       }
+       ts.Close()
+}
+
 type fooProto struct{}
 
 func (fooProto) RoundTrip(req *Request) (*Response, error) {
@@ -937,6 +1068,9 @@ var proxyFromEnvTests = []struct {
        wanterr error
 }{
        {"127.0.0.1:8080", "http://127.0.0.1:8080", nil},
+       {"cache.corp.example.com:1234", "http://cache.corp.example.com:1234", nil},
+       {"cache.corp.example.com", "http://cache.corp.example.com", nil},
+       {"https://cache.corp.example.com", "https://cache.corp.example.com", nil},
        {"http://127.0.0.1:8080", "http://127.0.0.1:8080", nil},
        {"https://127.0.0.1:8080", "https://127.0.0.1:8080", nil},
        {"", "<nil>", nil},
index 979d7acd53d949e7184c6bcf1cbe23f363bcd7b9..0aac3d187a1921ea7087ee54df4af63c8c4243db 100644 (file)
@@ -36,6 +36,7 @@ type IPMask []byte
 type IPNet struct {
        IP   IP     // network number
        Mask IPMask // network mask
+       Zone string // IPv6 scoped addressing zone
 }
 
 // IPv4 returns the IP address (in 16-byte form) of the
@@ -645,5 +646,5 @@ func ParseCIDR(s string) (IP, *IPNet, error) {
                return nil, nil, &ParseError{"CIDR address", s}
        }
        m := CIDRMask(n, 8*iplen)
-       return ip, &IPNet{ip.Mask(m), m}, nil
+       return ip, &IPNet{IP: ip.Mask(m), Mask: m}, nil
 }
index df647ef73c0f8fda053474f95454209a1358c646..8324d2a327c1b22c835f4b73953edd142f199a8e 100644 (file)
@@ -114,23 +114,23 @@ var parsecidrtests = []struct {
        net *IPNet
        err error
 }{
-       {"135.104.0.0/32", IPv4(135, 104, 0, 0), &IPNet{IPv4(135, 104, 0, 0), IPv4Mask(255, 255, 255, 255)}, nil},
-       {"0.0.0.0/24", IPv4(0, 0, 0, 0), &IPNet{IPv4(0, 0, 0, 0), IPv4Mask(255, 255, 255, 0)}, nil},
-       {"135.104.0.0/24", IPv4(135, 104, 0, 0), &IPNet{IPv4(135, 104, 0, 0), IPv4Mask(255, 255, 255, 0)}, nil},
-       {"135.104.0.1/32", IPv4(135, 104, 0, 1), &IPNet{IPv4(135, 104, 0, 1), IPv4Mask(255, 255, 255, 255)}, nil},
-       {"135.104.0.1/24", IPv4(135, 104, 0, 1), &IPNet{IPv4(135, 104, 0, 0), IPv4Mask(255, 255, 255, 0)}, nil},
-       {"::1/128", ParseIP("::1"), &IPNet{ParseIP("::1"), IPMask(ParseIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"))}, nil},
-       {"abcd:2345::/127", ParseIP("abcd:2345::"), &IPNet{ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe"))}, nil},
-       {"abcd:2345::/65", ParseIP("abcd:2345::"), &IPNet{ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:ffff:ffff:8000::"))}, nil},
-       {"abcd:2345::/64", ParseIP("abcd:2345::"), &IPNet{ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:ffff:ffff::"))}, nil},
-       {"abcd:2345::/63", ParseIP("abcd:2345::"), &IPNet{ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:ffff:fffe::"))}, nil},
-       {"abcd:2345::/33", ParseIP("abcd:2345::"), &IPNet{ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff:8000::"))}, nil},
-       {"abcd:2345::/32", ParseIP("abcd:2345::"), &IPNet{ParseIP("abcd:2345::"), IPMask(ParseIP("ffff:ffff::"))}, nil},
-       {"abcd:2344::/31", ParseIP("abcd:2344::"), &IPNet{ParseIP("abcd:2344::"), IPMask(ParseIP("ffff:fffe::"))}, nil},
-       {"abcd:2300::/24", ParseIP("abcd:2300::"), &IPNet{ParseIP("abcd:2300::"), IPMask(ParseIP("ffff:ff00::"))}, nil},
-       {"abcd:2345::/24", ParseIP("abcd:2345::"), &IPNet{ParseIP("abcd:2300::"), IPMask(ParseIP("ffff:ff00::"))}, nil},
-       {"2001:DB8::/48", ParseIP("2001:DB8::"), &IPNet{ParseIP("2001:DB8::"), IPMask(ParseIP("ffff:ffff:ffff::"))}, nil},
-       {"2001:DB8::1/48", ParseIP("2001:DB8::1"), &IPNet{ParseIP("2001:DB8::"), IPMask(ParseIP("ffff:ffff:ffff::"))}, nil},
+       {"135.104.0.0/32", IPv4(135, 104, 0, 0), &IPNet{IP: IPv4(135, 104, 0, 0), Mask: IPv4Mask(255, 255, 255, 255)}, nil},
+       {"0.0.0.0/24", IPv4(0, 0, 0, 0), &IPNet{IP: IPv4(0, 0, 0, 0), Mask: IPv4Mask(255, 255, 255, 0)}, nil},
+       {"135.104.0.0/24", IPv4(135, 104, 0, 0), &IPNet{IP: IPv4(135, 104, 0, 0), Mask: IPv4Mask(255, 255, 255, 0)}, nil},
+       {"135.104.0.1/32", IPv4(135, 104, 0, 1), &IPNet{IP: IPv4(135, 104, 0, 1), Mask: IPv4Mask(255, 255, 255, 255)}, nil},
+       {"135.104.0.1/24", IPv4(135, 104, 0, 1), &IPNet{IP: IPv4(135, 104, 0, 0), Mask: IPv4Mask(255, 255, 255, 0)}, nil},
+       {"::1/128", ParseIP("::1"), &IPNet{IP: ParseIP("::1"), Mask: IPMask(ParseIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"))}, nil},
+       {"abcd:2345::/127", ParseIP("abcd:2345::"), &IPNet{IP: ParseIP("abcd:2345::"), Mask: IPMask(ParseIP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe"))}, nil},
+       {"abcd:2345::/65", ParseIP("abcd:2345::"), &IPNet{IP: ParseIP("abcd:2345::"), Mask: IPMask(ParseIP("ffff:ffff:ffff:ffff:8000::"))}, nil},
+       {"abcd:2345::/64", ParseIP("abcd:2345::"), &IPNet{IP: ParseIP("abcd:2345::"), Mask: IPMask(ParseIP("ffff:ffff:ffff:ffff::"))}, nil},
+       {"abcd:2345::/63", ParseIP("abcd:2345::"), &IPNet{IP: ParseIP("abcd:2345::"), Mask: IPMask(ParseIP("ffff:ffff:ffff:fffe::"))}, nil},
+       {"abcd:2345::/33", ParseIP("abcd:2345::"), &IPNet{IP: ParseIP("abcd:2345::"), Mask: IPMask(ParseIP("ffff:ffff:8000::"))}, nil},
+       {"abcd:2345::/32", ParseIP("abcd:2345::"), &IPNet{IP: ParseIP("abcd:2345::"), Mask: IPMask(ParseIP("ffff:ffff::"))}, nil},
+       {"abcd:2344::/31", ParseIP("abcd:2344::"), &IPNet{IP: ParseIP("abcd:2344::"), Mask: IPMask(ParseIP("ffff:fffe::"))}, nil},
+       {"abcd:2300::/24", ParseIP("abcd:2300::"), &IPNet{IP: ParseIP("abcd:2300::"), Mask: IPMask(ParseIP("ffff:ff00::"))}, nil},
+       {"abcd:2345::/24", ParseIP("abcd:2345::"), &IPNet{IP: ParseIP("abcd:2300::"), Mask: IPMask(ParseIP("ffff:ff00::"))}, nil},
+       {"2001:DB8::/48", ParseIP("2001:DB8::"), &IPNet{IP: ParseIP("2001:DB8::"), Mask: IPMask(ParseIP("ffff:ffff:ffff::"))}, nil},
+       {"2001:DB8::1/48", ParseIP("2001:DB8::1"), &IPNet{IP: ParseIP("2001:DB8::"), Mask: IPMask(ParseIP("ffff:ffff:ffff::"))}, nil},
        {"192.168.1.1/255.255.255.0", nil, nil, &ParseError{"CIDR address", "192.168.1.1/255.255.255.0"}},
        {"192.168.1.1/35", nil, nil, &ParseError{"CIDR address", "192.168.1.1/35"}},
        {"2001:db8::1/-1", nil, nil, &ParseError{"CIDR address", "2001:db8::1/-1"}},
@@ -154,14 +154,14 @@ var ipnetcontainstests = []struct {
        net *IPNet
        ok  bool
 }{
-       {IPv4(172, 16, 1, 1), &IPNet{IPv4(172, 16, 0, 0), CIDRMask(12, 32)}, true},
-       {IPv4(172, 24, 0, 1), &IPNet{IPv4(172, 16, 0, 0), CIDRMask(13, 32)}, false},
-       {IPv4(192, 168, 0, 3), &IPNet{IPv4(192, 168, 0, 0), IPv4Mask(0, 0, 255, 252)}, true},
-       {IPv4(192, 168, 0, 4), &IPNet{IPv4(192, 168, 0, 0), IPv4Mask(0, 255, 0, 252)}, false},
-       {ParseIP("2001:db8:1:2::1"), &IPNet{ParseIP("2001:db8:1::"), CIDRMask(47, 128)}, true},
-       {ParseIP("2001:db8:1:2::1"), &IPNet{ParseIP("2001:db8:2::"), CIDRMask(47, 128)}, false},
-       {ParseIP("2001:db8:1:2::1"), &IPNet{ParseIP("2001:db8:1::"), IPMask(ParseIP("ffff:0:ffff::"))}, true},
-       {ParseIP("2001:db8:1:2::1"), &IPNet{ParseIP("2001:db8:1::"), IPMask(ParseIP("0:0:0:ffff::"))}, false},
+       {IPv4(172, 16, 1, 1), &IPNet{IP: IPv4(172, 16, 0, 0), Mask: CIDRMask(12, 32)}, true},
+       {IPv4(172, 24, 0, 1), &IPNet{IP: IPv4(172, 16, 0, 0), Mask: CIDRMask(13, 32)}, false},
+       {IPv4(192, 168, 0, 3), &IPNet{IP: IPv4(192, 168, 0, 0), Mask: IPv4Mask(0, 0, 255, 252)}, true},
+       {IPv4(192, 168, 0, 4), &IPNet{IP: IPv4(192, 168, 0, 0), Mask: IPv4Mask(0, 255, 0, 252)}, false},
+       {ParseIP("2001:db8:1:2::1"), &IPNet{IP: ParseIP("2001:db8:1::"), Mask: CIDRMask(47, 128)}, true},
+       {ParseIP("2001:db8:1:2::1"), &IPNet{IP: ParseIP("2001:db8:2::"), Mask: CIDRMask(47, 128)}, false},
+       {ParseIP("2001:db8:1:2::1"), &IPNet{IP: ParseIP("2001:db8:1::"), Mask: IPMask(ParseIP("ffff:0:ffff::"))}, true},
+       {ParseIP("2001:db8:1:2::1"), &IPNet{IP: ParseIP("2001:db8:1::"), Mask: IPMask(ParseIP("0:0:0:ffff::"))}, false},
 }
 
 func TestIPNetContains(t *testing.T) {
@@ -176,10 +176,10 @@ var ipnetstringtests = []struct {
        in  *IPNet
        out string
 }{
-       {&IPNet{IPv4(192, 168, 1, 0), CIDRMask(26, 32)}, "192.168.1.0/26"},
-       {&IPNet{IPv4(192, 168, 1, 0), IPv4Mask(255, 0, 255, 0)}, "192.168.1.0/ff00ff00"},
-       {&IPNet{ParseIP("2001:db8::"), CIDRMask(55, 128)}, "2001:db8::/55"},
-       {&IPNet{ParseIP("2001:db8::"), IPMask(ParseIP("8000:f123:0:cafe::"))}, "2001:db8::/8000f1230000cafe0000000000000000"},
+       {&IPNet{IP: IPv4(192, 168, 1, 0), Mask: CIDRMask(26, 32)}, "192.168.1.0/26"},
+       {&IPNet{IP: IPv4(192, 168, 1, 0), Mask: IPv4Mask(255, 0, 255, 0)}, "192.168.1.0/ff00ff00"},
+       {&IPNet{IP: ParseIP("2001:db8::"), Mask: CIDRMask(55, 128)}, "2001:db8::/55"},
+       {&IPNet{IP: ParseIP("2001:db8::"), Mask: IPMask(ParseIP("8000:f123:0:cafe::"))}, "2001:db8::/8000f1230000cafe0000000000000000"},
 }
 
 func TestIPNetString(t *testing.T) {
@@ -233,27 +233,27 @@ var networknumberandmasktests = []struct {
        in  IPNet
        out IPNet
 }{
-       {IPNet{v4addr, v4mask}, IPNet{v4addr, v4mask}},
-       {IPNet{v4addr, v4mappedv6mask}, IPNet{v4addr, v4mask}},
-       {IPNet{v4mappedv6addr, v4mappedv6mask}, IPNet{v4addr, v4mask}},
-       {IPNet{v4mappedv6addr, v6mask}, IPNet{v4addr, v4maskzero}},
-       {IPNet{v4addr, v6mask}, IPNet{v4addr, v4maskzero}},
-       {IPNet{v6addr, v6mask}, IPNet{v6addr, v6mask}},
-       {IPNet{v6addr, v4mappedv6mask}, IPNet{v6addr, v4mappedv6mask}},
-       {in: IPNet{v6addr, v4mask}},
-       {in: IPNet{v4addr, badmask}},
-       {in: IPNet{v4mappedv6addr, badmask}},
-       {in: IPNet{v6addr, badmask}},
-       {in: IPNet{badaddr, v4mask}},
-       {in: IPNet{badaddr, v4mappedv6mask}},
-       {in: IPNet{badaddr, v6mask}},
-       {in: IPNet{badaddr, badmask}},
+       {IPNet{IP: v4addr, Mask: v4mask}, IPNet{IP: v4addr, Mask: v4mask}},
+       {IPNet{IP: v4addr, Mask: v4mappedv6mask}, IPNet{IP: v4addr, Mask: v4mask}},
+       {IPNet{IP: v4mappedv6addr, Mask: v4mappedv6mask}, IPNet{IP: v4addr, Mask: v4mask}},
+       {IPNet{IP: v4mappedv6addr, Mask: v6mask}, IPNet{IP: v4addr, Mask: v4maskzero}},
+       {IPNet{IP: v4addr, Mask: v6mask}, IPNet{IP: v4addr, Mask: v4maskzero}},
+       {IPNet{IP: v6addr, Mask: v6mask}, IPNet{IP: v6addr, Mask: v6mask}},
+       {IPNet{IP: v6addr, Mask: v4mappedv6mask}, IPNet{IP: v6addr, Mask: v4mappedv6mask}},
+       {in: IPNet{IP: v6addr, Mask: v4mask}},
+       {in: IPNet{IP: v4addr, Mask: badmask}},
+       {in: IPNet{IP: v4mappedv6addr, Mask: badmask}},
+       {in: IPNet{IP: v6addr, Mask: badmask}},
+       {in: IPNet{IP: badaddr, Mask: v4mask}},
+       {in: IPNet{IP: badaddr, Mask: v4mappedv6mask}},
+       {in: IPNet{IP: badaddr, Mask: v6mask}},
+       {in: IPNet{IP: badaddr, Mask: badmask}},
 }
 
 func TestNetworkNumberAndMask(t *testing.T) {
        for _, tt := range networknumberandmasktests {
                ip, m := networkNumberAndMask(&tt.in)
-               out := &IPNet{ip, m}
+               out := &IPNet{IP: ip, Mask: m}
                if !reflect.DeepEqual(&tt.out, out) {
                        t.Errorf("networkNumberAndMask(%v) = %v; want %v", tt.in, out, &tt.out)
                }
@@ -268,6 +268,7 @@ var splitjointests = []struct {
        {"www.google.com", "80", "www.google.com:80"},
        {"127.0.0.1", "1234", "127.0.0.1:1234"},
        {"::1", "80", "[::1]:80"},
+       {"google.com", "https%foo", "google.com:https%foo"}, // Go 1.0 behavior
 }
 
 func TestSplitHostPort(t *testing.T) {
index 43b02aef2ea1f040a9292b769857222ce4d4e955..f21889fcbeaac96d9e4a7fcad2873254fd8c1a50 100644 (file)
@@ -9,11 +9,46 @@ package net
 import (
        "bytes"
        "os"
+       "reflect"
        "syscall"
        "testing"
        "time"
 )
 
+var resolveIPAddrTests = []struct {
+       net     string
+       litAddr string
+       addr    *IPAddr
+       err     error
+}{
+       {"ip", "127.0.0.1", &IPAddr{IP: IPv4(127, 0, 0, 1)}, nil},
+       {"ip4", "127.0.0.1", &IPAddr{IP: IPv4(127, 0, 0, 1)}, nil},
+       {"ip4:icmp", "127.0.0.1", &IPAddr{IP: IPv4(127, 0, 0, 1)}, nil},
+
+       {"ip", "::1", &IPAddr{IP: ParseIP("::1")}, nil},
+       {"ip6", "::1", &IPAddr{IP: ParseIP("::1")}, nil},
+       {"ip6:icmp", "::1", &IPAddr{IP: ParseIP("::1")}, nil},
+
+       {"", "127.0.0.1", &IPAddr{IP: IPv4(127, 0, 0, 1)}, nil}, // Go 1.0 behavior
+       {"", "::1", &IPAddr{IP: ParseIP("::1")}, nil},           // Go 1.0 behavior
+
+       {"l2tp", "127.0.0.1", nil, UnknownNetworkError("l2tp")},
+       {"l2tp:gre", "127.0.0.1", nil, UnknownNetworkError("l2tp:gre")},
+       {"tcp", "1.2.3.4:123", nil, UnknownNetworkError("tcp")},
+}
+
+func TestResolveIPAddr(t *testing.T) {
+       for _, tt := range resolveIPAddrTests {
+               addr, err := ResolveIPAddr(tt.net, tt.litAddr)
+               if err != tt.err {
+                       t.Fatalf("ResolveIPAddr(%v, %v) failed: %v", tt.net, tt.litAddr, err)
+               }
+               if !reflect.DeepEqual(addr, tt.addr) {
+                       t.Fatalf("got %#v; expected %#v", addr, tt.addr)
+               }
+       }
+}
+
 var icmpTests = []struct {
        net   string
        laddr string
index d7bffc69e95d37b3944a45302778fc2b41a5fd9c..13bfd62404a49a896d7dab9158b90d4a6e88c232 100644 (file)
@@ -2,17 +2,14 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// (Raw) IP sockets
+// Raw IP sockets
 
 package net
 
-import (
-       "time"
-)
-
 // IPAddr represents the address of an IP end point.
 type IPAddr struct {
-       IP IP
+       IP   IP
+       Zone string // IPv6 scoped addressing zone
 }
 
 // Network returns the address's network name, "ip".
@@ -27,47 +24,23 @@ func (a *IPAddr) String() string {
 
 // ResolveIPAddr parses addr as an IP address and resolves domain
 // names to numeric addresses on the network net, which must be
-// "ip", "ip4" or "ip6".  A literal IPv6 host address must be
-// enclosed in square brackets, as in "[::]".
+// "ip", "ip4" or "ip6".
 func ResolveIPAddr(net, addr string) (*IPAddr, error) {
-       return resolveIPAddr(net, addr, noDeadline)
-}
-
-func resolveIPAddr(net, addr string, deadline time.Time) (*IPAddr, error) {
-       ip, err := hostToIP(net, addr, deadline)
+       if net == "" { // a hint wildcard for Go 1.0 undocumented behavior
+               net = "ip"
+       }
+       afnet, _, err := parseDialNetwork(net)
        if err != nil {
                return nil, err
        }
-       return &IPAddr{ip}, nil
-}
-
-// Convert "host" into IP address.
-func hostToIP(net, host string, deadline time.Time) (ip IP, err error) {
-       var addr IP
-       // Try as an IP address.
-       addr = ParseIP(host)
-       if addr == nil {
-               filter := anyaddr
-               if net != "" && net[len(net)-1] == '4' {
-                       filter = ipv4only
-               }
-               if net != "" && net[len(net)-1] == '6' {
-                       filter = ipv6only
-               }
-               // Not an IP address.  Try as a DNS name.
-               addrs, err1 := lookupHostDeadline(host, deadline)
-               if err1 != nil {
-                       err = err1
-                       goto Error
-               }
-               addr = firstFavoriteAddr(filter, addrs)
-               if addr == nil {
-                       // should not happen
-                       err = &AddrError{"LookupHost returned no suitable address", addrs[0]}
-                       goto Error
-               }
+       switch afnet {
+       case "ip", "ip4", "ip6":
+       default:
+               return nil, UnknownNetworkError(net)
+       }
+       a, err := resolveInternetAddr(afnet, addr, noDeadline)
+       if err != nil {
+               return nil, err
        }
-       return addr, nil
-Error:
-       return nil, err
+       return a.(*IPAddr), nil
 }
index e77c5476afad6a506c7fb65ed0733a9599f9c096..88e3b2c60b5df4c825fd62079b7aadec8ffad45a 100644 (file)
@@ -2,83 +2,21 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// (Raw) IP sockets stubs for Plan 9
+// Raw IP sockets for Plan 9
 
 package net
 
 import (
-       "os"
        "syscall"
        "time"
 )
 
 // IPConn is the implementation of the Conn and PacketConn interfaces
 // for IP network connections.
-type IPConn bool
-
-// 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, syscall.EPLAN9
-}
-
-// Write implements the Conn Write method.
-func (c *IPConn) Write(b []byte) (int, error) {
-       return 0, syscall.EPLAN9
-}
-
-// LocalAddr returns the local network address.
-func (c *IPConn) LocalAddr() Addr {
-       return nil
-}
-
-// RemoteAddr returns the remote network address.
-func (c *IPConn) RemoteAddr() Addr {
-       return nil
-}
-
-// SetDeadline implements the Conn SetDeadline method.
-func (c *IPConn) SetDeadline(t time.Time) error {
-       return syscall.EPLAN9
-}
-
-// SetReadDeadline implements the Conn SetReadDeadline method.
-func (c *IPConn) SetReadDeadline(t time.Time) error {
-       return syscall.EPLAN9
-}
-
-// SetWriteDeadline implements the Conn SetWriteDeadline method.
-func (c *IPConn) SetWriteDeadline(t time.Time) error {
-       return syscall.EPLAN9
-}
-
-// SetReadBuffer sets the size of the operating system's receive
-// buffer associated with the connection.
-func (c *IPConn) SetReadBuffer(bytes int) error {
-       return syscall.EPLAN9
-}
-
-// SetWriteBuffer sets the size of the operating system's transmit
-// buffer associated with the connection.
-func (c *IPConn) SetWriteBuffer(bytes int) error {
-       return syscall.EPLAN9
+type IPConn struct {
+       conn
 }
 
-// File returns a copy of the underlying os.File, set to blocking
-// mode.  It is the caller's responsibility to close f when finished.
-// Closing c does not affect f, and closing f does not affect c.
-func (c *IPConn) File() (f *os.File, err error) {
-       return nil, syscall.EPLAN9
-}
-
-// Close closes the IP connection.
-func (c *IPConn) Close() error {
-       return syscall.EPLAN9
-}
-
-// IP-specific methods.
-
 // ReadFromIP reads an IP packet from c, copying the payload into b.
 // It returns the number of bytes copied into b and the return address
 // that was on the packet.
index 00e87cfbf0dda38c65d9d314f56f924a0c5eaf58..7a8cd4470d613cfba743a3db534e1bdab095990d 100644 (file)
@@ -4,7 +4,7 @@
 
 // +build darwin freebsd linux netbsd openbsd windows
 
-// (Raw) IP sockets
+// Raw IP sockets for POSIX
 
 package net
 
@@ -16,9 +16,9 @@ import (
 func sockaddrToIP(sa syscall.Sockaddr) Addr {
        switch sa := sa.(type) {
        case *syscall.SockaddrInet4:
-               return &IPAddr{sa.Addr[0:]}
+               return &IPAddr{IP: sa.Addr[0:]}
        case *syscall.SockaddrInet6:
-               return &IPAddr{sa.Addr[0:]}
+               return &IPAddr{IP: sa.Addr[0:], Zone: zoneToString(int(sa.ZoneId))}
        }
        return nil
 }
@@ -41,7 +41,7 @@ func (a *IPAddr) isWildcard() bool {
 }
 
 func (a *IPAddr) sockaddr(family int) (syscall.Sockaddr, error) {
-       return ipToSockaddr(family, a.IP, 0)
+       return ipToSockaddr(family, a.IP, 0, a.Zone)
 }
 
 func (a *IPAddr) toAddr() sockaddr {
@@ -59,8 +59,6 @@ type IPConn struct {
 
 func newIPConn(fd *netFD) *IPConn { return &IPConn{conn{fd}} }
 
-// IP-specific methods.
-
 // ReadFromIP reads an IP packet from c, copying the payload into b.
 // It returns the number of bytes copied into b and the return address
 // that was on the packet.
@@ -78,14 +76,14 @@ func (c *IPConn) ReadFromIP(b []byte) (int, *IPAddr, error) {
        n, sa, err := c.fd.ReadFrom(b)
        switch sa := sa.(type) {
        case *syscall.SockaddrInet4:
-               addr = &IPAddr{sa.Addr[0:]}
+               addr = &IPAddr{IP: sa.Addr[0:]}
                if len(b) >= IPv4len { // discard ipv4 header
                        hsize := (int(b[0]) & 0xf) * 4
                        copy(b, b[hsize:])
                        n -= hsize
                }
        case *syscall.SockaddrInet6:
-               addr = &IPAddr{sa.Addr[0:]}
+               addr = &IPAddr{IP: sa.Addr[0:], Zone: zoneToString(int(sa.ZoneId))}
        }
        return n, addr, err
 }
@@ -95,8 +93,8 @@ func (c *IPConn) ReadFrom(b []byte) (int, Addr, error) {
        if !c.ok() {
                return 0, nil, syscall.EINVAL
        }
-       n, uaddr, err := c.ReadFromIP(b)
-       return n, uaddr.toAddr(), err
+       n, addr, err := c.ReadFromIP(b)
+       return n, addr.toAddr(), err
 }
 
 // ReadMsgIP reads a packet from c, copying the payload into b and the
@@ -111,9 +109,9 @@ func (c *IPConn) ReadMsgIP(b, oob []byte) (n, oobn, flags int, addr *IPAddr, err
        n, oobn, flags, sa, err = c.fd.ReadMsg(b, oob)
        switch sa := sa.(type) {
        case *syscall.SockaddrInet4:
-               addr = &IPAddr{sa.Addr[0:]}
+               addr = &IPAddr{IP: sa.Addr[0:]}
        case *syscall.SockaddrInet6:
-               addr = &IPAddr{sa.Addr[0:]}
+               addr = &IPAddr{IP: sa.Addr[0:], Zone: zoneToString(int(sa.ZoneId))}
        }
        return
 }
index 9d48e8c1036347d94e7a1d34d796f9d44efbcf47..5636c85b4ffb91ede85f514e76bee76ecb057a47 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.
 
-// IP sockets
+// Internet protocol family sockets
 
 package net
 
@@ -72,15 +72,18 @@ func (e InvalidAddrError) Temporary() bool { return false }
 // "host:port" or "[host]:port" into host and port.
 // The latter form must be used when host contains a colon.
 func SplitHostPort(hostport string) (host, port string, err error) {
+       host, port, _, err = splitHostPort(hostport)
+       return
+}
+
+func splitHostPort(hostport string) (host, port, zone string, err error) {
        // The port starts after the last colon.
        i := last(hostport, ':')
        if i < 0 {
                err = &AddrError{"missing port in address", hostport}
                return
        }
-
-       host, port = hostport[0:i], hostport[i+1:]
-
+       host, port = hostport[:i], hostport[i+1:]
        // Can put brackets around host ...
        if len(host) > 0 && host[0] == '[' && host[len(host)-1] == ']' {
                host = host[1 : len(host)-1]
@@ -104,42 +107,84 @@ func JoinHostPort(host, port string) string {
        return host + ":" + port
 }
 
-// Convert "host:port" into IP address and port.
-func hostPortToIP(net, hostport string, deadline time.Time) (ip IP, iport int, err error) {
-       host, port, err := SplitHostPort(hostport)
-       if err != nil {
-               return nil, 0, err
-       }
-
-       var addr IP
-       if host != "" {
-               // Try as an IP address.
-               addr = ParseIP(host)
-               if addr == nil {
-                       var filter func(IP) IP
-                       if net != "" && net[len(net)-1] == '4' {
-                               filter = ipv4only
-                       }
-                       if net != "" && net[len(net)-1] == '6' {
-                               filter = ipv6only
-                       }
-                       // Not an IP address.  Try as a DNS name.
-                       addrs, err := lookupHostDeadline(host, deadline)
-                       if err != nil {
-                               return nil, 0, err
+func resolveInternetAddr(net, addr string, deadline time.Time) (Addr, error) {
+       var (
+               err              error
+               host, port, zone string
+               portnum          int
+       )
+       switch net {
+       case "tcp", "tcp4", "tcp6", "udp", "udp4", "udp6":
+               if addr != "" {
+                       if host, port, zone, err = splitHostPort(addr); err != nil {
+                               return nil, err
                        }
-                       addr = firstFavoriteAddr(filter, addrs)
-                       if addr == nil {
-                               // should not happen
-                               return nil, 0, &AddrError{"LookupHost returned no suitable address", addrs[0]}
+                       if portnum, err = parsePort(net, port); err != nil {
+                               return nil, err
                        }
                }
+       case "ip", "ip4", "ip6":
+               if addr != "" {
+                       host = addr
+               }
+       default:
+               return nil, UnknownNetworkError(net)
        }
-
-       p, err := parsePort(net, port)
+       inetaddr := func(net string, ip IP, port int, zone string) Addr {
+               switch net {
+               case "tcp", "tcp4", "tcp6":
+                       return &TCPAddr{IP: ip, Port: port, Zone: zone}
+               case "udp", "udp4", "udp6":
+                       return &UDPAddr{IP: ip, Port: port, Zone: zone}
+               case "ip", "ip4", "ip6":
+                       return &IPAddr{IP: ip, Zone: zone}
+               }
+               return nil
+       }
+       if host == "" {
+               return inetaddr(net, nil, portnum, zone), nil
+       }
+       // Try as an IP address.
+       if ip := ParseIP(host); ip != nil {
+               return inetaddr(net, ip, portnum, zone), nil
+       }
+       var filter func(IP) IP
+       if net != "" && net[len(net)-1] == '4' {
+               filter = ipv4only
+       }
+       if net != "" && net[len(net)-1] == '6' {
+               filter = ipv6only
+       }
+       // Try as a DNS name.
+       addrs, err := lookupHostDeadline(host, deadline)
        if err != nil {
-               return nil, 0, err
+               return nil, err
+       }
+       ip := firstFavoriteAddr(filter, addrs)
+       if ip == nil {
+               // should not happen
+               return nil, &AddrError{"LookupHost returned no suitable address", addrs[0]}
        }
+       return inetaddr(net, ip, portnum, zone), nil
+}
 
-       return addr, p, nil
+func zoneToString(zone int) string {
+       if zone == 0 {
+               return ""
+       }
+       if ifi, err := InterfaceByIndex(zone); err == nil {
+               return ifi.Name
+       }
+       return itod(uint(zone))
+}
+
+func zoneToInt(zone string) int {
+       if zone == "" {
+               return 0
+       }
+       if ifi, err := InterfaceByName(zone); err == nil {
+               return ifi.Index
+       }
+       n, _, _ := dtoi(zone, 0)
+       return n
 }
index 138c3b4855b7f4a6d864a5f20160302a6ee6b26e..eaef768fd01e30dafd7c207fd2686e23a285ff02 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.
 
-// IP sockets stubs for Plan 9
+// Internet protocol family sockets for Plan 9
 
 package net
 
@@ -59,9 +59,9 @@ func readPlan9Addr(proto, filename string) (addr Addr, err error) {
        }
        switch proto {
        case "tcp":
-               addr = &TCPAddr{ip, port}
+               addr = &TCPAddr{IP: ip, Port: port}
        case "udp":
-               addr = &UDPAddr{ip, port}
+               addr = &UDPAddr{IP: ip, Port: port}
        default:
                return nil, errors.New("unknown protocol " + proto)
        }
index 87a2288973b9c1a9cefc67d275a5f1c075b33a07..4c37616ecf861d8f0fd11b008a8c4556d11df570 100644 (file)
@@ -4,6 +4,8 @@
 
 // +build darwin freebsd linux netbsd openbsd windows
 
+// Internet protocol family sockets for POSIX
+
 package net
 
 import (
@@ -155,7 +157,7 @@ Error:
        return nil, &OpError{mode, net, addr, err}
 }
 
-func ipToSockaddr(family int, ip IP, port int) (syscall.Sockaddr, error) {
+func ipToSockaddr(family int, ip IP, port int, zone string) (syscall.Sockaddr, error) {
        switch family {
        case syscall.AF_INET:
                if len(ip) == 0 {
@@ -164,12 +166,12 @@ func ipToSockaddr(family int, ip IP, port int) (syscall.Sockaddr, error) {
                if ip = ip.To4(); ip == nil {
                        return nil, InvalidAddrError("non-IPv4 address")
                }
-               s := new(syscall.SockaddrInet4)
+               sa := new(syscall.SockaddrInet4)
                for i := 0; i < IPv4len; i++ {
-                       s.Addr[i] = ip[i]
+                       sa.Addr[i] = ip[i]
                }
-               s.Port = port
-               return s, nil
+               sa.Port = port
+               return sa, nil
        case syscall.AF_INET6:
                if len(ip) == 0 {
                        ip = IPv6zero
@@ -183,12 +185,13 @@ func ipToSockaddr(family int, ip IP, port int) (syscall.Sockaddr, error) {
                if ip = ip.To16(); ip == nil {
                        return nil, InvalidAddrError("non-IPv6 address")
                }
-               s := new(syscall.SockaddrInet6)
+               sa := new(syscall.SockaddrInet6)
                for i := 0; i < IPv6len; i++ {
-                       s.Addr[i] = ip[i]
+                       sa.Addr[i] = ip[i]
                }
-               s.Port = port
-               return s, nil
+               sa.Port = port
+               sa.ZoneId = uint32(zoneToInt(zone))
+               return sa, nil
        }
        return nil, InvalidAddrError("unexpected socket family")
 }
index d4a8a35627dd937977dacd1d8f9f96f55cffa6d8..bcc13ee8511b013ab28d2b7dd341eddc61cd4be4 100644 (file)
@@ -21,26 +21,26 @@ var multicastListenerTests = []struct {
 }{
        // 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},
-       {"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},
+       {"udp", &UDPAddr{IP: IPv4(224, 0, 0, 254), Port: 12345}, FlagUp | FlagLoopback, false},
+       {"udp", &UDPAddr{IP: IPv4(224, 0, 0, 254), Port: 12345}, 0, false},
+       {"udp", &UDPAddr{IP: ParseIP("ff0e::114"), Port: 12345}, FlagUp | FlagLoopback, true},
+       {"udp", &UDPAddr{IP: ParseIP("ff0e::114"), Port: 12345}, 0, true},
 
-       {"udp4", &UDPAddr{IPv4(224, 0, 0, 254), 12345}, FlagUp | FlagLoopback, false},
-       {"udp4", &UDPAddr{IPv4(224, 0, 0, 254), 12345}, 0, false},
+       {"udp4", &UDPAddr{IP: IPv4(224, 0, 0, 254), Port: 12345}, FlagUp | FlagLoopback, false},
+       {"udp4", &UDPAddr{IP: IPv4(224, 0, 0, 254), Port: 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},
+       {"udp6", &UDPAddr{IP: ParseIP("ff01::114"), Port: 12345}, FlagUp | FlagLoopback, true},
+       {"udp6", &UDPAddr{IP: ParseIP("ff01::114"), Port: 12345}, 0, true},
+       {"udp6", &UDPAddr{IP: ParseIP("ff02::114"), Port: 12345}, FlagUp | FlagLoopback, true},
+       {"udp6", &UDPAddr{IP: ParseIP("ff02::114"), Port: 12345}, 0, true},
+       {"udp6", &UDPAddr{IP: ParseIP("ff04::114"), Port: 12345}, FlagUp | FlagLoopback, true},
+       {"udp6", &UDPAddr{IP: ParseIP("ff04::114"), Port: 12345}, 0, true},
+       {"udp6", &UDPAddr{IP: ParseIP("ff05::114"), Port: 12345}, FlagUp | FlagLoopback, true},
+       {"udp6", &UDPAddr{IP: ParseIP("ff05::114"), Port: 12345}, 0, true},
+       {"udp6", &UDPAddr{IP: ParseIP("ff08::114"), Port: 12345}, FlagUp | FlagLoopback, true},
+       {"udp6", &UDPAddr{IP: ParseIP("ff08::114"), Port: 12345}, 0, true},
+       {"udp6", &UDPAddr{IP: ParseIP("ff0e::114"), Port: 12345}, FlagUp | FlagLoopback, true},
+       {"udp6", &UDPAddr{IP: ParseIP("ff0e::114"), Port: 12345}, 0, true},
 }
 
 // TestMulticastListener tests both single and double listen to a test
index feb92a2737d5623fc57c3997d044d1b79bd78eb8..a3d17598205387e622e97b4c2b349705374c10e5 100644 (file)
@@ -44,7 +44,9 @@ package net
 
 import (
        "errors"
+       "io"
        "os"
+       "sync"
        "syscall"
        "time"
 )
@@ -195,9 +197,13 @@ func (c *conn) SetWriteBuffer(bytes int) error {
        return setWriteBuffer(c.fd, bytes)
 }
 
-// File returns a copy of the underlying os.File, set to blocking mode.
+// File sets the underlying os.File to blocking mode and returns a copy.
 // It is the caller's responsibility to close f when finished.
 // Closing c does not affect f, and closing f does not affect c.
+//
+// The returned os.File's file descriptor is different from the connection's.
+// Attempting to change properties of the original using this duplicate
+// may or may not have the desired effect.
 func (c *conn) File() (f *os.File, err error) { return c.fd.dup() }
 
 // An Error represents a network error.
@@ -363,3 +369,47 @@ func (e *DNSConfigError) Error() string {
 
 func (e *DNSConfigError) Timeout() bool   { return false }
 func (e *DNSConfigError) Temporary() bool { return false }
+
+type writerOnly struct {
+       io.Writer
+}
+
+// Fallback implementation of io.ReaderFrom's ReadFrom, when sendfile isn't
+// applicable.
+func genericReadFrom(w io.Writer, r io.Reader) (n int64, err error) {
+       // Use wrapper to hide existing r.ReadFrom from io.Copy.
+       return io.Copy(writerOnly{w}, r)
+}
+
+// deadline is an atomically-accessed number of nanoseconds since 1970
+// or 0, if no deadline is set.
+type deadline struct {
+       sync.Mutex
+       val int64
+}
+
+func (d *deadline) expired() bool {
+       t := d.value()
+       return t > 0 && time.Now().UnixNano() >= t
+}
+
+func (d *deadline) value() (v int64) {
+       d.Lock()
+       v = d.val
+       d.Unlock()
+       return
+}
+
+func (d *deadline) set(v int64) {
+       d.Lock()
+       d.val = v
+       d.Unlock()
+}
+
+func (d *deadline) setTime(t time.Time) {
+       if t.IsZero() {
+               d.set(0)
+       } else {
+               d.set(t.UnixNano())
+       }
+}
index 8898b98abad488a9b3a713e549a87b232f8cd1f7..e71b6fb1a43414d871b2d53834cbf0d7bc01276b 100644 (file)
 
                // Asynchronous call
                quotient := new(Quotient)
-               divCall := client.Go("Arith.Divide", args, &quotient, nil)
+               divCall := client.Go("Arith.Divide", args, quotient, nil)
                replyCall := <-divCall.Done     // will be equal to divCall
                // check errors, print, etc.
 
@@ -219,8 +219,8 @@ func isExportedOrBuiltinType(t reflect.Type) bool {
 //     - exported method
 //     - two arguments, both pointers to exported structs
 //     - one return value, of type error
-// It returns an error if the receiver is not an exported type or has no
-// suitable methods.
+// It returns an error if the receiver is not an exported type or has
+// no methods or unsuitable methods. It also logs the error using package log.
 // The client accesses each method using a string of the form "Type.Method",
 // where Type is the receiver's concrete type.
 func (server *Server) Register(rcvr interface{}) error {
index d9ebe71e5c310a6cecff9a6435681ae9de18d4a1..2c734a479fd63365976cfb4687011635095c33b3 100644 (file)
@@ -446,6 +446,7 @@ func dialHTTP() (*Client, error) {
 }
 
 func countMallocs(dial func() (*Client, error), t *testing.T) uint64 {
+       defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
        once.Do(startServer)
        client, err := dial()
        if err != nil {
index 8500006104015b836d34a1a4ea4fd24642046a6a..8008bc3b56047d0ef3433bd7a150e4742dda9e3a 100644 (file)
@@ -82,7 +82,7 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) {
                if n == 0 && err1 == nil {
                        break
                }
-               if err1 == syscall.EAGAIN && c.wdeadline >= 0 {
+               if err1 == syscall.EAGAIN {
                        if err1 = c.pollServer.WaitWrite(c); err1 == nil {
                                continue
                        }
index 5ee18f9cccddb9cee78c5eaa4acd802b26a9271a..3357e653869a59b4860c101c5b0dd32772912d0a 100644 (file)
@@ -58,7 +58,7 @@ func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) {
                if n == 0 && err1 == nil {
                        break
                }
-               if err1 == syscall.EAGAIN && c.wdeadline >= 0 {
+               if err1 == syscall.EAGAIN {
                        if err1 = c.pollServer.WaitWrite(c); err1 == nil {
                                continue
                        }
index 78417fd2ee7f95547ec09a05b55102167fc6410b..12015ef0acdf4861d3b7a9da1f40b6bc5005d556 100644 (file)
@@ -9,7 +9,6 @@
 package net
 
 import (
-       "io"
        "syscall"
        "time"
 )
@@ -57,16 +56,13 @@ func socket(net string, f, t, p int, ipv6only bool, ulsa, ursa syscall.Sockaddr,
        }
 
        if ursa != nil {
-               if !deadline.IsZero() {
-                       fd.wdeadline = deadline.UnixNano()
-               }
+               fd.wdeadline.setTime(deadline)
                if err = fd.connect(ursa); err != nil {
                        closesocket(s)
-                       fd.Close()
                        return nil, err
                }
                fd.isConnected = true
-               fd.wdeadline = 0
+               fd.wdeadline.set(0)
        }
 
        lsa, _ := syscall.Getsockname(s)
@@ -76,14 +72,3 @@ func socket(net string, f, t, p int, ipv6only bool, ulsa, ursa syscall.Sockaddr,
        fd.setAddr(laddr, raddr)
        return fd, nil
 }
-
-type writerOnly struct {
-       io.Writer
-}
-
-// Fallback implementation of io.ReaderFrom's ReadFrom, when sendfile isn't
-// applicable.
-func genericReadFrom(w io.Writer, r io.Reader) (n int64, err error) {
-       // Use wrapper to hide existing r.ReadFrom from io.Copy.
-       return io.Copy(writerOnly{w}, r)
-}
index b139c427654f65d47e2f7c08adb6c30c6515db9f..fe371fe0cefc4f300514603b36f5672c8636fff1 100644 (file)
@@ -119,29 +119,22 @@ func setWriteBuffer(fd *netFD, bytes int) error {
        return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_SNDBUF, bytes))
 }
 
+// TODO(dfc) these unused error returns could be removed
+
 func setReadDeadline(fd *netFD, t time.Time) error {
-       if t.IsZero() {
-               fd.rdeadline = 0
-       } else {
-               fd.rdeadline = t.UnixNano()
-       }
+       fd.rdeadline.setTime(t)
        return nil
 }
 
 func setWriteDeadline(fd *netFD, t time.Time) error {
-       if t.IsZero() {
-               fd.wdeadline = 0
-       } else {
-               fd.wdeadline = t.UnixNano()
-       }
+       fd.wdeadline.setTime(t)
        return nil
 }
 
 func setDeadline(fd *netFD, t time.Time) error {
-       if err := setReadDeadline(fd, t); err != nil {
-               return err
-       }
-       return setWriteDeadline(fd, t)
+       setReadDeadline(fd, t)
+       setWriteDeadline(fd, t)
+       return nil
 }
 
 func setKeepAlive(fd *netFD, keepalive bool) error {
index f6e4df30a8b322f50d254b0c46ad95d91e7a78ac..bca748827ce1226409289cb7bf3a5be59f3399a4 100644 (file)
@@ -5,6 +5,7 @@
 package net
 
 import (
+       "reflect"
        "runtime"
        "testing"
        "time"
@@ -117,6 +118,36 @@ func benchmarkTCP(b *testing.B, persistent, timeout bool) {
        }
 }
 
+var resolveTCPAddrTests = []struct {
+       net     string
+       litAddr string
+       addr    *TCPAddr
+       err     error
+}{
+       {"tcp", "127.0.0.1:0", &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 0}, nil},
+       {"tcp4", "127.0.0.1:65535", &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 65535}, nil},
+
+       {"tcp", "[::1]:1", &TCPAddr{IP: ParseIP("::1"), Port: 1}, nil},
+       {"tcp6", "[::1]:65534", &TCPAddr{IP: ParseIP("::1"), Port: 65534}, nil},
+
+       {"", "127.0.0.1:0", &TCPAddr{IP: IPv4(127, 0, 0, 1), Port: 0}, nil}, // Go 1.0 behavior
+       {"", "[::1]:0", &TCPAddr{IP: ParseIP("::1"), Port: 0}, nil},         // Go 1.0 behavior
+
+       {"http", "127.0.0.1:0", nil, UnknownNetworkError("http")},
+}
+
+func TestResolveTCPAddr(t *testing.T) {
+       for _, tt := range resolveTCPAddrTests {
+               addr, err := ResolveTCPAddr(tt.net, tt.litAddr)
+               if err != tt.err {
+                       t.Fatalf("ResolveTCPAddr(%v, %v) failed: %v", tt.net, tt.litAddr, err)
+               }
+               if !reflect.DeepEqual(addr, tt.addr) {
+                       t.Fatalf("got %#v; expected %#v", addr, tt.addr)
+               }
+       }
+}
+
 var tcpListenerNameTests = []struct {
        net   string
        laddr *TCPAddr
index 6aba1f89fc82451e93a5d13abca13afcd23879ae..d5158b22def5c00e5e199882b1013c213bbf6141 100644 (file)
@@ -6,12 +6,11 @@
 
 package net
 
-import "time"
-
 // TCPAddr represents the address of a TCP end point.
 type TCPAddr struct {
        IP   IP
        Port int
+       Zone string // IPv6 scoped addressing zone
 }
 
 // Network returns the address's network name, "tcp".
@@ -30,13 +29,16 @@ func (a *TCPAddr) String() string {
 // "tcp4" or "tcp6".  A literal IPv6 host address must be
 // enclosed in square brackets, as in "[::]:80".
 func ResolveTCPAddr(net, addr string) (*TCPAddr, error) {
-       return resolveTCPAddr(net, addr, noDeadline)
-}
-
-func resolveTCPAddr(net, addr string, deadline time.Time) (*TCPAddr, error) {
-       ip, port, err := hostPortToIP(net, addr, deadline)
+       switch net {
+       case "tcp", "tcp4", "tcp6":
+       case "": // a hint wildcard for Go 1.0 undocumented behavior
+               net = "tcp"
+       default:
+               return nil, UnknownNetworkError(net)
+       }
+       a, err := resolveInternetAddr(net, addr, noDeadline)
        if err != nil {
                return nil, err
        }
-       return &TCPAddr{ip, port}, nil
+       return a.(*TCPAddr), nil
 }
index cec5bd2aa5ea3e0a3574f4bd67fa23e8acac8dfc..954c99a2d8191347f0adee34d07dd5e8822cdbbf 100644 (file)
@@ -25,7 +25,7 @@ func newTCPConn(fd *netFD) *TCPConn {
 
 // ReadFrom implements the io.ReaderFrom ReadFrom method.
 func (c *TCPConn) ReadFrom(r io.Reader) (int64, error) {
-       return 0, syscall.EPLAN9
+       return genericReadFrom(c, r)
 }
 
 // CloseRead shuts down the reading side of the TCP connection.
@@ -78,7 +78,7 @@ func (c *TCPConn) SetNoDelay(noDelay bool) error {
 // DialTCP connects to the remote address raddr on the network net,
 // which must be "tcp", "tcp4", or "tcp6".  If laddr is not nil, it is
 // used as the local address for the connection.
-func DialTCP(net string, laddr, raddr *TCPAddr) (c *TCPConn, err error) {
+func DialTCP(net string, laddr, raddr *TCPAddr) (*TCPConn, error) {
        return dialTCP(net, laddr, raddr, noDeadline)
 }
 
index e5b3a09f75c257eba7f46f6e7346d3562c375c68..4f9159566f3a4f83785680f01ff8a1a0f16c4e6f 100644 (file)
@@ -23,9 +23,9 @@ import (
 func sockaddrToTCP(sa syscall.Sockaddr) Addr {
        switch sa := sa.(type) {
        case *syscall.SockaddrInet4:
-               return &TCPAddr{sa.Addr[0:], sa.Port}
+               return &TCPAddr{IP: sa.Addr[0:], Port: sa.Port}
        case *syscall.SockaddrInet6:
-               return &TCPAddr{sa.Addr[0:], sa.Port}
+               return &TCPAddr{IP: sa.Addr[0:], Port: sa.Port, Zone: zoneToString(int(sa.ZoneId))}
        default:
                if sa != nil {
                        // Diagnose when we will turn a non-nil sockaddr into a nil.
@@ -53,7 +53,7 @@ func (a *TCPAddr) isWildcard() bool {
 }
 
 func (a *TCPAddr) sockaddr(family int) (syscall.Sockaddr, error) {
-       return ipToSockaddr(family, a.IP, a.Port)
+       return ipToSockaddr(family, a.IP, a.Port, a.Zone)
 }
 
 func (a *TCPAddr) toAddr() sockaddr {
@@ -299,7 +299,5 @@ func ListenTCP(net string, laddr *TCPAddr) (*TCPListener, error) {
                closesocket(fd.sysfd)
                return nil, &OpError{"listen", net, laddr, err}
        }
-       l := new(TCPListener)
-       l.fd = fd
-       return l, nil
+       return &TCPListener{fd}, nil
 }
index 68d8ced011a2b66dd131352072a1ad288a6f606e..21223cc74ad6fa477183d1321ecf47bc90113625 100644 (file)
@@ -6,11 +6,169 @@ package net
 
 import (
        "fmt"
+       "io"
+       "io/ioutil"
        "runtime"
        "testing"
        "time"
 )
 
+func isTimeout(err error) bool {
+       e, ok := err.(Error)
+       return ok && e.Timeout()
+}
+
+type copyRes struct {
+       n   int64
+       err error
+       d   time.Duration
+}
+
+func TestAcceptTimeout(t *testing.T) {
+       switch runtime.GOOS {
+       case "plan9":
+               t.Logf("skipping test on %q", runtime.GOOS)
+               return
+       }
+
+       ln := newLocalListener(t).(*TCPListener)
+       defer ln.Close()
+       ln.SetDeadline(time.Now().Add(-1 * time.Second))
+       if _, err := ln.Accept(); !isTimeout(err) {
+               t.Fatalf("Accept: expected err %v, got %v", errTimeout, err)
+       }
+       if _, err := ln.Accept(); !isTimeout(err) {
+               t.Fatalf("Accept: expected err %v, got %v", errTimeout, err)
+       }
+       ln.SetDeadline(time.Now().Add(100 * time.Millisecond))
+       if _, err := ln.Accept(); !isTimeout(err) {
+               t.Fatalf("Accept: expected err %v, got %v", errTimeout, err)
+       }
+       if _, err := ln.Accept(); !isTimeout(err) {
+               t.Fatalf("Accept: expected err %v, got %v", errTimeout, err)
+       }
+       ln.SetDeadline(noDeadline)
+       errc := make(chan error)
+       go func() {
+               _, err := ln.Accept()
+               errc <- err
+       }()
+       time.Sleep(100 * time.Millisecond)
+       select {
+       case err := <-errc:
+               t.Fatalf("Expected Accept() to not return, but it returned with %v\n", err)
+       default:
+       }
+       ln.Close()
+       if err := <-errc; err.(*OpError).Err != errClosing {
+               t.Fatalf("Accept: expected err %v, got %v", errClosing, err.(*OpError).Err)
+       }
+}
+
+func TestReadTimeout(t *testing.T) {
+       switch runtime.GOOS {
+       case "plan9":
+               t.Logf("skipping test on %q", runtime.GOOS)
+               return
+       }
+
+       ln := newLocalListener(t)
+       defer ln.Close()
+       c, err := DialTCP("tcp", nil, ln.Addr().(*TCPAddr))
+       if err != nil {
+               t.Fatalf("Connect: %v", err)
+       }
+       defer c.Close()
+       c.SetDeadline(time.Now().Add(time.Hour))
+       c.SetReadDeadline(time.Now().Add(-1 * time.Second))
+       buf := make([]byte, 1)
+       if _, err = c.Read(buf); !isTimeout(err) {
+               t.Fatalf("Read: expected err %v, got %v", errTimeout, err)
+       }
+       if _, err = c.Read(buf); !isTimeout(err) {
+               t.Fatalf("Read: expected err %v, got %v", errTimeout, err)
+       }
+       c.SetDeadline(time.Now().Add(100 * time.Millisecond))
+       if _, err = c.Read(buf); !isTimeout(err) {
+               t.Fatalf("Read: expected err %v, got %v", errTimeout, err)
+       }
+       if _, err = c.Read(buf); !isTimeout(err) {
+               t.Fatalf("Read: expected err %v, got %v", errTimeout, err)
+       }
+       c.SetReadDeadline(noDeadline)
+       c.SetWriteDeadline(time.Now().Add(-1 * time.Second))
+       errc := make(chan error)
+       go func() {
+               _, err := c.Read(buf)
+               errc <- err
+       }()
+       time.Sleep(100 * time.Millisecond)
+       select {
+       case err := <-errc:
+               t.Fatalf("Expected Read() to not return, but it returned with %v\n", err)
+       default:
+       }
+       c.Close()
+       if err := <-errc; err.(*OpError).Err != errClosing {
+               t.Fatalf("Read: expected err %v, got %v", errClosing, err.(*OpError).Err)
+       }
+}
+
+func TestWriteTimeout(t *testing.T) {
+       switch runtime.GOOS {
+       case "plan9":
+               t.Logf("skipping test on %q", runtime.GOOS)
+               return
+       }
+
+       ln := newLocalListener(t)
+       defer ln.Close()
+       c, err := DialTCP("tcp", nil, ln.Addr().(*TCPAddr))
+       if err != nil {
+               t.Fatalf("Connect: %v", err)
+       }
+       defer c.Close()
+       c.SetDeadline(time.Now().Add(time.Hour))
+       c.SetWriteDeadline(time.Now().Add(-1 * time.Second))
+       buf := make([]byte, 4096)
+       writeUntilTimeout := func() {
+               for {
+                       _, err := c.Write(buf)
+                       if err != nil {
+                               if isTimeout(err) {
+                                       return
+                               }
+                               t.Fatalf("Write: expected err %v, got %v", errTimeout, err)
+                       }
+               }
+       }
+       writeUntilTimeout()
+       c.SetDeadline(time.Now().Add(10 * time.Millisecond))
+       writeUntilTimeout()
+       writeUntilTimeout()
+       c.SetWriteDeadline(noDeadline)
+       c.SetReadDeadline(time.Now().Add(-1 * time.Second))
+       errc := make(chan error)
+       go func() {
+               for {
+                       _, err := c.Write(buf)
+                       if err != nil {
+                               errc <- err
+                       }
+               }
+       }()
+       time.Sleep(100 * time.Millisecond)
+       select {
+       case err := <-errc:
+               t.Fatalf("Expected Write() to not return, but it returned with %v\n", err)
+       default:
+       }
+       c.Close()
+       if err := <-errc; err.(*OpError).Err != errClosing {
+               t.Fatalf("Write: expected err %v, got %v", errClosing, err.(*OpError).Err)
+       }
+}
+
 func testTimeout(t *testing.T, net, addr string, readFrom bool) {
        c, err := Dial(net, addr)
        if err != nil {
@@ -104,7 +262,7 @@ func TestDeadlineReset(t *testing.T) {
        defer ln.Close()
        tl := ln.(*TCPListener)
        tl.SetDeadline(time.Now().Add(1 * time.Minute))
-       tl.SetDeadline(time.Time{}) // reset it
+       tl.SetDeadline(noDeadline) // reset it
        errc := make(chan error, 1)
        go func() {
                _, err := ln.Accept()
@@ -174,6 +332,7 @@ func TestReadWriteDeadline(t *testing.T) {
        if err != nil {
                t.Fatalf("ListenTCP on :0: %v", err)
        }
+       defer ln.Close()
 
        lnquit := make(chan bool)
 
@@ -230,3 +389,233 @@ func TestReadWriteDeadline(t *testing.T) {
        <-quit
        <-lnquit
 }
+
+type neverEnding byte
+
+func (b neverEnding) Read(p []byte) (n int, err error) {
+       for i := range p {
+               p[i] = byte(b)
+       }
+       return len(p), nil
+}
+
+func TestVariousDeadlines1Proc(t *testing.T) {
+       testVariousDeadlines(t, 1)
+}
+
+func TestVariousDeadlines4Proc(t *testing.T) {
+       testVariousDeadlines(t, 4)
+}
+
+func testVariousDeadlines(t *testing.T, maxProcs int) {
+       defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(maxProcs))
+       ln := newLocalListener(t)
+       defer ln.Close()
+       acceptc := make(chan error, 1)
+
+       // The server, with no timeouts of its own, sending bytes to clients
+       // as fast as it can.
+       servec := make(chan copyRes)
+       go func() {
+               for {
+                       c, err := ln.Accept()
+                       if err != nil {
+                               acceptc <- err
+                               return
+                       }
+                       go func() {
+                               t0 := time.Now()
+                               n, err := io.Copy(c, neverEnding('a'))
+                               d := time.Since(t0)
+                               c.Close()
+                               servec <- copyRes{n, err, d}
+                       }()
+               }
+       }()
+
+       for _, timeout := range []time.Duration{
+               1 * time.Nanosecond,
+               2 * time.Nanosecond,
+               5 * time.Nanosecond,
+               50 * time.Nanosecond,
+               100 * time.Nanosecond,
+               200 * time.Nanosecond,
+               500 * time.Nanosecond,
+               750 * time.Nanosecond,
+               1 * time.Microsecond,
+               5 * time.Microsecond,
+               25 * time.Microsecond,
+               250 * time.Microsecond,
+               500 * time.Microsecond,
+               1 * time.Millisecond,
+               5 * time.Millisecond,
+               100 * time.Millisecond,
+               250 * time.Millisecond,
+               500 * time.Millisecond,
+               1 * time.Second,
+       } {
+               numRuns := 3
+               if testing.Short() {
+                       numRuns = 1
+                       if timeout > 500*time.Microsecond {
+                               continue
+                       }
+               }
+               for run := 0; run < numRuns; run++ {
+                       name := fmt.Sprintf("%v run %d/%d", timeout, run+1, numRuns)
+                       t.Log(name)
+
+                       c, err := Dial("tcp", ln.Addr().String())
+                       if err != nil {
+                               t.Fatalf("Dial: %v", err)
+                       }
+                       clientc := make(chan copyRes)
+                       go func() {
+                               t0 := time.Now()
+                               c.SetDeadline(t0.Add(timeout))
+                               n, err := io.Copy(ioutil.Discard, c)
+                               d := time.Since(t0)
+                               c.Close()
+                               clientc <- copyRes{n, err, d}
+                       }()
+
+                       const tooLong = 2000 * time.Millisecond
+                       select {
+                       case res := <-clientc:
+                               if isTimeout(res.err) {
+                                       t.Logf("for %v, good client timeout after %v, reading %d bytes", name, res.d, res.n)
+                               } else {
+                                       t.Fatalf("for %v: client Copy = %d, %v (want timeout)", name, res.n, res.err)
+                               }
+                       case <-time.After(tooLong):
+                               t.Fatalf("for %v: timeout (%v) waiting for client to timeout (%v) reading", name, tooLong, timeout)
+                       }
+
+                       select {
+                       case res := <-servec:
+                               t.Logf("for %v: server in %v wrote %d, %v", name, res.d, res.n, res.err)
+                       case err := <-acceptc:
+                               t.Fatalf("for %v: server Accept = %v", name, err)
+                       case <-time.After(tooLong):
+                               t.Fatalf("for %v, timeout waiting for server to finish writing", name)
+                       }
+               }
+       }
+}
+
+// TestReadDeadlineDataAvailable tests that read deadlines work, even
+// if there's data ready to be read.
+func TestReadDeadlineDataAvailable(t *testing.T) {
+       ln := newLocalListener(t)
+       defer ln.Close()
+
+       servec := make(chan copyRes)
+       const msg = "data client shouldn't read, even though it it'll be waiting"
+       go func() {
+               c, err := ln.Accept()
+               if err != nil {
+                       t.Fatalf("Accept: %v", err)
+               }
+               defer c.Close()
+               n, err := c.Write([]byte(msg))
+               servec <- copyRes{n: int64(n), err: err}
+       }()
+
+       c, err := Dial("tcp", ln.Addr().String())
+       if err != nil {
+               t.Fatalf("Dial: %v", err)
+       }
+       defer c.Close()
+       if res := <-servec; res.err != nil || res.n != int64(len(msg)) {
+               t.Fatalf("unexpected server Write: n=%d, err=%d; want n=%d, err=nil", res.n, res.err, len(msg))
+       }
+       c.SetReadDeadline(time.Now().Add(-5 * time.Second)) // in the psat.
+       buf := make([]byte, len(msg)/2)
+       n, err := c.Read(buf)
+       if n > 0 || !isTimeout(err) {
+               t.Fatalf("client read = %d (%q) err=%v; want 0, timeout", n, buf[:n], err)
+       }
+}
+
+// TestWriteDeadlineBufferAvailable tests that write deadlines work, even
+// if there's buffer space available to write.
+func TestWriteDeadlineBufferAvailable(t *testing.T) {
+       ln := newLocalListener(t)
+       defer ln.Close()
+
+       servec := make(chan copyRes)
+       go func() {
+               c, err := ln.Accept()
+               if err != nil {
+                       t.Fatalf("Accept: %v", err)
+               }
+               defer c.Close()
+               c.SetWriteDeadline(time.Now().Add(-5 * time.Second)) // in the past
+               n, err := c.Write([]byte{'x'})
+               servec <- copyRes{n: int64(n), err: err}
+       }()
+
+       c, err := Dial("tcp", ln.Addr().String())
+       if err != nil {
+               t.Fatalf("Dial: %v", err)
+       }
+       defer c.Close()
+       res := <-servec
+       if res.n != 0 {
+               t.Errorf("Write = %d; want 0", res.n)
+       }
+       if !isTimeout(res.err) {
+               t.Errorf("Write error = %v; want timeout", res.err)
+       }
+}
+
+// TestProlongTimeout tests concurrent deadline modification.
+// Known to cause data races in the past.
+func TestProlongTimeout(t *testing.T) {
+       switch runtime.GOOS {
+       case "plan9":
+               t.Logf("skipping test on %q", runtime.GOOS)
+               return
+       }
+
+       ln := newLocalListener(t)
+       defer ln.Close()
+       connected := make(chan bool)
+       go func() {
+               s, err := ln.Accept()
+               connected <- true
+               if err != nil {
+                       t.Fatalf("ln.Accept: %v", err)
+               }
+               defer s.Close()
+               s.SetDeadline(time.Now().Add(time.Hour))
+               go func() {
+                       var buf [4096]byte
+                       for {
+                               _, err := s.Write(buf[:])
+                               if err != nil {
+                                       break
+                               }
+                               s.SetDeadline(time.Now().Add(time.Hour))
+                       }
+               }()
+               buf := make([]byte, 1)
+               for {
+                       _, err := s.Read(buf)
+                       if err != nil {
+                               break
+                       }
+                       s.SetDeadline(time.Now().Add(time.Hour))
+               }
+       }()
+       c, err := Dial("tcp", ln.Addr().String())
+       if err != nil {
+               t.Fatalf("DialTCP: %v", err)
+       }
+       defer c.Close()
+       <-connected
+       for i := 0; i < 1024; i++ {
+               var buf [1]byte
+               c.Write(buf[:])
+       }
+}
index 37b904f3242218d606b7ea1a2a1ab0f43a34f00b..d3594b40a9e25e720b3c0ded562283ff412aac2c 100644 (file)
@@ -5,10 +5,41 @@
 package net
 
 import (
+       "reflect"
        "runtime"
        "testing"
 )
 
+var resolveUDPAddrTests = []struct {
+       net     string
+       litAddr string
+       addr    *UDPAddr
+       err     error
+}{
+       {"udp", "127.0.0.1:0", &UDPAddr{IP: IPv4(127, 0, 0, 1), Port: 0}, nil},
+       {"udp4", "127.0.0.1:65535", &UDPAddr{IP: IPv4(127, 0, 0, 1), Port: 65535}, nil},
+
+       {"udp", "[::1]:1", &UDPAddr{IP: ParseIP("::1"), Port: 1}, nil},
+       {"udp6", "[::1]:65534", &UDPAddr{IP: ParseIP("::1"), Port: 65534}, nil},
+
+       {"", "127.0.0.1:0", &UDPAddr{IP: IPv4(127, 0, 0, 1), Port: 0}, nil}, // Go 1.0 behavior
+       {"", "[::1]:0", &UDPAddr{IP: ParseIP("::1"), Port: 0}, nil},         // Go 1.0 behavior
+
+       {"sip", "127.0.0.1:0", nil, UnknownNetworkError("sip")},
+}
+
+func TestResolveUDPAddr(t *testing.T) {
+       for _, tt := range resolveUDPAddrTests {
+               addr, err := ResolveUDPAddr(tt.net, tt.litAddr)
+               if err != tt.err {
+                       t.Fatalf("ResolveUDPAddr(%v, %v) failed: %v", tt.net, tt.litAddr, err)
+               }
+               if !reflect.DeepEqual(addr, tt.addr) {
+                       t.Fatalf("got %#v; expected %#v", addr, tt.addr)
+               }
+       }
+}
+
 func TestWriteToUDP(t *testing.T) {
        switch runtime.GOOS {
        case "plan9":
index bf2107b03a22438267ccc8be78ce1127684a2325..6e5e902689b39a41f170c9fcbd2b2b11c40120d5 100644 (file)
@@ -6,10 +6,7 @@
 
 package net
 
-import (
-       "errors"
-       "time"
-)
+import "errors"
 
 var ErrWriteToConnected = errors.New("use of WriteTo with pre-connected UDP")
 
@@ -17,6 +14,7 @@ var ErrWriteToConnected = errors.New("use of WriteTo with pre-connected UDP")
 type UDPAddr struct {
        IP   IP
        Port int
+       Zone string // IPv6 scoped addressing zone
 }
 
 // Network returns the address's network name, "udp".
@@ -35,13 +33,16 @@ func (a *UDPAddr) String() string {
 // "udp4" or "udp6".  A literal IPv6 host address must be
 // enclosed in square brackets, as in "[::]:80".
 func ResolveUDPAddr(net, addr string) (*UDPAddr, error) {
-       return resolveUDPAddr(net, addr, noDeadline)
-}
-
-func resolveUDPAddr(net, addr string, deadline time.Time) (*UDPAddr, error) {
-       ip, port, err := hostPortToIP(net, addr, deadline)
+       switch net {
+       case "udp", "udp4", "udp6":
+       case "": // a hint wildcard for Go 1.0 undocumented behavior
+               net = "udp"
+       default:
+               return nil, UnknownNetworkError(net)
+       }
+       a, err := resolveInternetAddr(net, addr, noDeadline)
        if err != nil {
                return nil, err
        }
-       return &UDPAddr{ip, port}, nil
+       return a.(*UDPAddr), nil
 }
index 6a828e14d20378dac788baff9237b4901179d3be..b9ade48bec97065fdb932b8c1f4205dc0023585d 100644 (file)
@@ -19,8 +19,6 @@ type UDPConn struct {
        conn
 }
 
-// UDP-specific methods.
-
 // ReadFromUDP reads a UDP packet from c, copying the payload into b.
 // It returns the number of bytes copied into b and the return address
 // that was on the packet.
@@ -50,11 +48,11 @@ func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err error) {
 
        h, buf := unmarshalUDPHeader(buf)
        n = copy(b, buf)
-       return n, &UDPAddr{h.raddr, int(h.rport)}, nil
+       return n, &UDPAddr{IP: h.raddr, Port: int(h.rport)}, nil
 }
 
 // ReadFrom implements the PacketConn ReadFrom method.
-func (c *UDPConn) ReadFrom(b []byte) (n int, addr Addr, err error) {
+func (c *UDPConn) ReadFrom(b []byte) (int, Addr, error) {
        if !c.ok() {
                return 0, nil, syscall.EINVAL
        }
@@ -77,15 +75,16 @@ func (c *UDPConn) ReadMsgUDP(b, oob []byte) (n, oobn, flags int, addr *UDPAddr,
 // Timeout() == true after a fixed time limit; see SetDeadline and
 // SetWriteDeadline.  On packet-oriented connections, write timeouts
 // are rare.
-func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (n int, err error) {
+func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (int, error) {
        if !c.ok() {
                return 0, syscall.EINVAL
        }
        if c.fd.data == nil {
-               c.fd.data, err = os.OpenFile(c.fd.dir+"/data", os.O_RDWR, 0)
+               f, err := os.OpenFile(c.fd.dir+"/data", os.O_RDWR, 0)
                if err != nil {
                        return 0, err
                }
+               c.fd.data = f
        }
        h := new(udpHeader)
        h.raddr = addr.IP.To16()
@@ -101,7 +100,7 @@ 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) {
+func (c *UDPConn) WriteTo(b []byte, addr Addr) (int, error) {
        if !c.ok() {
                return 0, syscall.EINVAL
        }
@@ -122,7 +121,7 @@ func (c *UDPConn) WriteMsgUDP(b, oob []byte, addr *UDPAddr) (n, oobn int, err er
 // DialUDP connects to the remote address raddr on the network net,
 // which must be "udp", "udp4", or "udp6".  If laddr is not nil, it is
 // used as the local address for the connection.
-func DialUDP(net string, laddr, raddr *UDPAddr) (c *UDPConn, err error) {
+func DialUDP(net string, laddr, raddr *UDPAddr) (*UDPConn, error) {
        return dialUDP(net, laddr, raddr, noDeadline)
 }
 
index d7329bf32fc1809fce396a925b3ef7a3dd681be9..b7de678f928c979b58940edb4e598b8a18d5f7f6 100644 (file)
@@ -4,7 +4,7 @@
 
 // +build darwin freebsd linux netbsd openbsd windows
 
-// UDP sockets
+// UDP sockets for POSIX
 
 package net
 
@@ -16,9 +16,9 @@ import (
 func sockaddrToUDP(sa syscall.Sockaddr) Addr {
        switch sa := sa.(type) {
        case *syscall.SockaddrInet4:
-               return &UDPAddr{sa.Addr[0:], sa.Port}
+               return &UDPAddr{IP: sa.Addr[0:], Port: sa.Port}
        case *syscall.SockaddrInet6:
-               return &UDPAddr{sa.Addr[0:], sa.Port}
+               return &UDPAddr{IP: sa.Addr[0:], Port: sa.Port, Zone: zoneToString(int(sa.ZoneId))}
        }
        return nil
 }
@@ -41,7 +41,7 @@ func (a *UDPAddr) isWildcard() bool {
 }
 
 func (a *UDPAddr) sockaddr(family int) (syscall.Sockaddr, error) {
-       return ipToSockaddr(family, a.IP, a.Port)
+       return ipToSockaddr(family, a.IP, a.Port, a.Zone)
 }
 
 func (a *UDPAddr) toAddr() sockaddr {
@@ -59,8 +59,6 @@ type UDPConn struct {
 
 func newUDPConn(fd *netFD) *UDPConn { return &UDPConn{conn{fd}} }
 
-// UDP-specific methods.
-
 // ReadFromUDP reads a UDP packet from c, copying the payload into b.
 // It returns the number of bytes copied into b and the return address
 // that was on the packet.
@@ -74,9 +72,9 @@ func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err error) {
        n, sa, err := c.fd.ReadFrom(b)
        switch sa := sa.(type) {
        case *syscall.SockaddrInet4:
-               addr = &UDPAddr{sa.Addr[0:], sa.Port}
+               addr = &UDPAddr{IP: sa.Addr[0:], Port: sa.Port}
        case *syscall.SockaddrInet6:
-               addr = &UDPAddr{sa.Addr[0:], sa.Port}
+               addr = &UDPAddr{IP: sa.Addr[0:], Port: sa.Port, Zone: zoneToString(int(sa.ZoneId))}
        }
        return
 }
@@ -86,8 +84,8 @@ func (c *UDPConn) ReadFrom(b []byte) (int, Addr, error) {
        if !c.ok() {
                return 0, nil, syscall.EINVAL
        }
-       n, uaddr, err := c.ReadFromUDP(b)
-       return n, uaddr.toAddr(), err
+       n, addr, err := c.ReadFromUDP(b)
+       return n, addr.toAddr(), err
 }
 
 // ReadMsgUDP reads a packet from c, copying the payload into b and
@@ -103,9 +101,9 @@ func (c *UDPConn) ReadMsgUDP(b, oob []byte) (n, oobn, flags int, addr *UDPAddr,
        n, oobn, flags, sa, err = c.fd.ReadMsg(b, oob)
        switch sa := sa.(type) {
        case *syscall.SockaddrInet4:
-               addr = &UDPAddr{sa.Addr[0:], sa.Port}
+               addr = &UDPAddr{IP: sa.Addr[0:], Port: sa.Port}
        case *syscall.SockaddrInet6:
-               addr = &UDPAddr{sa.Addr[0:], sa.Port}
+               addr = &UDPAddr{IP: sa.Addr[0:], Port: sa.Port, Zone: zoneToString(int(sa.ZoneId))}
        }
        return
 }
@@ -276,7 +274,7 @@ func listenIPv6MulticastUDP(c *UDPConn, ifi *Interface, ip IP) error {
 func joinIPv4GroupUDP(c *UDPConn, ifi *Interface, ip IP) error {
        err := joinIPv4Group(c.fd, ifi, ip)
        if err != nil {
-               return &OpError{"joinipv4group", c.fd.net, &IPAddr{ip}, err}
+               return &OpError{"joinipv4group", c.fd.net, &IPAddr{IP: ip}, err}
        }
        return nil
 }
@@ -284,7 +282,7 @@ func joinIPv4GroupUDP(c *UDPConn, ifi *Interface, ip IP) error {
 func joinIPv6GroupUDP(c *UDPConn, ifi *Interface, ip IP) error {
        err := joinIPv6Group(c.fd, ifi, ip)
        if err != nil {
-               return &OpError{"joinipv6group", c.fd.net, &IPAddr{ip}, err}
+               return &OpError{"joinipv6group", c.fd.net, &IPAddr{IP: ip}, err}
        }
        return nil
 }
index 342e26fce0239ba8d2533cbffb352c57f22714a3..f7be5d2e9ae0f5c4d45b3138ecaacd9c08d3a2ad 100644 (file)
@@ -14,67 +14,8 @@ import (
 
 // UnixConn is an implementation of the Conn interface for connections
 // to Unix domain sockets.
-type UnixConn bool
-
-// Implementation of the Conn interface - see Conn for documentation.
-
-// Read implements the Conn Read method.
-func (c *UnixConn) Read(b []byte) (int, error) {
-       return 0, syscall.EPLAN9
-}
-
-// Write implements the Conn Write method.
-func (c *UnixConn) Write(b []byte) (int, error) {
-       return 0, syscall.EPLAN9
-}
-
-// LocalAddr returns the local network address.
-func (c *UnixConn) LocalAddr() Addr {
-       return nil
-}
-
-// RemoteAddr returns the remote network address.
-func (c *UnixConn) RemoteAddr() Addr {
-       return nil
-}
-
-// SetDeadline implements the Conn SetDeadline method.
-func (c *UnixConn) SetDeadline(t time.Time) error {
-       return syscall.EPLAN9
-}
-
-// SetReadDeadline implements the Conn SetReadDeadline method.
-func (c *UnixConn) SetReadDeadline(t time.Time) error {
-       return syscall.EPLAN9
-}
-
-// SetWriteDeadline implements the Conn SetWriteDeadline method.
-func (c *UnixConn) SetWriteDeadline(t time.Time) error {
-       return syscall.EPLAN9
-}
-
-// SetReadBuffer sets the size of the operating system's receive
-// buffer associated with the connection.
-func (c *UnixConn) SetReadBuffer(bytes int) error {
-       return syscall.EPLAN9
-}
-
-// SetWriteBuffer sets the size of the operating system's transmit
-// buffer associated with the connection.
-func (c *UnixConn) SetWriteBuffer(bytes int) error {
-       return syscall.EPLAN9
-}
-
-// File returns a copy of the underlying os.File, set to blocking
-// mode.  It is the caller's responsibility to close f when finished.
-// Closing c does not affect f, and closing f does not affect c.
-func (c *UnixConn) File() (f *os.File, err error) {
-       return nil, syscall.EPLAN9
-}
-
-// Close closes the Unix domain connection.
-func (c *UnixConn) Close() error {
-       return syscall.EPLAN9
+type UnixConn struct {
+       conn
 }
 
 // ReadFromUnix reads a packet from c, copying the payload into b.  It
@@ -149,7 +90,7 @@ func dialUnix(net string, laddr, raddr *UnixAddr, deadline time.Time) (*UnixConn
 // UnixListener is a Unix domain socket listener.  Clients should
 // typically use variables of type Listener instead of assuming Unix
 // domain sockets.
-type UnixListener bool
+type UnixListener struct{}
 
 // ListenUnix announces on the Unix domain socket laddr and returns a
 // Unix listener.  Net must be "unix" (stream sockets).
index d1fff89da79609548fc5cef9bac233e2251a1b18..692a7fdc0484702f579f5c1f123705d751633e97 100644 (file)
@@ -224,7 +224,7 @@ type URL struct {
        Scheme   string
        Opaque   string    // encoded opaque data
        User     *Userinfo // username and password information
-       Host     string
+       Host     string    // host or host:port
        Path     string
        RawQuery string // encoded query values, without '?'
        Fragment string // fragment for references, without '#'
index 060c0b2e8f870c4d654b26025c708f06091ede95..8195c02a4659e864bde357a746b0ce77849aadd3 100644 (file)
@@ -5,15 +5,11 @@
 package os
 
 import (
-       "errors"
        "io"
        "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) {
+func (file *File) readdir(n int) ([]FileInfo, error) {
        // If this file has no dirinfo, create one.
        if file.dirinfo == nil {
                file.dirinfo = new(dirInfo)
@@ -24,44 +20,47 @@ func (file *File) readdir(n int) (fi []FileInfo, err error) {
                size = 100
                n = -1
        }
-       result := make([]FileInfo, 0, size) // Empty with room to grow.
+       fi := make([]FileInfo, 0, size) // Empty with room to grow.
        for n != 0 {
-               // Refill the buffer if necessary
+               // Refill the buffer if necessary.
                if d.bufp >= d.nbuf {
-                       d.bufp = 0
-                       var e error
-                       d.nbuf, e = file.Read(d.buf[:])
-                       if e != nil && e != io.EOF {
-                               return result, &PathError{"readdir", file.name, e}
-                       }
-                       if e == io.EOF {
-                               break
+                       nb, err := file.Read(d.buf[:])
+
+                       // Update the buffer state before checking for errors.
+                       d.bufp, d.nbuf = 0, nb
+
+                       if err != nil {
+                               if err == io.EOF {
+                                       break
+                               }
+                               return fi, &PathError{"readdir", file.name, err}
                        }
-                       if d.nbuf < syscall.STATFIXLEN {
-                               return result, &PathError{"readdir", file.name, errShortStat}
+                       if nb < syscall.STATFIXLEN {
+                               return fi, &PathError{"readdir", file.name, syscall.ErrShortStat}
                        }
                }
 
-               // Get a record from buffer
-               m, _ := gbit16(d.buf[d.bufp:])
-               m += 2
+               // Get a record from the buffer.
+               b := d.buf[d.bufp:]
+               m := int(uint16(b[0])|uint16(b[1])<<8) + 2
                if m < syscall.STATFIXLEN {
-                       return result, &PathError{"readdir", file.name, errShortStat}
+                       return fi, &PathError{"readdir", file.name, syscall.ErrShortStat}
                }
-               dir, e := unmarshalDir(d.buf[d.bufp : d.bufp+int(m)])
-               if e != nil {
-                       return result, &PathError{"readdir", file.name, e}
+
+               dir, err := syscall.UnmarshalDir(b[:m])
+               if err != nil {
+                       return fi, &PathError{"readdir", file.name, err}
                }
-               result = append(result, fileInfoFromStat(dir))
+               fi = append(fi, fileInfoFromStat(dir))
 
-               d.bufp += int(m)
+               d.bufp += m
                n--
        }
 
-       if n >= 0 && len(result) == 0 {
-               return result, io.EOF
+       if n >= 0 && len(fi) == 0 {
+               return fi, io.EOF
        }
-       return result, nil
+       return fi, nil
 }
 
 func (file *File) readdirnames(n int) (names []string, err error) {
@@ -72,205 +71,3 @@ func (file *File) readdirnames(n int) (names []string, err error) {
        }
        return
 }
-
-type dir struct {
-       // system-modified data
-       Type uint16 // server type
-       Dev  uint32 // server subtype
-       // file data
-       Qid    qid    // unique id from server
-       Mode   uint32 // permissions
-       Atime  uint32 // last read time
-       Mtime  uint32 // last write time
-       Length uint64 // file length
-       Name   string // last element of path
-       Uid    string // owner name
-       Gid    string // group name
-       Muid   string // last modifier name
-}
-
-type qid struct {
-       Path uint64 // the file server's unique identification for the file
-       Vers uint32 // version number for given Path
-       Type uint8  // the type of the file (syscall.QTDIR for example)
-}
-
-var nullDir = dir{
-       ^uint16(0),
-       ^uint32(0),
-       qid{^uint64(0), ^uint32(0), ^uint8(0)},
-       ^uint32(0),
-       ^uint32(0),
-       ^uint32(0),
-       ^uint64(0),
-       "",
-       "",
-       "",
-       "",
-}
-
-// Null assigns members of d with special "don't care" values indicating
-// they should not be written by syscall.Wstat.
-func (d *dir) Null() {
-       *d = nullDir
-}
-
-// pdir appends a 9P Stat message based on the contents of Dir d to a byte slice b.
-func pdir(b []byte, d *dir) []byte {
-       n := len(b)
-       b = pbit16(b, 0) // length, filled in later
-       b = pbit16(b, d.Type)
-       b = pbit32(b, d.Dev)
-       b = pqid(b, d.Qid)
-       b = pbit32(b, d.Mode)
-       b = pbit32(b, d.Atime)
-       b = pbit32(b, d.Mtime)
-       b = pbit64(b, d.Length)
-       b = pstring(b, d.Name)
-       b = pstring(b, d.Uid)
-       b = pstring(b, d.Gid)
-       b = pstring(b, d.Muid)
-       pbit16(b[0:n], uint16(len(b)-(n+2)))
-       return b
-}
-
-// unmarshalDir reads a 9P Stat message from a 9P protocol message stored in b,
-// returning the corresponding dir struct.
-func unmarshalDir(b []byte) (d *dir, err error) {
-       n := uint16(0)
-       n, b = gbit16(b)
-
-       if int(n) != len(b) {
-               return nil, errBadStat
-       }
-
-       d = new(dir)
-       d.Type, b = gbit16(b)
-       d.Dev, b = gbit32(b)
-       d.Qid, b = gqid(b)
-       d.Mode, b = gbit32(b)
-       d.Atime, b = gbit32(b)
-       d.Mtime, b = gbit32(b)
-       d.Length, b = gbit64(b)
-       d.Name, b = gstring(b)
-       d.Uid, b = gstring(b)
-       d.Gid, b = gstring(b)
-       d.Muid, b = gstring(b)
-
-       if len(b) != 0 {
-               return nil, errBadStat
-       }
-
-       return d, nil
-}
-
-// gqid reads the qid part of a 9P Stat message from a 9P protocol message stored in b,
-// returning the corresponding qid struct and the remaining slice of b.
-func gqid(b []byte) (qid, []byte) {
-       var q qid
-       q.Path, b = gbit64(b)
-       q.Vers, b = gbit32(b)
-       q.Type, b = gbit8(b)
-       return q, b
-}
-
-// pqid appends a qid struct q to a 9P message b.
-func pqid(b []byte, q qid) []byte {
-       b = pbit64(b, q.Path)
-       b = pbit32(b, q.Vers)
-       b = pbit8(b, q.Type)
-       return b
-}
-
-// gbit8 reads a byte-sized numeric value from a 9P protocol message stored in b,
-// returning the value and the remaining slice of b.
-func gbit8(b []byte) (uint8, []byte) {
-       return uint8(b[0]), b[1:]
-}
-
-// gbit16 reads a 16-bit numeric value from a 9P protocol message stored in b,
-// returning the value and the remaining slice of b.
-func gbit16(b []byte) (uint16, []byte) {
-       return uint16(b[0]) | uint16(b[1])<<8, b[2:]
-}
-
-// gbit32 reads a 32-bit numeric value from a 9P protocol message stored in b,
-// returning the value and the remaining slice of b.
-func gbit32(b []byte) (uint32, []byte) {
-       return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24, b[4:]
-}
-
-// gbit64 reads a 64-bit numeric value from a 9P protocol message stored in b,
-// returning the value and the remaining slice of b.
-func gbit64(b []byte) (uint64, []byte) {
-       lo, b := gbit32(b)
-       hi, b := gbit32(b)
-       return uint64(hi)<<32 | uint64(lo), b
-}
-
-// gstring reads a string from a 9P protocol message stored in b,
-// returning the value as a Go string and the remaining slice of b.
-func gstring(b []byte) (string, []byte) {
-       n, b := gbit16(b)
-       return string(b[0:n]), b[n:]
-}
-
-// pbit8 appends a byte-sized numeric value x to a 9P message b.
-func pbit8(b []byte, x uint8) []byte {
-       n := len(b)
-       if n+1 > cap(b) {
-               nb := make([]byte, n, 100+2*cap(b))
-               copy(nb, b)
-               b = nb
-       }
-       b = b[0 : n+1]
-       b[n] = x
-       return b
-}
-
-// pbit16 appends a 16-bit numeric value x to a 9P message b.
-func pbit16(b []byte, x uint16) []byte {
-       n := len(b)
-       if n+2 > cap(b) {
-               nb := make([]byte, n, 100+2*cap(b))
-               copy(nb, b)
-               b = nb
-       }
-       b = b[0 : n+2]
-       b[n] = byte(x)
-       b[n+1] = byte(x >> 8)
-       return b
-}
-
-// pbit32 appends a 32-bit numeric value x to a 9P message b.
-func pbit32(b []byte, x uint32) []byte {
-       n := len(b)
-       if n+4 > cap(b) {
-               nb := make([]byte, n, 100+2*cap(b))
-               copy(nb, b)
-               b = nb
-       }
-       b = b[0 : n+4]
-       b[n] = byte(x)
-       b[n+1] = byte(x >> 8)
-       b[n+2] = byte(x >> 16)
-       b[n+3] = byte(x >> 24)
-       return b
-}
-
-// pbit64 appends a 64-bit numeric value x to a 9P message b.
-func pbit64(b []byte, x uint64) []byte {
-       b = pbit32(b, uint32(x))
-       b = pbit32(b, uint32(x>>32))
-       return b
-}
-
-// pstring appends a Go string s to a 9P message b.
-func pstring(b []byte, s string) []byte {
-       if len(s) >= 1<<16 {
-               panic(errors.New("string too long"))
-       }
-       b = pbit16(b, uint16(len(s)))
-       b = append(b, s...)
-       return b
-}
index db366a07cc9e626fa4341e7a57a32bafd64bc330..fb2f2347d7d071fb7c99a14198d3b59b8221b5a9 100644 (file)
@@ -169,13 +169,18 @@ func (f *File) Stat() (fi FileInfo, err error) {
 // It does not change the I/O offset.
 // If there is an error, it will be of type *PathError.
 func (f *File) Truncate(size int64) error {
-       var d dir
-       d.Null()
+       var d syscall.Dir
 
-       d.Length = uint64(size)
+       d.Null()
+       d.Length = size
 
-       if e := syscall.Fwstat(f.fd, pdir(nil, &d)); e != nil {
-               return &PathError{"truncate", f.name, e}
+       var buf [syscall.STATFIXLEN]byte
+       n, err := d.Marshal(buf[:])
+       if err != nil {
+               return &PathError{"truncate", f.name, err}
+       }
+       if err = syscall.Fwstat(f.fd, buf[:n]); err != nil {
+               return &PathError{"truncate", f.name, err}
        }
        return nil
 }
@@ -185,7 +190,7 @@ const chmodMask = uint32(syscall.DMAPPEND | syscall.DMEXCL | syscall.DMTMP | Mod
 // Chmod changes the mode of the file to mode.
 // If there is an error, it will be of type *PathError.
 func (f *File) Chmod(mode FileMode) error {
-       var d dir
+       var d syscall.Dir
 
        odir, e := dirstat(f)
        if e != nil {
@@ -193,8 +198,14 @@ func (f *File) Chmod(mode FileMode) error {
        }
        d.Null()
        d.Mode = odir.Mode&^chmodMask | syscallMode(mode)&chmodMask
-       if e := syscall.Fwstat(f.fd, pdir(nil, &d)); e != nil {
-               return &PathError{"chmod", f.name, e}
+
+       var buf [syscall.STATFIXLEN]byte
+       n, err := d.Marshal(buf[:])
+       if err != nil {
+               return &PathError{"chmod", f.name, err}
+       }
+       if err = syscall.Fwstat(f.fd, buf[:n]); err != nil {
+               return &PathError{"chmod", f.name, err}
        }
        return nil
 }
@@ -206,12 +217,16 @@ func (f *File) Sync() (err error) {
        if f == nil {
                return ErrInvalid
        }
-
-       var d dir
+       var d syscall.Dir
        d.Null()
 
-       if e := syscall.Fwstat(f.fd, pdir(nil, &d)); e != nil {
-               return NewSyscallError("fsync", e)
+       var buf [syscall.STATFIXLEN]byte
+       n, err := d.Marshal(buf[:])
+       if err != nil {
+               return NewSyscallError("fsync", err)
+       }
+       if err = syscall.Fwstat(f.fd, buf[:n]); err != nil {
+               return NewSyscallError("fsync", err)
        }
        return nil
 }
@@ -253,13 +268,18 @@ func (f *File) seek(offset int64, whence int) (ret int64, err error) {
 // If the file is a symbolic link, it changes the size of the link's target.
 // If there is an error, it will be of type *PathError.
 func Truncate(name string, size int64) error {
-       var d dir
-       d.Null()
+       var d syscall.Dir
 
-       d.Length = uint64(size)
+       d.Null()
+       d.Length = size
 
-       if e := syscall.Wstat(name, pdir(nil, &d)); e != nil {
-               return &PathError{"truncate", name, e}
+       var buf [syscall.STATFIXLEN]byte
+       n, err := d.Marshal(buf[:])
+       if err != nil {
+               return &PathError{"truncate", name, err}
+       }
+       if err = syscall.Wstat(name, buf[:n]); err != nil {
+               return &PathError{"truncate", name, err}
        }
        return nil
 }
@@ -275,13 +295,18 @@ func Remove(name string) error {
 
 // Rename renames a file.
 func Rename(oldname, newname string) error {
-       var d dir
-       d.Null()
+       var d syscall.Dir
 
+       d.Null()
        d.Name = newname
 
-       if e := syscall.Wstat(oldname, pdir(nil, &d)); e != nil {
-               return &PathError{"rename", oldname, e}
+       var buf [syscall.STATFIXLEN]byte
+       n, err := d.Marshal(buf[:])
+       if err != nil {
+               return &PathError{"rename", oldname, err}
+       }
+       if err = syscall.Wstat(oldname, buf[:n]); err != nil {
+               return &PathError{"rename", oldname, err}
        }
        return nil
 }
@@ -290,7 +315,7 @@ func Rename(oldname, newname string) error {
 // If the file is a symbolic link, it changes the mode of the link's target.
 // If there is an error, it will be of type *PathError.
 func Chmod(name string, mode FileMode) error {
-       var d dir
+       var d syscall.Dir
 
        odir, e := dirstat(name)
        if e != nil {
@@ -298,8 +323,14 @@ func Chmod(name string, mode FileMode) error {
        }
        d.Null()
        d.Mode = odir.Mode&^chmodMask | syscallMode(mode)&chmodMask
-       if e := syscall.Wstat(name, pdir(nil, &d)); e != nil {
-               return &PathError{"chmod", name, e}
+
+       var buf [syscall.STATFIXLEN]byte
+       n, err := d.Marshal(buf[:])
+       if err != nil {
+               return &PathError{"chmod", name, err}
+       }
+       if err = syscall.Wstat(name, buf[:n]); err != nil {
+               return &PathError{"chmod", name, err}
        }
        return nil
 }
@@ -311,14 +342,19 @@ func Chmod(name string, mode FileMode) error {
 // less precise time unit.
 // If there is an error, it will be of type *PathError.
 func Chtimes(name string, atime time.Time, mtime time.Time) error {
-       var d dir
-       d.Null()
+       var d syscall.Dir
 
+       d.Null()
        d.Atime = uint32(atime.Unix())
        d.Mtime = uint32(mtime.Unix())
 
-       if e := syscall.Wstat(name, pdir(nil, &d)); e != nil {
-               return &PathError{"chtimes", name, e}
+       var buf [syscall.STATFIXLEN]byte
+       n, err := d.Marshal(buf[:])
+       if err != nil {
+               return &PathError{"chtimes", name, err}
+       }
+       if err = syscall.Wstat(name, buf[:n]); err != nil {
+               return &PathError{"chtimes", name, err}
        }
        return nil
 }
index 671c301f048b0f7f99a28e3814ca419fd4976084..9f0bbdc25cc09864e3d6045e3df5f6b7f9294bb4 100644 (file)
@@ -1093,3 +1093,25 @@ func TestLargeWriteToConsole(t *testing.T) {
                t.Errorf("Write to os.Stderr should return %d; got %d", len(b), n)
        }
 }
+
+func TestStatDirModeExec(t *testing.T) {
+       const mode = 0111
+
+       path, err := ioutil.TempDir("", "go-build")
+       if err != nil {
+               t.Fatalf("Failed to create temp directory: %v", err)
+       }
+       defer RemoveAll(path)
+
+       if err := Chmod(path, 0777); err != nil {
+               t.Fatalf("Chmod %q 0777: %v", path, err)
+       }
+
+       dir, err := Stat(path)
+       if err != nil {
+               t.Fatalf("Stat %q (looking for mode %#o): %s", path, mode, err)
+       }
+       if dir.Mode()&mode != mode {
+               t.Errorf("Stat %q: mode %#o want %#o", path, dir.Mode()&mode, mode)
+       }
+}
index b3dd188343320fb18b3277db824f50a84bbf6ece..6822cc019eadb8ea5f2f66a1deb1824209678d15 100644 (file)
@@ -10,12 +10,12 @@ import (
 )
 
 func sameFile(sys1, sys2 interface{}) bool {
-       a := sys1.(*dir)
-       b := sys2.(*dir)
+       a := sys1.(*syscall.Dir)
+       b := sys2.(*syscall.Dir)
        return a.Qid.Path == b.Qid.Path && a.Type == b.Type && a.Dev == b.Dev
 }
 
-func fileInfoFromStat(d *dir) FileInfo {
+func fileInfoFromStat(d *syscall.Dir) FileInfo {
        fs := &fileStat{
                name:    d.Name,
                size:    int64(d.Length),
@@ -39,7 +39,7 @@ func fileInfoFromStat(d *dir) FileInfo {
 }
 
 // arg is an open *File or a path string.
-func dirstat(arg interface{}) (d *dir, err error) {
+func dirstat(arg interface{}) (*syscall.Dir, error) {
        var name string
 
        // This is big enough for most stat messages
@@ -50,36 +50,40 @@ func dirstat(arg interface{}) (d *dir, err error) {
                buf := make([]byte, size)
 
                var n int
+               var err error
                switch a := arg.(type) {
                case *File:
                        name = a.name
                        n, err = syscall.Fstat(a.fd, buf)
                case string:
                        name = a
-                       n, err = syscall.Stat(name, buf)
+                       n, err = syscall.Stat(a, buf)
+               default:
+                       panic("phase error in dirstat")
                }
                if err != nil {
                        return nil, &PathError{"stat", name, err}
                }
                if n < syscall.STATFIXLEN {
-                       return nil, &PathError{"stat", name, errShortStat}
+                       return nil, &PathError{"stat", name, syscall.ErrShortStat}
                }
 
                // Pull the real size out of the stat message.
-               s, _ := gbit16(buf)
-               size = int(s)
+               size = int(uint16(buf[0]) | uint16(buf[1])<<8)
 
                // If the stat message is larger than our buffer we will
                // go around the loop and allocate one that is big enough.
-               if size <= n {
-                       d, err = unmarshalDir(buf[:n])
-                       if err != nil {
-                               return nil, &PathError{"stat", name, err}
-                       }
-                       return
+               if size > n {
+                       continue
                }
+
+               d, err := syscall.UnmarshalDir(buf[:n])
+               if err != nil {
+                       return nil, &PathError{"stat", name, err}
+               }
+               return d, nil
        }
-       return nil, &PathError{"stat", name, errBadStat}
+       return nil, &PathError{"stat", name, syscall.ErrBadStat}
 }
 
 // Stat returns a FileInfo describing the named file.
@@ -102,5 +106,5 @@ func Lstat(name string) (fi FileInfo, err error) {
 
 // For testing.
 func atime(fi FileInfo) time.Time {
-       return time.Unix(int64(fi.Sys().(*dir).Atime), 0)
+       return time.Unix(int64(fi.Sys().(*syscall.Dir).Atime), 0)
 }
diff --git a/libgo/go/os/user/lookup.go b/libgo/go/os/user/lookup.go
new file mode 100644 (file)
index 0000000..09f00c7
--- /dev/null
@@ -0,0 +1,22 @@
+// 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 user
+
+// Current returns the current user.
+func Current() (*User, error) {
+       return current()
+}
+
+// Lookup looks up a user by username. If the user cannot be found, the
+// returned error is of type UnknownUserError.
+func Lookup(username string) (*User, error) {
+       return lookup(username)
+}
+
+// LookupId looks up a user by userid. If the user cannot be found, the
+// returned error is of type UnknownUserIdError.
+func LookupId(uid string) (*User, error) {
+       return lookupId(uid)
+}
index 415f869f2291b358a1592d20320093dc1e701862..ad06907b5d57760a5deab8a0944bdc911145be8b 100644 (file)
@@ -15,14 +15,14 @@ func init() {
        implemented = false
 }
 
-func Current() (*User, error) {
+func current() (*User, error) {
        return nil, fmt.Errorf("user: Current not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
 }
 
-func Lookup(username string) (*User, error) {
+func lookup(username string) (*User, error) {
        return nil, fmt.Errorf("user: Lookup not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
 }
 
-func LookupId(string) (*User, error) {
+func lookupId(uid string) (*User, error) {
        return nil, fmt.Errorf("user: LookupId not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
 }
index e0baae2dc487fe7c1663d004ba85e55ef7fc5d5b..7e67495f1b717cca6e03a64d53e3d51df525ce3e 100644 (file)
@@ -44,28 +44,23 @@ func bytePtrToString(p *byte) string {
        return string(a[:i])
 }
 
-// Current returns the current user.
-func Current() (*User, error) {
-       return lookup(syscall.Getuid(), "", false)
+func current() (*User, error) {
+       return lookupUnix(syscall.Getuid(), "", false)
 }
 
-// Lookup looks up a user by username. If the user cannot be found,
-// the returned error is of type UnknownUserError.
-func Lookup(username string) (*User, error) {
-       return lookup(-1, username, true)
+func lookup(username string) (*User, error) {
+       return lookupUnix(-1, username, true)
 }
 
-// LookupId looks up a user by userid. If the user cannot be found,
-// the returned error is of type UnknownUserIdError.
-func LookupId(uid string) (*User, error) {
+func lookupId(uid string) (*User, error) {
        i, e := strconv.Atoi(uid)
        if e != nil {
                return nil, e
        }
-       return lookup(i, "", false)
+       return lookupUnix(i, "", false)
 }
 
-func lookup(uid int, username string, lookupByName bool) (*User, error) {
+func lookupUnix(uid int, username string, lookupByName bool) (*User, error) {
        var pwd syscall.Passwd
        var result *syscall.Passwd
 
index 3626a4e9f059b7b97d2f3fd151045877ffb4f83a..a0a8a4ec10f3fcf4c1c37b323a83cc45a58eaa43 100644 (file)
@@ -68,8 +68,7 @@ func newUser(usid *syscall.SID, gid, dir string) (*User, error) {
        return u, nil
 }
 
-// Current returns the current user.
-func Current() (*User, error) {
+func current() (*User, error) {
        t, e := syscall.OpenCurrentProcessToken()
        if e != nil {
                return nil, e
@@ -103,8 +102,7 @@ func newUserFromSid(usid *syscall.SID) (*User, error) {
        return newUser(usid, gid, dir)
 }
 
-// Lookup looks up a user by username.
-func Lookup(username string) (*User, error) {
+func lookup(username string) (*User, error) {
        sid, _, t, e := syscall.LookupSID("", username)
        if e != nil {
                return nil, e
@@ -115,8 +113,7 @@ func Lookup(username string) (*User, error) {
        return newUserFromSid(sid)
 }
 
-// LookupId looks up a user by userid.
-func LookupId(uid string) (*User, error) {
+func lookupId(uid string) (*User, error) {
        sid, e := syscall.StringToSid(uid)
        if e != nil {
                return nil, e
index 577b0a3e125ab1c60aed32e17b3af8f9349c7df6..bee4d95bc7ce12d4e4348c45bba38cebac2fd9a0 100644 (file)
@@ -91,6 +91,7 @@ var wincleantests = []PathTest{
 }
 
 func TestClean(t *testing.T) {
+       defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
        tests := cleantests
        if runtime.GOOS == "windows" {
                for i := range tests {
@@ -902,7 +903,7 @@ func TestDriveLetterInEvalSymlinks(t *testing.T) {
    differently.
 
 func TestBug3486(t *testing.T) { // http://code.google.com/p/go/issues/detail?id=3486
-       root, err := filepath.EvalSymlinks(os.Getenv("GOROOT"))
+       root, err := filepath.EvalSymlinks(runtime.GOROOT())
        if err != nil {
                t.Fatal(err)
        }
index 69096494e2a119257b482676fb7bb0c06086d1ef..926b57355a0936d982351fd9b976e074b745b3b7 100644 (file)
@@ -64,6 +64,7 @@ var cleantests = []PathTest{
 }
 
 func TestClean(t *testing.T) {
+       defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
        for _, test := range cleantests {
                if s := Clean(test.path); s != test.result {
                        t.Errorf("Clean(%q) = %q, want %q", test.path, s, test.result)
index dc2b5b4e0efcc2e2b870071ef868c609a2cbffb8..8a3602347fd9402678052326d235b77962e7494a 100644 (file)
@@ -2020,6 +2020,7 @@ func TestAddr(t *testing.T) {
 /* gccgo does do allocations here.
 
 func noAlloc(t *testing.T, n int, f func(int)) {
+       defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
        // once to prime everything
        f(-1)
        memstats := new(runtime.MemStats)
@@ -2029,12 +2030,9 @@ func noAlloc(t *testing.T, n int, f func(int)) {
        for j := 0; j < n; j++ {
                f(j)
        }
-       // A few allocs may happen in the testing package when GOMAXPROCS > 1, so don't
-       // require zero mallocs.
-       // A new thread, one of which will be created if GOMAXPROCS>1, does 6 allocations.
        runtime.ReadMemStats(memstats)
        mallocs := memstats.Mallocs - oldmallocs
-       if mallocs > 10 {
+       if mallocs > 0 {
                t.Fatalf("%d mallocs after %d iterations", mallocs, n)
        }
 }
index fb0606d58b80a596224e50928490855df98f2fa9..9e65870990f6bc8e9fa3eb721a2430fffb9dea04 100644 (file)
@@ -1334,7 +1334,7 @@ func cachePut(k cacheKey, t *rtype) Type {
        return t
 }
 
-// ChanOf returns the channel type with the given direction and and element type.
+// ChanOf returns the channel type with the given direction and element type.
 // For example, if t represents int, ChanOf(RecvDir, t) represents <-chan int.
 //
 // The gc runtime imposes a limit of 64 kB on channel element types.
index 39a28dfc322cf76907892e2bd35889abb60826de..b7e4f044a57dc2fc2a04a0ad41008336c02e647a 100644 (file)
@@ -5,6 +5,7 @@
 package regexp_test
 
 import (
+       "reflect"
        . "regexp"
        "strings"
        "testing"
@@ -417,6 +418,59 @@ func TestSubexp(t *testing.T) {
        }
 }
 
+var splitTests = []struct {
+       s   string
+       r   string
+       n   int
+       out []string
+}{
+       {"foo:and:bar", ":", -1, []string{"foo", "and", "bar"}},
+       {"foo:and:bar", ":", 1, []string{"foo:and:bar"}},
+       {"foo:and:bar", ":", 2, []string{"foo", "and:bar"}},
+       {"foo:and:bar", "foo", -1, []string{"", ":and:bar"}},
+       {"foo:and:bar", "bar", -1, []string{"foo:and:", ""}},
+       {"foo:and:bar", "baz", -1, []string{"foo:and:bar"}},
+       {"baabaab", "a", -1, []string{"b", "", "b", "", "b"}},
+       {"baabaab", "a*", -1, []string{"b", "b", "b"}},
+       {"baabaab", "ba*", -1, []string{"", "", "", ""}},
+       {"foobar", "f*b*", -1, []string{"", "o", "o", "a", "r"}},
+       {"foobar", "f+.*b+", -1, []string{"", "ar"}},
+       {"foobooboar", "o{2}", -1, []string{"f", "b", "boar"}},
+       {"a,b,c,d,e,f", ",", 3, []string{"a", "b", "c,d,e,f"}},
+       {"a,b,c,d,e,f", ",", 0, nil},
+       {",", ",", -1, []string{"", ""}},
+       {",,,", ",", -1, []string{"", "", "", ""}},
+       {"", ",", -1, []string{""}},
+       {"", ".*", -1, []string{""}},
+       {"", ".+", -1, []string{""}},
+       {"", "", -1, []string{}},
+       {"foobar", "", -1, []string{"f", "o", "o", "b", "a", "r"}},
+       {"abaabaccadaaae", "a*", 5, []string{"", "b", "b", "c", "cadaaae"}},
+       {":x:y:z:", ":", -1, []string{"", "x", "y", "z", ""}},
+}
+
+func TestSplit(t *testing.T) {
+       for i, test := range splitTests {
+               re, err := Compile(test.r)
+               if err != nil {
+                       t.Errorf("#%d: %q: compile error: %s", i, test.r, err.Error())
+                       continue
+               }
+
+               split := re.Split(test.s, test.n)
+               if !reflect.DeepEqual(split, test.out) {
+                       t.Errorf("#%d: %q: got %q; want %q", i, test.r, split, test.out)
+               }
+
+               if QuoteMeta(test.r) == test.r {
+                       strsplit := strings.SplitN(test.s, test.r, test.n)
+                       if !reflect.DeepEqual(split, strsplit) {
+                               t.Errorf("#%d: Split(%q, %q, %d): regexp vs strings mismatch\nregexp=%q\nstrings=%q", i, test.s, test.r, test.n, split, strsplit)
+                       }
+               }
+       }
+}
+
 func BenchmarkLiteral(b *testing.B) {
        x := strings.Repeat("x", 50) + "y"
        b.StopTimer()
index aa92e0b58bc7ab9da192a118b155e9c3d086faf5..b0ad9d3400263efefbd2149f4be76842e76249d4 100644 (file)
@@ -20,3 +20,125 @@ func Example() {
        // false
        // false
 }
+
+func ExampleMatchString() {
+       matched, err := regexp.MatchString("foo.*", "seafood")
+       fmt.Println(matched, err)
+       matched, err = regexp.MatchString("bar.*", "seafood")
+       fmt.Println(matched, err)
+       matched, err = regexp.MatchString("a(b", "seafood")
+       fmt.Println(matched, err)
+       // Output:
+       // true <nil>
+       // false <nil>
+       // false error parsing regexp: missing closing ): `a(b`
+}
+
+func ExampleRegexp_FindString() {
+       re := regexp.MustCompile("fo.?")
+       fmt.Printf("%q\n", re.FindString("seafood"))
+       fmt.Printf("%q\n", re.FindString("meat"))
+       // Output:
+       // "foo"
+       // ""
+}
+
+func ExampleRegexp_FindStringIndex() {
+       re := regexp.MustCompile("ab?")
+       fmt.Println(re.FindStringIndex("tablett"))
+       fmt.Println(re.FindStringIndex("foo") == nil)
+       // Output:
+       // [1 3]
+       // true
+}
+
+func ExampleRegexp_FindStringSubmatch() {
+       re := regexp.MustCompile("a(x*)b(y|z)c")
+       fmt.Printf("%q\n", re.FindStringSubmatch("-axxxbyc-"))
+       fmt.Printf("%q\n", re.FindStringSubmatch("-abzc-"))
+       // Output:
+       // ["axxxbyc" "xxx" "y"]
+       // ["abzc" "" "z"]
+}
+
+func ExampleRegexp_FindAllString() {
+       re := regexp.MustCompile("a.")
+       fmt.Println(re.FindAllString("paranormal", -1))
+       fmt.Println(re.FindAllString("paranormal", 2))
+       fmt.Println(re.FindAllString("graal", -1))
+       fmt.Println(re.FindAllString("none", -1))
+       // Output:
+       // [ar an al]
+       // [ar an]
+       // [aa]
+       // []
+}
+
+func ExampleRegexp_FindAllStringSubmatch() {
+       re := regexp.MustCompile("a(x*)b")
+       fmt.Printf("%q\n", re.FindAllStringSubmatch("-ab-", -1))
+       fmt.Printf("%q\n", re.FindAllStringSubmatch("-axxb-", -1))
+       fmt.Printf("%q\n", re.FindAllStringSubmatch("-ab-axb-", -1))
+       fmt.Printf("%q\n", re.FindAllStringSubmatch("-axxb-ab-", -1))
+       // Output:
+       // [["ab" ""]]
+       // [["axxb" "xx"]]
+       // [["ab" ""] ["axb" "x"]]
+       // [["axxb" "xx"] ["ab" ""]]
+}
+
+func ExampleRegexp_FindAllStringSubmatchIndex() {
+       re := regexp.MustCompile("a(x*)b")
+       // Indices:
+       //    01234567   012345678
+       //    -ab-axb-   -axxb-ab-
+       fmt.Println(re.FindAllStringSubmatchIndex("-ab-", -1))
+       fmt.Println(re.FindAllStringSubmatchIndex("-axxb-", -1))
+       fmt.Println(re.FindAllStringSubmatchIndex("-ab-axb-", -1))
+       fmt.Println(re.FindAllStringSubmatchIndex("-axxb-ab-", -1))
+       fmt.Println(re.FindAllStringSubmatchIndex("-foo-", -1))
+       // Output:
+       // [[1 3 2 2]]
+       // [[1 5 2 4]]
+       // [[1 3 2 2] [4 7 5 6]]
+       // [[1 5 2 4] [6 8 7 7]]
+       // []
+}
+
+func ExampleRegexp_ReplaceAllLiteralString() {
+       re := regexp.MustCompile("a(x*)b")
+       fmt.Println(re.ReplaceAllLiteralString("-ab-axxb-", "T"))
+       fmt.Println(re.ReplaceAllLiteralString("-ab-axxb-", "$1"))
+       fmt.Println(re.ReplaceAllLiteralString("-ab-axxb-", "${1}"))
+       // Output:
+       // -T-T-
+       // -$1-$1-
+       // -${1}-${1}-
+}
+
+func ExampleRegexp_ReplaceAllString() {
+       re := regexp.MustCompile("a(x*)b")
+       fmt.Println(re.ReplaceAllString("-ab-axxb-", "T"))
+       fmt.Println(re.ReplaceAllString("-ab-axxb-", "$1"))
+       fmt.Println(re.ReplaceAllString("-ab-axxb-", "$1W"))
+       fmt.Println(re.ReplaceAllString("-ab-axxb-", "${1}W"))
+       // Output:
+       // -T-T-
+       // --xx-
+       // ---
+       // -W-xxW-
+}
+
+func ExampleRegexp_SubexpNames() {
+       re := regexp.MustCompile("(?P<first>[a-zA-Z]+) (?P<last>[a-zA-Z]+)")
+       fmt.Println(re.MatchString("Alan Turing"))
+       fmt.Printf("%q\n", re.SubexpNames())
+       reversed := fmt.Sprintf("${%s} ${%s}", re.SubexpNames()[2], re.SubexpNames()[1])
+       fmt.Println(reversed)
+       fmt.Println(re.ReplaceAllString("Alan Turing", reversed))
+       // Output:
+       // true
+       // ["" "first" "last"]
+       // ${last} ${first}
+       // Turing Alan
+}
index 2a1ae56bd829b1d5dd65b82b60b6f73cb02cc41c..bcf354b44d6b818288aab159eaa81e212a88bb44 100644 (file)
@@ -1048,3 +1048,52 @@ func (re *Regexp) FindAllStringSubmatchIndex(s string, n int) [][]int {
        }
        return result
 }
+
+// Split slices s into substrings separated by the expression and returns a slice of
+// the substrings between those expression matches.
+//
+// The slice returned by this method consists of all the substrings of s
+// not contained in the slice returned by FindAllString. When called on an expression
+// that contains no metacharacters, it is equivalent to strings.SplitN.
+//
+// Example:
+//   s := regexp.MustCompile("a*").Split("abaabaccadaaae", 5)
+//   // s: ["", "b", "b", "c", "cadaaae"]
+//
+// The count determines the number of substrings to return:
+//   n > 0: at most n substrings; the last substring will be the unsplit remainder.
+//   n == 0: the result is nil (zero substrings)
+//   n < 0: all substrings
+func (re *Regexp) Split(s string, n int) []string {
+
+       if n == 0 {
+               return nil
+       }
+
+       if len(re.expr) > 0 && len(s) == 0 {
+               return []string{""}
+       }
+
+       matches := re.FindAllStringIndex(s, n)
+       strings := make([]string, 0, len(matches))
+
+       beg := 0
+       end := 0
+       for _, match := range matches {
+               if n > 0 && len(strings) >= n-1 {
+                       break
+               }
+
+               end = match[0]
+               if match[1] != 0 {
+                       strings = append(strings, s[beg:end])
+               }
+               beg = match[1]
+       }
+
+       if end != len(s) {
+               strings = append(strings, s[beg:])
+       }
+
+       return strings
+}
index 9da71a7857c952fb1888fc5b7dc4d1c192b09ede..bcdde4b6a0b1869a7c492bf24777c9adef5f2300 100644 (file)
@@ -125,6 +125,7 @@ func GoroutineProfile(p []StackRecord) (n int, ok bool)
 // blocking until data is available.  If profiling is turned off and all the profile
 // data accumulated while it was on has been returned, CPUProfile returns nil.
 // The caller must save the returned data before calling CPUProfile again.
+//
 // Most clients should use the runtime/pprof package or
 // the testing package's -test.cpuprofile flag instead of calling
 // CPUProfile directly.
@@ -133,6 +134,7 @@ func CPUProfile() []byte
 // SetCPUProfileRate sets the CPU profiling rate to hz samples per second.
 // If hz <= 0, SetCPUProfileRate turns off profiling.
 // If the profiler is on, the rate cannot be changed without first turning it off.
+//
 // Most clients should use the runtime/pprof package or
 // the testing package's -test.cpuprofile flag instead of calling
 // SetCPUProfileRate directly.
index 09d1391d25490f1816709886745e37355e0fea89..2a90113a3a9b8ec7b89af5b64e40f6441e02611d 100644 (file)
@@ -59,11 +59,7 @@ func (f *Func) FileLine(pc uintptr) (file string, line int) {
 // implemented in symtab.c
 func funcline_go(*Func, uintptr) (string, int)
 
-// A gccgo specific hook to use debug info to get file/line info.
-func RegisterDebugLookup(func(pc uintptr, function *string, file *string, line *int) bool,
-       func(sym string, val *uintptr) bool)
-
-// mid returns the current os thread (m) id.
+// mid returns the current OS thread (m) id.
 func mid() uint32
 
 // SetFinalizer sets the finalizer associated with x to f.
index 56dd93819e179f1e4a8c65140cfd6fd0203c2d82..283a6812e95f3d270920ff526cfffe45d50ecdd1 100644 (file)
@@ -10,6 +10,7 @@ import (
 )
 
 func TestGcSys(t *testing.T) {
+       defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
        memstats := new(runtime.MemStats)
        runtime.GC()
        runtime.ReadMemStats(memstats)
index 41c104c0ba798331655702883ea8748b417395ff..bc33e3a6b4b643ebd8dc9fbbeddd17774d4dd869 100644 (file)
@@ -39,6 +39,7 @@ func OkAmount(size, n uintptr) bool {
 }
 
 func AllocAndFree(size, count int) {
+       defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
        if *chatty {
                fmt.Printf("size=%d count=%d ...\n", size, count)
        }
index 5a3beae61d01aafd7da8a87483d1b49da4bfbd53..b01aff16ca0018ec83c009544a1707a5891351a0 100644 (file)
@@ -48,6 +48,7 @@ var (
 )
 
 func TestCountMallocs(t *testing.T) {
+       defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
        for _, mt := range mallocTest {
                const N = 100
                memstats := new(runtime.MemStats)
index 98325ce75bf659f69e14db9228e03d91dad38914..11240efc0780f7223d1e88a8b9161fb481b9414e 100644 (file)
@@ -124,7 +124,7 @@ func (r *Reader) Seek(offset int64, whence int) (int64, error) {
 func (r *Reader) WriteTo(w io.Writer) (n int64, err error) {
        r.prevRune = -1
        if r.i >= len(r.s) {
-               return 0, io.EOF
+               return 0, nil
        }
        s := r.s[r.i:]
        m, err := io.WriteString(w, s)
index bab91fc71979767871946702c2329de43eff5912..4fdddcdb58e47719271fc1e23ec4c4468d1eb8d9 100644 (file)
@@ -90,7 +90,7 @@ func TestReaderAt(t *testing.T) {
 
 func TestWriteTo(t *testing.T) {
        const str = "0123456789"
-       for i := 0; i < len(str); i++ {
+       for i := 0; i <= len(str); i++ {
                s := str[i:]
                r := strings.NewReader(s)
                var b bytes.Buffer
@@ -99,7 +99,7 @@ func TestWriteTo(t *testing.T) {
                        t.Errorf("got %v; want %v", n, expect)
                }
                if err != nil {
-                       t.Errorf("got error = %v; want nil", err)
+                       t.Errorf("for length %d: got error = %v; want nil", len(s), err)
                }
                if b.String() != s {
                        t.Errorf("got string %q; want %q", b.String(), s)
diff --git a/libgo/go/syscall/dir_plan9.go b/libgo/go/syscall/dir_plan9.go
new file mode 100644 (file)
index 0000000..eee8be4
--- /dev/null
@@ -0,0 +1,205 @@
+// 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.
+
+// Plan 9 directory marshalling. See intro(5).
+
+package syscall
+
+import "errors"
+
+var (
+       ErrShortStat = errors.New("stat buffer too short")
+       ErrBadStat   = errors.New("malformed stat buffer")
+)
+
+// A Qid represents a 9P server's unique identification for a file.
+type Qid struct {
+       Path uint64 // the file server's unique identification for the file
+       Vers uint32 // version number for given Path
+       Type uint8  // the type of the file (syscall.QTDIR for example)
+}
+
+// A Dir contains the metadata for a file.
+type Dir struct {
+       // system-modified data
+       Type uint16 // server type
+       Dev  uint32 // server subtype
+
+       // file data
+       Qid    Qid    // unique id from server
+       Mode   uint32 // permissions
+       Atime  uint32 // last read time
+       Mtime  uint32 // last write time
+       Length int64  // file length
+       Name   string // last element of path
+       Uid    string // owner name
+       Gid    string // group name
+       Muid   string // last modifier name
+}
+
+var nullDir = Dir{
+       Type: ^uint16(0),
+       Dev:  ^uint32(0),
+       Qid: Qid{
+               Path: ^uint64(0),
+               Vers: ^uint32(0),
+               Type: ^uint8(0),
+       },
+       Mode:   ^uint32(0),
+       Atime:  ^uint32(0),
+       Mtime:  ^uint32(0),
+       Length: ^int64(0),
+}
+
+// Null assigns special "don't touch" values to members of d to
+// avoid modifiying them during syscall.Wstat.
+func (d *Dir) Null() { *d = nullDir }
+
+// Marshal encodes a 9P stat message corresponding to d into b
+//
+// If there isn't enough space in b for a stat message, ErrShortStat is returned.
+func (d *Dir) Marshal(b []byte) (n int, err error) {
+       n = STATFIXLEN + len(d.Name) + len(d.Uid) + len(d.Gid) + len(d.Muid)
+       if n > len(b) {
+               return n, ErrShortStat
+       }
+
+       b = pbit16(b, uint16(n)-2)
+       b = pbit16(b, d.Type)
+       b = pbit32(b, d.Dev)
+       b = pbit64(b, d.Qid.Path)
+       b = pbit32(b, d.Qid.Vers)
+       b = pbit8(b, d.Qid.Type)
+       b = pbit32(b, d.Mode)
+       b = pbit32(b, d.Atime)
+       b = pbit32(b, d.Mtime)
+       b = pbit64(b, uint64(d.Length))
+       b = pstring(b, d.Name)
+       b = pstring(b, d.Uid)
+       b = pstring(b, d.Gid)
+       b = pstring(b, d.Muid)
+
+       return n, nil
+}
+
+// UnmarshalDir decodes a single 9P stat message from b and returns the resulting Dir.
+//
+// If b is too small to hold a valid stat message, ErrShortStat is returned.
+//
+// If the stat message itself is invalid, ErrBadStat is returned.
+func UnmarshalDir(b []byte) (*Dir, error) {
+       if len(b) < STATFIXLEN {
+               return nil, ErrShortStat
+       }
+       size, buf := gbit16(b)
+       if len(b) != int(size)+2 {
+               return nil, ErrBadStat
+       }
+       b = buf
+
+       var d Dir
+       d.Type, b = gbit16(b)
+       d.Dev, b = gbit32(b)
+       d.Qid.Path, b = gbit64(b)
+       d.Qid.Vers, b = gbit32(b)
+       d.Qid.Type, b = gbit8(b)
+       d.Mode, b = gbit32(b)
+       d.Atime, b = gbit32(b)
+       d.Mtime, b = gbit32(b)
+
+       n, b := gbit64(b)
+       d.Length = int64(n)
+
+       var ok bool
+       if d.Name, b, ok = gstring(b); !ok {
+               return nil, ErrBadStat
+       }
+       if d.Uid, b, ok = gstring(b); !ok {
+               return nil, ErrBadStat
+       }
+       if d.Gid, b, ok = gstring(b); !ok {
+               return nil, ErrBadStat
+       }
+       if d.Muid, b, ok = gstring(b); !ok {
+               return nil, ErrBadStat
+       }
+
+       return &d, nil
+}
+
+// pbit8 copies the 8-bit number v to b and returns the remaining slice of b.
+func pbit8(b []byte, v uint8) []byte {
+       b[0] = byte(v)
+       return b[1:]
+}
+
+// pbit16 copies the 16-bit number v to b in little-endian order and returns the remaining slice of b.
+func pbit16(b []byte, v uint16) []byte {
+       b[0] = byte(v)
+       b[1] = byte(v >> 8)
+       return b[2:]
+}
+
+// pbit32 copies the 32-bit number v to b in little-endian order and returns the remaining slice of b.
+func pbit32(b []byte, v uint32) []byte {
+       b[0] = byte(v)
+       b[1] = byte(v >> 8)
+       b[2] = byte(v >> 16)
+       b[3] = byte(v >> 24)
+       return b[4:]
+}
+
+// pbit64 copies the 64-bit number v to b in little-endian order and returns the remaining slice of b.
+func pbit64(b []byte, v uint64) []byte {
+       b[0] = byte(v)
+       b[1] = byte(v >> 8)
+       b[2] = byte(v >> 16)
+       b[3] = byte(v >> 24)
+       b[4] = byte(v >> 32)
+       b[5] = byte(v >> 40)
+       b[6] = byte(v >> 48)
+       b[7] = byte(v >> 56)
+       return b[8:]
+}
+
+// pstring copies the string s to b, prepending it with a 16-bit length in little-endian order, and
+// returning the remaining slice of b..
+func pstring(b []byte, s string) []byte {
+       b = pbit16(b, uint16(len(s)))
+       n := copy(b, s)
+       return b[n:]
+}
+
+// gbit8 reads an 8-bit number from b and returns it with the remaining slice of b.
+func gbit8(b []byte) (uint8, []byte) {
+       return uint8(b[0]), b[1:]
+}
+
+// gbit16 reads a 16-bit number in little-endian order from b and returns it with the remaining slice of b.
+func gbit16(b []byte) (uint16, []byte) {
+       return uint16(b[0]) | uint16(b[1])<<8, b[2:]
+}
+
+// gbit32 reads a 32-bit number in little-endian order from b and returns it with the remaining slice of b.
+func gbit32(b []byte) (uint32, []byte) {
+       return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24, b[4:]
+}
+
+// gbit64 reads a 64-bit number in little-endian order from b and returns it with the remaining slice of b.
+func gbit64(b []byte) (uint64, []byte) {
+       lo := uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
+       hi := uint32(b[4]) | uint32(b[5])<<8 | uint32(b[6])<<16 | uint32(b[7])<<24
+       return uint64(lo) | uint64(hi)<<32, b[8:]
+}
+
+// gstring reads a string from b, prefixed with a 16-bit length in little-endian order.
+// It returns the string with the remaining slice of b and a boolean. If the length is
+// greater than the number of bytes in b, the boolean will be false.
+func gstring(b []byte) (string, []byte, bool) {
+       n, b := gbit16(b)
+       if int(n) > len(b) {
+               return "", b, false
+       }
+       return string(b[:n]), b[n:], true
+}
index 944cc789c3167e21480ee31c02c144b017168996..ea26710d8d2bcd9d6a44add7258dfe692973a514 100644 (file)
@@ -56,3 +56,58 @@ func ExampleDate() {
        fmt.Printf("Go launched at %s\n", t.Local())
        // Output: Go launched at 2009-11-10 15:00:00 -0800 PST
 }
+
+func ExampleTime_Round() {
+       t := time.Date(0, 0, 0, 12, 15, 30, 918273645, time.UTC)
+       round := []time.Duration{
+               time.Nanosecond,
+               time.Microsecond,
+               time.Millisecond,
+               time.Second,
+               2 * time.Second,
+               time.Minute,
+               10 * time.Minute,
+               time.Hour,
+       }
+
+       for _, d := range round {
+               fmt.Printf("t.Round(%6s) = %s\n", d, t.Round(d).Format("15:04:05.999999999"))
+       }
+       // Output:
+       // t.Round(   1ns) = 12:15:30.918273645
+       // t.Round(   1us) = 12:15:30.918274
+       // t.Round(   1ms) = 12:15:30.918
+       // t.Round(    1s) = 12:15:31
+       // t.Round(    2s) = 12:15:30
+       // t.Round(  1m0s) = 12:16:00
+       // t.Round( 10m0s) = 12:20:00
+       // t.Round(1h0m0s) = 12:00:00
+}
+
+func ExampleTime_Truncate() {
+       t, _ := time.Parse("2006 Jan 02 15:04:05", "2012 Dec 07 12:15:30.918273645")
+       trunc := []time.Duration{
+               time.Nanosecond,
+               time.Microsecond,
+               time.Millisecond,
+               time.Second,
+               2 * time.Second,
+               time.Minute,
+               10 * time.Minute,
+               time.Hour,
+       }
+
+       for _, d := range trunc {
+               fmt.Printf("t.Truncate(%6s) = %s\n", d, t.Truncate(d).Format("15:04:05.999999999"))
+       }
+
+       // Output:
+       // t.Truncate(   1ns) = 12:15:30.918273645
+       // t.Truncate(   1us) = 12:15:30.918273
+       // t.Truncate(   1ms) = 12:15:30.918
+       // t.Truncate(    1s) = 12:15:30
+       // t.Truncate(    2s) = 12:15:30
+       // t.Truncate(  1m0s) = 12:15:00
+       // t.Truncate( 10m0s) = 12:10:00
+       // t.Truncate(1h0m0s) = 12:00:00
+}
index 011a1e31e31ca40ddcdeb78cf2ca4d4c9e69bc13..190cc37ddbd97397423dba16d5459500d2eed507 100644 (file)
@@ -1033,3 +1033,116 @@ func Date(year int, month Month, day, hour, min, sec, nsec int, loc *Location) T
 
        return Time{unix + unixToInternal, int32(nsec), loc}
 }
+
+// Truncate returns the result of rounding t down to a multiple of d (since the zero time).
+// If d <= 0, Truncate returns t unchanged.
+func (t Time) Truncate(d Duration) Time {
+       if d <= 0 {
+               return t
+       }
+       _, r := div(t, d)
+       return t.Add(-r)
+}
+
+// Round returns the result of rounding t to the nearest multiple of d (since the zero time).
+// The rounding behavior for halfway values is to round up.
+// If d <= 0, Round returns t unchanged.
+func (t Time) Round(d Duration) Time {
+       if d <= 0 {
+               return t
+       }
+       _, r := div(t, d)
+       if r+r < d {
+               return t.Add(-r)
+       }
+       return t.Add(d - r)
+}
+
+// div divides t by d and returns the quotient parity and remainder.
+// We don't use the quotient parity anymore (round half up instead of round to even)
+// but it's still here in case we change our minds.
+func div(t Time, d Duration) (qmod2 int, r Duration) {
+       neg := false
+       if t.sec < 0 {
+               // Operate on absolute value.
+               neg = true
+               t.sec = -t.sec
+               t.nsec = -t.nsec
+               if t.nsec < 0 {
+                       t.nsec += 1e9
+                       t.sec-- // t.sec >= 1 before the -- so safe
+               }
+       }
+
+       switch {
+       // Special case: 2d divides 1 second.
+       case d < Second && Second%(d+d) == 0:
+               qmod2 = int(t.nsec/int32(d)) & 1
+               r = Duration(t.nsec % int32(d))
+
+       // Special case: d is a multiple of 1 second.
+       case d%Second == 0:
+               d1 := int64(d / Second)
+               qmod2 = int(t.sec/d1) & 1
+               r = Duration(t.sec%d1)*Second + Duration(t.nsec)
+
+       // General case.
+       // This could be faster if more cleverness were applied,
+       // but it's really only here to avoid special case restrictions in the API.
+       // No one will care about these cases.
+       default:
+               // Compute nanoseconds as 128-bit number.
+               sec := uint64(t.sec)
+               tmp := (sec >> 32) * 1e9
+               u1 := tmp >> 32
+               u0 := tmp << 32
+               tmp = uint64(sec&0xFFFFFFFF) * 1e9
+               u0x, u0 := u0, u0+tmp
+               if u0 < u0x {
+                       u1++
+               }
+               u0x, u0 = u0, u0+uint64(t.nsec)
+               if u0 < u0x {
+                       u1++
+               }
+
+               // Compute remainder by subtracting r<<k for decreasing k.
+               // Quotient parity is whether we subtract on last round.
+               d1 := uint64(d)
+               for d1>>63 != 1 {
+                       d1 <<= 1
+               }
+               d0 := uint64(0)
+               for {
+                       qmod2 = 0
+                       if u1 > d1 || u1 == d1 && u0 >= d0 {
+                               // subtract
+                               qmod2 = 1
+                               u0x, u0 = u0, u0-d0
+                               if u0 > u0x {
+                                       u1--
+                               }
+                               u1 -= d1
+                       }
+                       if d1 == 0 && d0 == uint64(d) {
+                               break
+                       }
+                       d0 >>= 1
+                       d0 |= (d1 & 1) << 63
+                       d1 >>= 1
+               }
+               r = Duration(u0)
+       }
+
+       if neg && r != 0 {
+               // If input was negative and not an exact multiple of d, we computed q, r such that
+               //      q*d + r = -t
+               // But the right answers are given by -(q-1), d-r:
+               //      q*d + r = -t
+               //      -q*d - r = t
+               //      -(q-1)*d + (d - r) = t
+               qmod2 ^= 1
+               r = d - r
+       }
+       return
+}
index 9888d0d9c190347d2d353dadbf8ad0fef2b87a4a..1fd575b0956e6f857e0de0b420d082784e6cedd7 100644 (file)
@@ -9,7 +9,9 @@ import (
        "encoding/gob"
        "encoding/json"
        "fmt"
+       "math/big"
        "math/rand"
+       "runtime"
        "strconv"
        "strings"
        "testing"
@@ -192,6 +194,184 @@ func TestNanosecondsToUTCAndBack(t *testing.T) {
        }
 }
 
+// The time routines provide no way to get absolute time
+// (seconds since zero), but we need it to compute the right
+// answer for bizarre roundings like "to the nearest 3 ns".
+// Compute as t - year1 = (t - 1970) + (1970 - 2001) + (2001 - 1).
+// t - 1970 is returned by Unix and Nanosecond.
+// 1970 - 2001 is -(31*365+8)*86400 = -978307200 seconds.
+// 2001 - 1 is 2000*365.2425*86400 = 63113904000 seconds.
+const unixToZero = -978307200 + 63113904000
+
+// abs returns the absolute time stored in t, as seconds and nanoseconds.
+func abs(t Time) (sec, nsec int64) {
+       unix := t.Unix()
+       nano := t.Nanosecond()
+       return unix + unixToZero, int64(nano)
+}
+
+// absString returns abs as a decimal string.
+func absString(t Time) string {
+       sec, nsec := abs(t)
+       if sec < 0 {
+               sec = -sec
+               nsec = -nsec
+               if nsec < 0 {
+                       nsec += 1e9
+                       sec--
+               }
+               return fmt.Sprintf("-%d%09d", sec, nsec)
+       }
+       return fmt.Sprintf("%d%09d", sec, nsec)
+}
+
+var truncateRoundTests = []struct {
+       t Time
+       d Duration
+}{
+       {Date(-1, January, 1, 12, 15, 30, 5e8, UTC), 3},
+       {Date(-1, January, 1, 12, 15, 31, 5e8, UTC), 3},
+       {Date(2012, January, 1, 12, 15, 30, 5e8, UTC), Second},
+       {Date(2012, January, 1, 12, 15, 31, 5e8, UTC), Second},
+}
+
+func TestTruncateRound(t *testing.T) {
+       var (
+               bsec  = new(big.Int)
+               bnsec = new(big.Int)
+               bd    = new(big.Int)
+               bt    = new(big.Int)
+               br    = new(big.Int)
+               bq    = new(big.Int)
+               b1e9  = new(big.Int)
+       )
+
+       b1e9.SetInt64(1e9)
+
+       testOne := func(ti, tns, di int64) bool {
+               t0 := Unix(ti, int64(tns)).UTC()
+               d := Duration(di)
+               if d < 0 {
+                       d = -d
+               }
+               if d <= 0 {
+                       d = 1
+               }
+
+               // Compute bt = absolute nanoseconds.
+               sec, nsec := abs(t0)
+               bsec.SetInt64(sec)
+               bnsec.SetInt64(nsec)
+               bt.Mul(bsec, b1e9)
+               bt.Add(bt, bnsec)
+
+               // Compute quotient and remainder mod d.
+               bd.SetInt64(int64(d))
+               bq.DivMod(bt, bd, br)
+
+               // To truncate, subtract remainder.
+               // br is < d, so it fits in an int64.
+               r := br.Int64()
+               t1 := t0.Add(-Duration(r))
+
+               // Check that time.Truncate works.
+               if trunc := t0.Truncate(d); trunc != t1 {
+                       t.Errorf("Time.Truncate(%s, %s) = %s, want %s\n"+
+                               "%v trunc %v =\n%v want\n%v",
+                               t0.Format(RFC3339Nano), d, trunc, t1.Format(RFC3339Nano),
+                               absString(t0), int64(d), absString(trunc), absString(t1))
+                       return false
+               }
+
+               // To round, add d back if remainder r > d/2 or r == exactly d/2.
+               // The commented out code would round half to even instead of up,
+               // but that makes it time-zone dependent, which is a bit strange.
+               if r > int64(d)/2 || r+r == int64(d) /*&& bq.Bit(0) == 1*/ {
+                       t1 = t1.Add(Duration(d))
+               }
+
+               // Check that time.Round works.
+               if rnd := t0.Round(d); rnd != t1 {
+                       t.Errorf("Time.Round(%s, %s) = %s, want %s\n"+
+                               "%v round %v =\n%v want\n%v",
+                               t0.Format(RFC3339Nano), d, rnd, t1.Format(RFC3339Nano),
+                               absString(t0), int64(d), absString(rnd), absString(t1))
+                       return false
+               }
+               return true
+       }
+
+       // manual test cases
+       for _, tt := range truncateRoundTests {
+               testOne(tt.t.Unix(), int64(tt.t.Nanosecond()), int64(tt.d))
+       }
+
+       // exhaustive near 0
+       for i := 0; i < 100; i++ {
+               for j := 1; j < 100; j++ {
+                       testOne(unixToZero, int64(i), int64(j))
+                       testOne(unixToZero, -int64(i), int64(j))
+                       if t.Failed() {
+                               return
+                       }
+               }
+       }
+
+       if t.Failed() {
+               return
+       }
+
+       // randomly generated test cases
+       cfg := &quick.Config{MaxCount: 100000}
+       if testing.Short() {
+               cfg.MaxCount = 1000
+       }
+
+       // divisors of Second
+       f1 := func(ti int64, tns int32, logdi int32) bool {
+               d := Duration(1)
+               a, b := uint(logdi%9), (logdi>>16)%9
+               d <<= a
+               for i := 0; i < int(b); i++ {
+                       d *= 5
+               }
+               return testOne(ti, int64(tns), int64(d))
+       }
+       quick.Check(f1, cfg)
+
+       // multiples of Second
+       f2 := func(ti int64, tns int32, di int32) bool {
+               d := Duration(di) * Second
+               if d < 0 {
+                       d = -d
+               }
+               return testOne(ti, int64(tns), int64(d))
+       }
+       quick.Check(f2, cfg)
+
+       // halfway cases
+       f3 := func(tns, di int64) bool {
+               di &= 0xfffffffe
+               if di == 0 {
+                       di = 2
+               }
+               tns -= tns % di
+               if tns < 0 {
+                       tns += di / 2
+               } else {
+                       tns -= di / 2
+               }
+               return testOne(0, tns, di)
+       }
+       quick.Check(f3, cfg)
+
+       // full generality
+       f4 := func(ti int64, tns int32, di int64) bool {
+               return testOne(ti, int64(tns), di)
+       }
+       quick.Check(f4, cfg)
+}
+
 type TimeFormatTest struct {
        time           Time
        formattedValue string
@@ -1037,9 +1217,47 @@ func TestParseDurationRoundTrip(t *testing.T) {
        }
 }
 
+var (
+       t Time
+       u int64
+)
+
+var mallocTest = []struct {
+       count int
+       desc  string
+       fn    func()
+}{
+       {0, `time.Now()`, func() { t = Now() }},
+       {0, `time.Now().UnixNano()`, func() { u = Now().UnixNano() }},
+}
+
+func TestCountMallocs(t *testing.T) {
+       defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
+       for _, mt := range mallocTest {
+               const N = 100
+               memstats := new(runtime.MemStats)
+               runtime.ReadMemStats(memstats)
+               mallocs := 0 - memstats.Mallocs
+               for i := 0; i < N; i++ {
+                       mt.fn()
+               }
+               runtime.ReadMemStats(memstats)
+               mallocs += memstats.Mallocs
+               if mallocs/N > uint64(mt.count) {
+                       t.Errorf("%s: expected %d mallocs, got %d", mt.desc, mt.count, mallocs/N)
+               }
+       }
+}
+
 func BenchmarkNow(b *testing.B) {
        for i := 0; i < b.N; i++ {
-               Now()
+               t = Now()
+       }
+}
+
+func BenchmarkNowUnixNano(b *testing.B) {
+       for i := 0; i < b.N; i++ {
+               u = Now().UnixNano()
        }
 }
 
index de25e9fd834b4004374dbe68e7a7037ff215f854..ceee42c5d6f5a27e0a5260e61d4051bba0e8d618 100644 (file)
@@ -197,7 +197,7 @@ runtime_chansend(ChanType *t, Hchan *c, byte *ep, bool *pres, void *pc)
        runtime_lock(c);
        // TODO(dvyukov): add similar instrumentation to select.
        if(raceenabled)
-               runtime_racereadpc(c, pc);
+               runtime_racereadpc(c, pc, runtime_chansend);
        if(c->closed)
                goto closed;
 
@@ -1271,7 +1271,7 @@ runtime_closechan(Hchan *c)
        }
 
        if(raceenabled) {
-               runtime_racewritepc(c, runtime_getcallerpc(&c));
+               runtime_racewritepc(c, runtime_getcallerpc(&c), runtime_closechan);
                runtime_racerelease(c);
        }
 
index 5ea456f3cf134211318e3060dabd5013bfc61876..45f8a56ca7579e6aae0b36cc0acdb112fffd211a 100644 (file)
@@ -949,6 +949,7 @@ runtime_memorydump(void)
                dumpspan(spanidx);
        }
 }
+
 void
 runtime_gchelper(void)
 {
@@ -1025,16 +1026,21 @@ cachestats(GCStats *stats)
        mstats.stacks_sys = stacks_sys;
 }
 
+// Structure of arguments passed to function gc().
+// This allows the arguments to be passed via reflect_call.
+struct gc_args
+{
+       int32 force;
+};
+
+static void gc(struct gc_args *args);
+
 void
 runtime_gc(int32 force)
 {
        M *m;
-       int64 t0, t1, t2, t3;
-       uint64 heap0, heap1, obj0, obj1;
        const byte *p;
-       GCStats stats;
-       M *m1;
-       uint32 i;
+       struct gc_args a, *ap;
 
        // The atomic operations are not atomic if the uint64s
        // are not aligned on uint64 boundaries. This has been
@@ -1074,12 +1080,37 @@ runtime_gc(int32 force)
        if(gcpercent < 0)
                return;
 
+       // Run gc on a bigger stack to eliminate
+       // a potentially large number of calls to runtime_morestack.
+       // But not when using gccgo.
+       a.force = force;
+       ap = &a;
+       gc(ap);
+
+       if(gctrace > 1 && !force) {
+               a.force = 1;
+               gc(&a);
+       }
+}
+
+static void
+gc(struct gc_args *args)
+{
+       M *m;
+       int64 t0, t1, t2, t3;
+       uint64 heap0, heap1, obj0, obj1;
+       GCStats stats;
+       M *m1;
+       uint32 i;
+
        runtime_semacquire(&runtime_worldsema);
-       if(!force && mstats.heap_alloc < mstats.next_gc) {
+       if(!args->force && mstats.heap_alloc < mstats.next_gc) {
                runtime_semrelease(&runtime_worldsema);
                return;
        }
 
+       m = runtime_m();
+
        t0 = runtime_nanotime();
 
        m->gcing = 1;
@@ -1181,9 +1212,6 @@ runtime_gc(int32 force)
        // give the queued finalizers, if any, a chance to run
        if(finq != nil)
                runtime_gosched();
-
-       if(gctrace > 1 && !force)
-               runtime_gc(1);
 }
 
 void runtime_ReadMemStats(MStats *)
index 2d8d095eaafa5eba09d30f4c057bb35af5ec8da0..9f3b3ec66088c383d71708685d0d59970191e0e8 100644 (file)
@@ -20,8 +20,8 @@ void  runtime_racemalloc(void *p, uintptr sz, void *pc);
 void   runtime_racefree(void *p);
 void   runtime_racegostart(int32 goid, void *pc);
 void   runtime_racegoend(int32 goid);
-void   runtime_racewritepc(void *addr, void *pc);
-void   runtime_racereadpc(void *addr, void *pc);
+void   runtime_racewritepc(void *addr, void *callpc, void *pc);
+void   runtime_racereadpc(void *addr, void *callpc, void *pc);
 void   runtime_racefingo(void);
 void   runtime_raceacquire(void *addr);
 void   runtime_raceacquireg(G *gp, void *addr);