]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
libgo: Update to Go 1.3 release.
authorIan Lance Taylor <ian@gcc.gnu.org>
Sat, 19 Jul 2014 08:53:52 +0000 (08:53 +0000)
committerIan Lance Taylor <ian@gcc.gnu.org>
Sat, 19 Jul 2014 08:53:52 +0000 (08:53 +0000)
From-SVN: r212837

456 files changed:
libgo/MERGE
libgo/Makefile.am
libgo/Makefile.in
libgo/go/archive/tar/common.go
libgo/go/archive/tar/reader.go
libgo/go/archive/tar/reader_test.go
libgo/go/archive/tar/testdata/sparse-formats.tar [new file with mode: 0644]
libgo/go/archive/tar/testdata/writer-big-long.tar [new file with mode: 0644]
libgo/go/archive/tar/writer.go
libgo/go/archive/tar/writer_test.go
libgo/go/archive/zip/struct.go
libgo/go/bufio/bufio.go
libgo/go/bufio/bufio_test.go
libgo/go/bufio/scan.go
libgo/go/bufio/scan_test.go
libgo/go/bytes/bytes.go
libgo/go/bytes/bytes_test.go
libgo/go/bytes/reader.go
libgo/go/bytes/reader_test.go
libgo/go/compress/bzip2/bzip2_test.go
libgo/go/compress/flate/inflate.go
libgo/go/compress/flate/reader_test.go
libgo/go/compress/gzip/gunzip.go
libgo/go/compress/gzip/gunzip_test.go
libgo/go/compress/gzip/gzip.go
libgo/go/compress/lzw/reader.go
libgo/go/compress/lzw/writer.go
libgo/go/compress/zlib/reader.go
libgo/go/compress/zlib/writer.go
libgo/go/compress/zlib/writer_test.go
libgo/go/container/heap/heap.go
libgo/go/crypto/aes/aes_test.go
libgo/go/crypto/aes/cipher.go
libgo/go/crypto/aes/cipher_asm.go
libgo/go/crypto/cipher/benchmark_test.go
libgo/go/crypto/cipher/gcm.go
libgo/go/crypto/dsa/dsa.go
libgo/go/crypto/ecdsa/ecdsa.go
libgo/go/crypto/md5/md5block_decl.go
libgo/go/crypto/md5/md5block_generic.go
libgo/go/crypto/rand/rand_unix.go
libgo/go/crypto/rc4/rc4_asm.go
libgo/go/crypto/rc4/rc4_ref.go
libgo/go/crypto/rsa/pkcs1v15.go
libgo/go/crypto/rsa/pss.go
libgo/go/crypto/rsa/rsa.go
libgo/go/crypto/sha1/sha1block_decl.go
libgo/go/crypto/sha1/sha1block_generic.go
libgo/go/crypto/tls/common.go
libgo/go/crypto/tls/conn.go
libgo/go/crypto/tls/generate_cert.go
libgo/go/crypto/tls/handshake_client.go
libgo/go/crypto/tls/handshake_messages.go
libgo/go/crypto/tls/handshake_messages_test.go
libgo/go/crypto/tls/handshake_server.go
libgo/go/crypto/tls/handshake_server_test.go
libgo/go/crypto/tls/key_agreement.go
libgo/go/crypto/tls/testdata/Client-TLSv10-ClientCert-ECDSA-ECDSA
libgo/go/crypto/tls/testdata/Client-TLSv10-ClientCert-ECDSA-RSA
libgo/go/crypto/tls/testdata/Client-TLSv10-ClientCert-RSA-ECDSA
libgo/go/crypto/tls/testdata/Client-TLSv10-ClientCert-RSA-RSA
libgo/go/crypto/tls/testdata/Client-TLSv10-ECDHE-ECDSA-AES
libgo/go/crypto/tls/testdata/Client-TLSv10-ECDHE-RSA-AES
libgo/go/crypto/tls/testdata/Client-TLSv10-RSA-RC4
libgo/go/crypto/tls/testdata/Client-TLSv11-ECDHE-ECDSA-AES
libgo/go/crypto/tls/testdata/Client-TLSv11-ECDHE-RSA-AES
libgo/go/crypto/tls/testdata/Client-TLSv11-RSA-RC4
libgo/go/crypto/tls/testdata/Client-TLSv12-ClientCert-ECDSA-ECDSA
libgo/go/crypto/tls/testdata/Client-TLSv12-ClientCert-ECDSA-RSA
libgo/go/crypto/tls/testdata/Client-TLSv12-ClientCert-RSA-ECDSA
libgo/go/crypto/tls/testdata/Client-TLSv12-ClientCert-RSA-RSA
libgo/go/crypto/tls/testdata/Client-TLSv12-ECDHE-ECDSA-AES
libgo/go/crypto/tls/testdata/Client-TLSv12-ECDHE-ECDSA-AES-GCM
libgo/go/crypto/tls/testdata/Client-TLSv12-ECDHE-RSA-AES
libgo/go/crypto/tls/testdata/Client-TLSv12-RSA-RC4
libgo/go/crypto/tls/testdata/Server-TLSv10-ECDHE-ECDSA-AES
libgo/go/crypto/tls/testdata/Server-TLSv12-CipherSuiteCertPreferenceECDSA
libgo/go/crypto/tls/testdata/Server-TLSv12-CipherSuiteCertPreferenceRSA
libgo/go/crypto/tls/testdata/Server-TLSv12-ECDHE-ECDSA-AES
libgo/go/crypto/tls/testdata/Server-TLSv12-RSA-AES-GCM
libgo/go/crypto/tls/tls.go
libgo/go/crypto/tls/tls_test.go
libgo/go/crypto/x509/root_unix.go
libgo/go/crypto/x509/verify_test.go
libgo/go/crypto/x509/x509.go
libgo/go/crypto/x509/x509_test.go
libgo/go/database/sql/driver/driver.go
libgo/go/database/sql/sql.go
libgo/go/database/sql/sql_test.go
libgo/go/debug/dwarf/const.go
libgo/go/debug/dwarf/entry.go
libgo/go/debug/elf/elf.go
libgo/go/debug/elf/file.go
libgo/go/debug/elf/file_test.go
libgo/go/debug/elf/testdata/go-relocation-test-clang-x86.obj [new file with mode: 0644]
libgo/go/debug/goobj/read.go [deleted file]
libgo/go/debug/goobj/read_test.go [deleted file]
libgo/go/debug/macho/file.go
libgo/go/debug/macho/file_test.go
libgo/go/debug/macho/macho.go
libgo/go/debug/pe/file.go
libgo/go/debug/pe/file_test.go
libgo/go/debug/pe/pe.go
libgo/go/debug/pe/testdata/gcc-amd64-mingw-exec [new file with mode: 0644]
libgo/go/debug/pe/testdata/gcc-amd64-mingw-obj [new file with mode: 0644]
libgo/go/debug/plan9obj/file.go
libgo/go/debug/plan9obj/file_test.go
libgo/go/debug/plan9obj/plan9obj.go
libgo/go/encoding/ascii85/ascii85.go
libgo/go/encoding/ascii85/ascii85_test.go
libgo/go/encoding/asn1/asn1.go
libgo/go/encoding/asn1/marshal.go
libgo/go/encoding/asn1/marshal_test.go
libgo/go/encoding/base32/base32.go
libgo/go/encoding/base64/base64.go
libgo/go/encoding/base64/base64_test.go
libgo/go/encoding/binary/binary.go
libgo/go/encoding/binary/binary_test.go
libgo/go/encoding/gob/decode.go
libgo/go/encoding/gob/decoder.go
libgo/go/encoding/gob/encode.go
libgo/go/encoding/gob/gobencdec_test.go
libgo/go/encoding/hex/hex.go
libgo/go/encoding/json/decode.go
libgo/go/encoding/json/indent.go
libgo/go/encoding/json/stream.go
libgo/go/encoding/xml/marshal.go
libgo/go/encoding/xml/marshal_test.go
libgo/go/encoding/xml/read.go
libgo/go/encoding/xml/read_test.go
libgo/go/encoding/xml/typeinfo.go
libgo/go/encoding/xml/xml.go
libgo/go/expvar/expvar.go
libgo/go/expvar/expvar_test.go
libgo/go/flag/flag.go
libgo/go/fmt/doc.go
libgo/go/fmt/fmt_test.go
libgo/go/fmt/format.go
libgo/go/fmt/print.go
libgo/go/go/ast/commentmap.go
libgo/go/go/build/build.go
libgo/go/go/build/deps_test.go
libgo/go/go/build/doc.go
libgo/go/go/build/syslist.go
libgo/go/go/doc/comment.go
libgo/go/go/doc/comment_test.go
libgo/go/go/doc/example.go
libgo/go/go/parser/error_test.go
libgo/go/go/parser/interface.go
libgo/go/go/parser/parser.go
libgo/go/go/parser/parser_test.go
libgo/go/go/parser/short_test.go
libgo/go/go/printer/nodes.go
libgo/go/go/printer/printer.go
libgo/go/go/printer/printer_test.go
libgo/go/go/printer/testdata/comments.golden
libgo/go/go/printer/testdata/comments.input
libgo/go/go/printer/testdata/comments2.golden
libgo/go/go/printer/testdata/comments2.input
libgo/go/go/printer/testdata/declarations.golden
libgo/go/go/printer/testdata/declarations.input
libgo/go/go/scanner/scanner.go
libgo/go/go/scanner/scanner_test.go
libgo/go/hash/crc32/crc32_amd64x.go [moved from libgo/go/hash/crc32/crc32_amd64.go with 96% similarity]
libgo/go/html/template/attr.go
libgo/go/html/template/content.go
libgo/go/html/template/context.go
libgo/go/html/template/escape.go
libgo/go/html/template/escape_test.go
libgo/go/html/template/html.go
libgo/go/html/template/js.go
libgo/go/html/template/template.go
libgo/go/image/jpeg/huffman.go
libgo/go/image/jpeg/reader_test.go
libgo/go/image/jpeg/scan.go
libgo/go/image/png/reader.go
libgo/go/image/testdata/video-001.separate.dc.progression.jpeg [new file with mode: 0644]
libgo/go/image/testdata/video-001.separate.dc.progression.progressive.jpeg [new file with mode: 0644]
libgo/go/image/testdata/video-005.gray.gif [new file with mode: 0644]
libgo/go/io/io.go
libgo/go/io/multi.go
libgo/go/io/multi_test.go
libgo/go/log/syslog/syslog.go
libgo/go/log/syslog/syslog_test.go
libgo/go/log/syslog/syslog_unix.go
libgo/go/math/big/int.go
libgo/go/math/big/int_test.go
libgo/go/math/big/nat.go
libgo/go/math/big/nat_test.go
libgo/go/math/big/rat.go
libgo/go/math/cmplx/cmath_test.go
libgo/go/math/cmplx/pow.go
libgo/go/math/cmplx/sqrt.go
libgo/go/math/rand/rand.go
libgo/go/math/rand/rand_test.go
libgo/go/math/rand/regress_test.go [new file with mode: 0644]
libgo/go/mime/mediatype.go
libgo/go/mime/mediatype_test.go
libgo/go/mime/multipart/multipart.go
libgo/go/mime/multipart/quotedprintable_test.go
libgo/go/mime/type_unix.go
libgo/go/net/cgo_bsd.go
libgo/go/net/conn_test.go
libgo/go/net/dial.go
libgo/go/net/dial_test.go
libgo/go/net/dnsclient.go
libgo/go/net/dnsclient_test.go [new file with mode: 0644]
libgo/go/net/dnsclient_unix.go
libgo/go/net/dnsclient_unix_test.go
libgo/go/net/dnsconfig_unix.go
libgo/go/net/dnsconfig_unix_test.go
libgo/go/net/fd_mutex_test.go
libgo/go/net/fd_plan9.go
libgo/go/net/fd_poll_nacl.go [new file with mode: 0644]
libgo/go/net/fd_poll_runtime.go
libgo/go/net/fd_unix.go
libgo/go/net/fd_unix_test.go
libgo/go/net/fd_windows.go
libgo/go/net/file_test.go
libgo/go/net/file_unix.go
libgo/go/net/http/cgi/host.go
libgo/go/net/http/cgi/matryoshka_test.go
libgo/go/net/http/client.go
libgo/go/net/http/client_test.go
libgo/go/net/http/cookie.go
libgo/go/net/http/cookie_test.go
libgo/go/net/http/export_test.go
libgo/go/net/http/fcgi/child.go
libgo/go/net/http/fs.go
libgo/go/net/http/header.go
libgo/go/net/http/header_test.go
libgo/go/net/http/httptest/server_test.go
libgo/go/net/http/httputil/dump.go
libgo/go/net/http/httputil/dump_test.go
libgo/go/net/http/httputil/persist.go
libgo/go/net/http/proxy_test.go
libgo/go/net/http/race.go [new file with mode: 0644]
libgo/go/net/http/request.go
libgo/go/net/http/request_test.go
libgo/go/net/http/requestwrite_test.go
libgo/go/net/http/response.go
libgo/go/net/http/response_test.go
libgo/go/net/http/responsewrite_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/interface.go
libgo/go/net/interface_stub.go
libgo/go/net/ipraw_test.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/lookup_plan9.go
libgo/go/net/lookup_unix.go
libgo/go/net/mail/message.go
libgo/go/net/mail/message_test.go
libgo/go/net/multicast_test.go
libgo/go/net/net.go
libgo/go/net/net_test.go
libgo/go/net/packetconn_test.go
libgo/go/net/port_unix.go
libgo/go/net/protoconn_test.go
libgo/go/net/rpc/client.go
libgo/go/net/rpc/client_test.go [new file with mode: 0644]
libgo/go/net/rpc/jsonrpc/all_test.go
libgo/go/net/rpc/jsonrpc/server.go
libgo/go/net/rpc/server_test.go
libgo/go/net/sendfile_dragonfly.go
libgo/go/net/sendfile_freebsd.go
libgo/go/net/sendfile_stub.go
libgo/go/net/server_test.go
libgo/go/net/smtp/smtp.go
libgo/go/net/smtp/smtp_test.go
libgo/go/net/sock_bsd.go
libgo/go/net/sock_cloexec.go
libgo/go/net/sock_posix.go
libgo/go/net/sock_solaris.go
libgo/go/net/sockopt_bsd.go
libgo/go/net/sockopt_posix.go
libgo/go/net/sockopt_solaris.go [new file with mode: 0644]
libgo/go/net/sockoptip_bsd.go
libgo/go/net/sockoptip_posix.go
libgo/go/net/sockoptip_stub.go [new file with mode: 0644]
libgo/go/net/sys_cloexec.go
libgo/go/net/tcp_test.go
libgo/go/net/tcpsock_plan9.go
libgo/go/net/tcpsock_posix.go
libgo/go/net/tcpsockopt_dragonfly.go [new file with mode: 0644]
libgo/go/net/tcpsockopt_posix.go
libgo/go/net/tcpsockopt_solaris.go [new file with mode: 0644]
libgo/go/net/tcpsockopt_unix.go
libgo/go/net/textproto/reader.go
libgo/go/net/textproto/reader_test.go
libgo/go/net/timeout_test.go
libgo/go/net/udp_test.go
libgo/go/net/udpsock.go
libgo/go/net/udpsock_posix.go
libgo/go/net/unix_test.go
libgo/go/net/unixsock_posix.go
libgo/go/net/url/url.go
libgo/go/net/url/url_test.go
libgo/go/os/dir_unix.go
libgo/go/os/doc.go
libgo/go/os/error_unix.go
libgo/go/os/exec/exec.go
libgo/go/os/exec/exec_test.go
libgo/go/os/exec/lp_unix.go
libgo/go/os/exec_plan9.go
libgo/go/os/exec_posix.go
libgo/go/os/exec_unix.go
libgo/go/os/file_posix.go
libgo/go/os/file_unix.go
libgo/go/os/getwd.go
libgo/go/os/os_test.go
libgo/go/os/path_test.go
libgo/go/os/path_unix.go
libgo/go/os/pipe_bsd.go
libgo/go/os/signal/signal_unix.go
libgo/go/os/stat_nacl.go [new file with mode: 0644]
libgo/go/os/sys_bsd.go
libgo/go/os/sys_darwin.go [new file with mode: 0644]
libgo/go/os/sys_freebsd.go [new file with mode: 0644]
libgo/go/os/sys_nacl.go [new file with mode: 0644]
libgo/go/os/sys_unix.go [new file with mode: 0644]
libgo/go/path/filepath/match.go
libgo/go/path/filepath/match_test.go
libgo/go/path/filepath/path.go
libgo/go/path/filepath/path_test.go
libgo/go/path/filepath/path_unix.go
libgo/go/reflect/all_test.go
libgo/go/reflect/export_test.go
libgo/go/reflect/makefunc.go
libgo/go/reflect/type.go
libgo/go/reflect/value.go
libgo/go/regexp/all_test.go
libgo/go/regexp/exec.go
libgo/go/regexp/onepass.go [new file with mode: 0644]
libgo/go/regexp/onepass_test.go [new file with mode: 0644]
libgo/go/regexp/regexp.go
libgo/go/regexp/syntax/doc.go
libgo/go/regexp/syntax/parse.go
libgo/go/regexp/syntax/prog.go
libgo/go/regexp/syntax/prog_test.go
libgo/go/runtime/chan_test.go
libgo/go/runtime/crash_test.go
libgo/go/runtime/debug/garbage.go
libgo/go/runtime/debug/heapdump_test.go [new file with mode: 0644]
libgo/go/runtime/extern.go
libgo/go/runtime/gc_test.go
libgo/go/runtime/map_test.go
libgo/go/runtime/memmove_test.go
libgo/go/runtime/mfinal_test.go
libgo/go/runtime/mgc0.go
libgo/go/runtime/norace_test.go
libgo/go/runtime/pprof/pprof.go
libgo/go/runtime/pprof/pprof_test.go
libgo/go/runtime/proc_test.go
libgo/go/runtime/runtime_test.go
libgo/go/runtime/type.go
libgo/go/sort/sort.go
libgo/go/strconv/atoi.go
libgo/go/strconv/quote.go
libgo/go/strconv/quote_test.go
libgo/go/strings/reader.go
libgo/go/strings/reader_test.go
libgo/go/strings/replace.go
libgo/go/strings/strings_test.go
libgo/go/sync/atomic/atomic_test.go
libgo/go/sync/mutex_test.go
libgo/go/sync/once_test.go
libgo/go/sync/pool.go
libgo/go/sync/pool_test.go
libgo/go/sync/runtime_sema_test.go
libgo/go/sync/rwmutex_test.go
libgo/go/sync/waitgroup.go
libgo/go/sync/waitgroup_test.go
libgo/go/syscall/consistency_unix_test.go [deleted file]
libgo/go/syscall/dir_plan9.go
libgo/go/syscall/env_unix.go
libgo/go/syscall/exec_unix.go
libgo/go/syscall/libcall_posix.go
libgo/go/syscall/libcall_posix_largefile.go
libgo/go/syscall/libcall_posix_regfile.go
libgo/go/syscall/mmap_unix_test.go [new file with mode: 0644]
libgo/go/syscall/rlimit_unix_test.go [deleted file]
libgo/go/syscall/route_dragonfly.go
libgo/go/syscall/route_freebsd.go
libgo/go/syscall/route_freebsd_32bit.go [new file with mode: 0644]
libgo/go/syscall/route_freebsd_64bit.go [new file with mode: 0644]
libgo/go/syscall/route_netbsd.go
libgo/go/syscall/route_openbsd.go
libgo/go/syscall/sockcmsg_unix.go
libgo/go/syscall/socket.go
libgo/go/syscall/socket_posix.go
libgo/go/syscall/socket_xnet.go
libgo/go/syscall/syscall_linux_386.go
libgo/go/syscall/syscall_unix.go
libgo/go/syscall/syscall_unix_test.go [moved from libgo/go/syscall/passfd_test.go with 62% similarity]
libgo/go/testing/benchmark.go
libgo/go/testing/benchmark_test.go
libgo/go/testing/testing.go
libgo/go/text/scanner/scanner.go
libgo/go/text/scanner/scanner_test.go
libgo/go/text/template/doc.go
libgo/go/text/template/exec.go
libgo/go/time/format.go
libgo/go/time/format_test.go
libgo/go/time/internal_test.go
libgo/go/time/sleep.go
libgo/go/time/sleep_test.go
libgo/go/time/sys_unix.go
libgo/go/time/tick.go
libgo/go/time/time.go
libgo/go/time/time_test.go
libgo/go/time/zoneinfo_read.go
libgo/go/time/zoneinfo_test.go
libgo/go/time/zoneinfo_unix.go
libgo/go/time/zoneinfo_windows.go
libgo/go/unicode/letter.go
libgo/go/unicode/letter_test.go
libgo/go/unicode/utf16/utf16.go
libgo/go/unicode/utf8/utf8.go
libgo/merge.sh
libgo/runtime/chan.c [new file with mode: 0644]
libgo/runtime/chan.goc
libgo/runtime/cpuprof.c [new file with mode: 0644]
libgo/runtime/cpuprof.goc
libgo/runtime/env_posix.c
libgo/runtime/go-now.c
libgo/runtime/go-signal.c
libgo/runtime/heapdump.c [new file with mode: 0644]
libgo/runtime/lfstack.c [new file with mode: 0644]
libgo/runtime/lock_sema.c
libgo/runtime/malloc.goc
libgo/runtime/malloc.h
libgo/runtime/mcache.c
libgo/runtime/mcentral.c
libgo/runtime/mem.c
libgo/runtime/mgc0.c
libgo/runtime/mgc0.h
libgo/runtime/mheap.c
libgo/runtime/mprof.goc
libgo/runtime/netpoll.goc
libgo/runtime/netpoll_epoll.c
libgo/runtime/netpoll_kqueue.c
libgo/runtime/panic.c
libgo/runtime/print.c
libgo/runtime/proc.c
libgo/runtime/race.h
libgo/runtime/rdebug.goc
libgo/runtime/runtime.c
libgo/runtime/runtime.h
libgo/runtime/time.goc

index 23732d0fc85e94a399c58214a41bd0e59452cfe6..b7e1968e24f6a6d2d0810ee8d17061ac8c51fc4f 100644 (file)
@@ -1,4 +1,4 @@
-63484e8b6b76
+9895f9e36435
 
 The first line of this file holds the Mercurial revision number of the
 last merge done from the master library sources.
index f9bc89f322e017213945a1b3bdadff2de35999f7..ab15e90b7bdbcafe577c0d319f606e16dc485633 100644 (file)
@@ -196,7 +196,6 @@ toolexeclibgodebugdir = $(toolexeclibgodir)/debug
 toolexeclibgodebug_DATA = \
        debug/dwarf.gox \
        debug/elf.gox \
-       debug/goobj.gox \
        debug/gosym.gox \
        debug/macho.gox \
        debug/pe.gox \
@@ -492,6 +491,7 @@ runtime_files = \
        runtime/go-unwind.c \
        runtime/go-varargs.c \
        runtime/env_posix.c \
+       runtime/heapdump.c \
        $(runtime_lock_files) \
        runtime/mcache.c \
        runtime/mcentral.c \
@@ -689,8 +689,8 @@ else
 if LIBGO_IS_SOLARIS
 go_net_cgo_file = go/net/cgo_linux.go
 go_net_sock_file = go/net/sock_solaris.go
-go_net_sockopt_file = go/net/sockopt_bsd.go
-go_net_sockoptip_file = go/net/sockoptip_bsd.go go/net/sockoptip_posix.go
+go_net_sockopt_file = go/net/sockopt_solaris.go
+go_net_sockoptip_file = go/net/sockoptip_stub.go
 else
 if LIBGO_IS_FREEBSD
 go_net_cgo_file = go/net/cgo_bsd.go
@@ -755,9 +755,13 @@ if LIBGO_IS_DARWIN
 go_net_tcpsockopt_file = go/net/tcpsockopt_darwin.go
 else
 if LIBGO_IS_SOLARIS
-go_net_tcpsockopt_file = go/net/tcpsockopt_darwin.go
+go_net_tcpsockopt_file = go/net/tcpsockopt_solaris.go
+else
+if LIBGO_IS_DRAGONFLY
+go_net_tcpsockopt_file = go/net/tcpsockopt_dragonfly.go
 else
-go_net_tcpsockopt_file =  go/net/tcpsockopt_unix.go
+go_net_tcpsockopt_file = go/net/tcpsockopt_unix.go
+endif
 endif
 endif
 endif
@@ -850,6 +854,16 @@ endif
 endif
 endif
 
+if LIBGO_IS_FREEBSD
+go_os_cloexec_file = go/os/sys_freebsd.go
+else
+if LIBGO_IS_DARWIN
+go_os_cloexec_file = go/os/sys_darwin.go
+else
+go_os_cloexec_file = go/os/sys_unix.go
+endif
+endif
+
 if LIBGO_IS_SOLARIS
 go_os_stat_file = go/os/stat_solaris.go
 else
@@ -908,6 +922,7 @@ go_os_files = \
        $(go_os_stat_file) \
        go/os/str.go \
        $(go_os_sys_file) \
+       $(go_os_cloexec_file) \
        go/os/types.go \
        go/os/types_notwin.go
 
@@ -942,6 +957,7 @@ go_reflect_files = \
 
 go_regexp_files = \
        go/regexp/exec.go \
+       go/regexp/onepass.go \
        go/regexp/regexp.go
 
 go_net_rpc_files = \
@@ -1214,8 +1230,6 @@ go_debug_dwarf_files = \
 go_debug_elf_files = \
        go/debug/elf/elf.go \
        go/debug/elf/file.go
-go_debug_goobj_files = \
-       go/debug/goobj/read.go
 go_debug_gosym_files = \
        go/debug/gosym/pclntab.go \
        go/debug/gosym/symtab.go
@@ -1744,7 +1758,9 @@ go_syscall_c_files = \
 
 go_syscall_test_files = \
        $(syscall_creds_test_file) \
-       go/syscall/passfd_test.go
+       go/syscall/mmap_unix_test.go \
+       go/syscall/syscall_test.go \
+       go/syscall/syscall_unix_test.go
 
 libcalls.go: s-libcalls; @true
 s-libcalls: libcalls-list go/syscall/mksyscall.awk $(go_base_syscall_files)
@@ -1879,7 +1895,6 @@ libgo_go_objs = \
        database/sql/driver.lo \
        debug/dwarf.lo \
        debug/elf.lo \
-       debug/goobj.lo \
        debug/gosym.lo \
        debug/macho.lo \
        debug/pe.lo \
@@ -2611,15 +2626,6 @@ debug/elf/check: $(CHECK_DEPS)
        @$(CHECK)
 .PHONY: debug/elf/check
 
-@go_include@ debug/goobj.lo.dep
-debug/goobj.lo.dep: $(go_debug_goobj_files)
-       $(BUILDDEPS)
-debug/goobj.lo: $(go_debug_goobj_files)
-       $(BUILDPACKAGE)
-debug/goobj/check: $(CHECK_DEPS)
-       @$(CHECK)
-.PHONY: debug/goobj/check
-
 @go_include@ debug/gosym.lo.dep
 debug/gosym.lo.dep: $(go_debug_gosym_files)
        $(BUILDDEPS)
@@ -3447,8 +3453,6 @@ debug/dwarf.gox: debug/dwarf.lo
        $(BUILDGOX)
 debug/elf.gox: debug/elf.lo
        $(BUILDGOX)
-debug/goobj.gox: debug/goobj.lo
-       $(BUILDGOX)
 debug/gosym.gox: debug/gosym.lo
        $(BUILDGOX)
 debug/macho.gox: debug/macho.lo
index 8207e602f8e7a2143c634fcf1fff616aca59c842..f11adca45226b7d95c59ad69744181c9213d8651 100644 (file)
@@ -147,22 +147,22 @@ am__DEPENDENCIES_2 = bufio.lo bytes.lo bytes/index.lo crypto.lo \
        crypto/rc4.lo crypto/rsa.lo crypto/sha1.lo crypto/sha256.lo \
        crypto/sha512.lo crypto/subtle.lo crypto/tls.lo crypto/x509.lo \
        crypto/x509/pkix.lo database/sql.lo database/sql/driver.lo \
-       debug/dwarf.lo debug/elf.lo debug/goobj.lo debug/gosym.lo \
-       debug/macho.lo debug/pe.lo debug/plan9obj.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/proxy.lo exp/terminal.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/cookiejar.lo net/http/fcgi.lo \
-       net/http/httptest.lo net/http/httputil.lo net/http/pprof.lo \
-       image/color.lo image/color/palette.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 \
+       debug/dwarf.lo debug/elf.lo debug/gosym.lo debug/macho.lo \
+       debug/pe.lo debug/plan9obj.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/proxy.lo exp/terminal.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/cookiejar.lo net/http/fcgi.lo net/http/httptest.lo \
+       net/http/httputil.lo net/http/pprof.lo image/color.lo \
+       image/color/palette.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/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 \
@@ -211,13 +211,14 @@ am__objects_6 = go-append.lo go-assert.lo go-assert-interface.lo \
        go-type-float.lo go-type-identity.lo go-type-interface.lo \
        go-type-string.lo go-typedesc-equal.lo go-unsafe-new.lo \
        go-unsafe-newarray.lo go-unsafe-pointer.lo go-unwind.lo \
-       go-varargs.lo env_posix.lo $(am__objects_1) mcache.lo \
-       mcentral.lo $(am__objects_2) mfixalloc.lo mgc0.lo mheap.lo \
-       msize.lo $(am__objects_3) panic.lo parfor.lo print.lo proc.lo \
-       runtime.lo signal_unix.lo thread.lo yield.lo $(am__objects_4) \
-       chan.lo cpuprof.lo go-iface.lo lfstack.lo malloc.lo map.lo \
-       mprof.lo netpoll.lo rdebug.lo reflect.lo runtime1.lo sema.lo \
-       sigqueue.lo string.lo time.lo $(am__objects_5)
+       go-varargs.lo env_posix.lo heapdump.lo $(am__objects_1) \
+       mcache.lo mcentral.lo $(am__objects_2) mfixalloc.lo mgc0.lo \
+       mheap.lo msize.lo $(am__objects_3) panic.lo parfor.lo print.lo \
+       proc.lo runtime.lo signal_unix.lo thread.lo yield.lo \
+       $(am__objects_4) chan.lo cpuprof.lo go-iface.lo lfstack.lo \
+       malloc.lo map.lo mprof.lo netpoll.lo rdebug.lo reflect.lo \
+       runtime1.lo sema.lo sigqueue.lo string.lo time.lo \
+       $(am__objects_5)
 am_libgo_la_OBJECTS = $(am__objects_6)
 libgo_la_OBJECTS = $(am_libgo_la_OBJECTS)
 libgo_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
@@ -587,7 +588,6 @@ toolexeclibgodebugdir = $(toolexeclibgodir)/debug
 toolexeclibgodebug_DATA = \
        debug/dwarf.gox \
        debug/elf.gox \
-       debug/goobj.gox \
        debug/gosym.gox \
        debug/macho.gox \
        debug/pe.gox \
@@ -820,6 +820,7 @@ runtime_files = \
        runtime/go-unwind.c \
        runtime/go-varargs.c \
        runtime/env_posix.c \
+       runtime/heapdump.c \
        $(runtime_lock_files) \
        runtime/mcache.c \
        runtime/mcentral.c \
@@ -976,13 +977,13 @@ go_mime_files = \
 @LIBGO_IS_FREEBSD_FALSE@@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_net_sockopt_file = go/net/sockopt_bsd.go
 @LIBGO_IS_FREEBSD_FALSE@@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_TRUE@@LIBGO_IS_SOLARIS_FALSE@go_net_sockopt_file = go/net/sockopt_bsd.go
 @LIBGO_IS_FREEBSD_TRUE@@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_net_sockopt_file = go/net/sockopt_bsd.go
-@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_TRUE@go_net_sockopt_file = go/net/sockopt_bsd.go
+@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_TRUE@go_net_sockopt_file = go/net/sockopt_solaris.go
 @LIBGO_IS_IRIX_TRUE@@LIBGO_IS_LINUX_FALSE@go_net_sockopt_file = go/net/sockopt_linux.go
 @LIBGO_IS_LINUX_TRUE@go_net_sockopt_file = go/net/sockopt_linux.go
 @LIBGO_IS_FREEBSD_FALSE@@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_net_sockoptip_file = go/net/sockoptip_bsd.go go/net/sockoptip_posix.go
 @LIBGO_IS_FREEBSD_FALSE@@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_TRUE@@LIBGO_IS_SOLARIS_FALSE@go_net_sockoptip_file = go/net/sockoptip_bsd.go go/net/sockoptip_posix.go
 @LIBGO_IS_FREEBSD_TRUE@@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_net_sockoptip_file = go/net/sockoptip_bsd.go go/net/sockoptip_posix.go
-@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_TRUE@go_net_sockoptip_file = go/net/sockoptip_bsd.go go/net/sockoptip_posix.go
+@LIBGO_IS_IRIX_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_TRUE@go_net_sockoptip_file = go/net/sockoptip_stub.go
 @LIBGO_IS_IRIX_TRUE@@LIBGO_IS_LINUX_FALSE@go_net_sockoptip_file = go/net/sockoptip_linux.go go/net/sockoptip_posix.go
 @LIBGO_IS_LINUX_TRUE@go_net_sockoptip_file = go/net/sockoptip_linux.go go/net/sockoptip_posix.go
 @LIBGO_IS_DRAGONFLY_FALSE@@LIBGO_IS_FREEBSD_FALSE@@LIBGO_IS_LINUX_FALSE@go_net_sendfile_file = go/net/sendfile_stub.go
@@ -995,8 +996,9 @@ go_mime_files = \
 @LIBGO_IS_LINUX_TRUE@go_net_interface_file = go/net/interface_linux.go
 @LIBGO_IS_LINUX_FALSE@go_net_cloexec_file = go/net/sys_cloexec.go
 @LIBGO_IS_LINUX_TRUE@go_net_cloexec_file = go/net/sock_cloexec.go
-@LIBGO_IS_DARWIN_FALSE@@LIBGO_IS_OPENBSD_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_net_tcpsockopt_file = go/net/tcpsockopt_unix.go
-@LIBGO_IS_DARWIN_FALSE@@LIBGO_IS_OPENBSD_FALSE@@LIBGO_IS_SOLARIS_TRUE@go_net_tcpsockopt_file = go/net/tcpsockopt_darwin.go
+@LIBGO_IS_DARWIN_FALSE@@LIBGO_IS_DRAGONFLY_FALSE@@LIBGO_IS_OPENBSD_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_net_tcpsockopt_file = go/net/tcpsockopt_unix.go
+@LIBGO_IS_DARWIN_FALSE@@LIBGO_IS_DRAGONFLY_TRUE@@LIBGO_IS_OPENBSD_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_net_tcpsockopt_file = go/net/tcpsockopt_dragonfly.go
+@LIBGO_IS_DARWIN_FALSE@@LIBGO_IS_OPENBSD_FALSE@@LIBGO_IS_SOLARIS_TRUE@go_net_tcpsockopt_file = go/net/tcpsockopt_solaris.go
 @LIBGO_IS_DARWIN_TRUE@@LIBGO_IS_OPENBSD_FALSE@go_net_tcpsockopt_file = go/net/tcpsockopt_darwin.go
 @LIBGO_IS_OPENBSD_TRUE@go_net_tcpsockopt_file = go/net/tcpsockopt_openbsd.go
 go_net_files = \
@@ -1057,6 +1059,9 @@ go_net_files = \
 @LIBGO_IS_IRIX_TRUE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_os_sys_file = go/os/sys_uname.go
 @LIBGO_IS_LINUX_FALSE@@LIBGO_IS_SOLARIS_TRUE@go_os_sys_file = go/os/sys_uname.go
 @LIBGO_IS_LINUX_TRUE@go_os_sys_file = go/os/sys_linux.go
+@LIBGO_IS_DARWIN_FALSE@@LIBGO_IS_FREEBSD_FALSE@go_os_cloexec_file = go/os/sys_unix.go
+@LIBGO_IS_DARWIN_TRUE@@LIBGO_IS_FREEBSD_FALSE@go_os_cloexec_file = go/os/sys_darwin.go
+@LIBGO_IS_FREEBSD_TRUE@go_os_cloexec_file = go/os/sys_freebsd.go
 @LIBGO_IS_DARWIN_FALSE@@LIBGO_IS_DRAGONFLY_FALSE@@LIBGO_IS_FREEBSD_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_FALSE@@LIBGO_IS_OPENBSD_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_os_stat_file = go/os/stat.go
 @LIBGO_IS_DARWIN_FALSE@@LIBGO_IS_DRAGONFLY_TRUE@@LIBGO_IS_FREEBSD_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_FALSE@@LIBGO_IS_OPENBSD_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_os_stat_file = go/os/stat_dragonfly.go
 @LIBGO_IS_DARWIN_FALSE@@LIBGO_IS_FREEBSD_FALSE@@LIBGO_IS_LINUX_FALSE@@LIBGO_IS_NETBSD_TRUE@@LIBGO_IS_OPENBSD_FALSE@@LIBGO_IS_SOLARIS_FALSE@go_os_stat_file = go/os/stat_atimespec.go
@@ -1089,6 +1094,7 @@ go_os_files = \
        $(go_os_stat_file) \
        go/os/str.go \
        $(go_os_sys_file) \
+       $(go_os_cloexec_file) \
        go/os/types.go \
        go/os/types_notwin.go
 
@@ -1121,6 +1127,7 @@ go_reflect_files = \
 
 go_regexp_files = \
        go/regexp/exec.go \
+       go/regexp/onepass.go \
        go/regexp/regexp.go
 
 go_net_rpc_files = \
@@ -1382,9 +1389,6 @@ go_debug_elf_files = \
        go/debug/elf/elf.go \
        go/debug/elf/file.go
 
-go_debug_goobj_files = \
-       go/debug/goobj/read.go
-
 go_debug_gosym_files = \
        go/debug/gosym/pclntab.go \
        go/debug/gosym/symtab.go
@@ -1876,7 +1880,9 @@ go_syscall_c_files = \
 
 go_syscall_test_files = \
        $(syscall_creds_test_file) \
-       go/syscall/passfd_test.go
+       go/syscall/mmap_unix_test.go \
+       go/syscall/syscall_test.go \
+       go/syscall/syscall_unix_test.go
 
 @LIBGO_IS_LINUX_FALSE@os_lib_inotify_lo = 
 
@@ -1950,7 +1956,6 @@ libgo_go_objs = \
        database/sql/driver.lo \
        debug/dwarf.lo \
        debug/elf.lo \
-       debug/goobj.lo \
        debug/gosym.lo \
        debug/macho.lo \
        debug/pe.lo \
@@ -2473,6 +2478,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-unsafe-pointer.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-unwind.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/go-varargs.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/heapdump.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lfstack.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lock_futex.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lock_sema.Plo@am__quote@
@@ -3000,6 +3006,13 @@ env_posix.lo: runtime/env_posix.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@  $(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o env_posix.lo `test -f 'runtime/env_posix.c' || echo '$(srcdir)/'`runtime/env_posix.c
 
+heapdump.lo: runtime/heapdump.c
+@am__fastdepCC_TRUE@   $(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT heapdump.lo -MD -MP -MF $(DEPDIR)/heapdump.Tpo -c -o heapdump.lo `test -f 'runtime/heapdump.c' || echo '$(srcdir)/'`runtime/heapdump.c
+@am__fastdepCC_TRUE@   $(am__mv) $(DEPDIR)/heapdump.Tpo $(DEPDIR)/heapdump.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      source='runtime/heapdump.c' object='heapdump.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@      DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@  $(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o heapdump.lo `test -f 'runtime/heapdump.c' || echo '$(srcdir)/'`runtime/heapdump.c
+
 lock_sema.lo: runtime/lock_sema.c
 @am__fastdepCC_TRUE@   $(LIBTOOL)  --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT lock_sema.lo -MD -MP -MF $(DEPDIR)/lock_sema.Tpo -c -o lock_sema.lo `test -f 'runtime/lock_sema.c' || echo '$(srcdir)/'`runtime/lock_sema.c
 @am__fastdepCC_TRUE@   $(am__mv) $(DEPDIR)/lock_sema.Tpo $(DEPDIR)/lock_sema.Plo
@@ -4946,15 +4959,6 @@ debug/elf/check: $(CHECK_DEPS)
        @$(CHECK)
 .PHONY: debug/elf/check
 
-@go_include@ debug/goobj.lo.dep
-debug/goobj.lo.dep: $(go_debug_goobj_files)
-       $(BUILDDEPS)
-debug/goobj.lo: $(go_debug_goobj_files)
-       $(BUILDPACKAGE)
-debug/goobj/check: $(CHECK_DEPS)
-       @$(CHECK)
-.PHONY: debug/goobj/check
-
 @go_include@ debug/gosym.lo.dep
 debug/gosym.lo.dep: $(go_debug_gosym_files)
        $(BUILDDEPS)
@@ -5774,8 +5778,6 @@ debug/dwarf.gox: debug/dwarf.lo
        $(BUILDGOX)
 debug/elf.gox: debug/elf.lo
        $(BUILDGOX)
-debug/goobj.gox: debug/goobj.lo
-       $(BUILDGOX)
 debug/gosym.gox: debug/gosym.lo
        $(BUILDGOX)
 debug/macho.gox: debug/macho.lo
index e8b973c1faf0b5b8566b9bc0fc1f9fe4226ae3d4..e363aa793e0f32410366a702b46c75ec621b2059 100644 (file)
@@ -38,6 +38,7 @@ const (
        TypeXGlobalHeader = 'g'    // global extended header
        TypeGNULongName   = 'L'    // Next file has a long name
        TypeGNULongLink   = 'K'    // Next file symlinks to a file w/ a long name
+       TypeGNUSparse     = 'S'    // sparse file
 )
 
 // A Header represents a single header in a tar archive.
index 7cb6e649c7bec17549063c890ad12f296e3e93a7..920a9b08f902032b37fb37ca523079cb78caab4f 100644 (file)
@@ -29,12 +29,57 @@ const maxNanoSecondIntSize = 9
 // The Next method advances to the next file in the archive (including the first),
 // and then it can be treated as an io.Reader to access the file's data.
 type Reader struct {
-       r   io.Reader
-       err error
-       nb  int64 // number of unread bytes for current file entry
-       pad int64 // amount of padding (ignored) after current file entry
+       r    io.Reader
+       err  error
+       pad  int64          // amount of padding (ignored) after current file entry
+       curr numBytesReader // reader for current file entry
 }
 
+// A numBytesReader is an io.Reader with a numBytes method, returning the number
+// of bytes remaining in the underlying encoded data.
+type numBytesReader interface {
+       io.Reader
+       numBytes() int64
+}
+
+// A regFileReader is a numBytesReader for reading file data from a tar archive.
+type regFileReader struct {
+       r  io.Reader // underlying reader
+       nb int64     // number of unread bytes for current file entry
+}
+
+// A sparseFileReader is a numBytesReader for reading sparse file data from a tar archive.
+type sparseFileReader struct {
+       rfr *regFileReader // reads the sparse-encoded file data
+       sp  []sparseEntry  // the sparse map for the file
+       pos int64          // keeps track of file position
+       tot int64          // total size of the file
+}
+
+// Keywords for GNU sparse files in a PAX extended header
+const (
+       paxGNUSparseNumBlocks = "GNU.sparse.numblocks"
+       paxGNUSparseOffset    = "GNU.sparse.offset"
+       paxGNUSparseNumBytes  = "GNU.sparse.numbytes"
+       paxGNUSparseMap       = "GNU.sparse.map"
+       paxGNUSparseName      = "GNU.sparse.name"
+       paxGNUSparseMajor     = "GNU.sparse.major"
+       paxGNUSparseMinor     = "GNU.sparse.minor"
+       paxGNUSparseSize      = "GNU.sparse.size"
+       paxGNUSparseRealSize  = "GNU.sparse.realsize"
+)
+
+// Keywords for old GNU sparse headers
+const (
+       oldGNUSparseMainHeaderOffset               = 386
+       oldGNUSparseMainHeaderIsExtendedOffset     = 482
+       oldGNUSparseMainHeaderNumEntries           = 4
+       oldGNUSparseExtendedHeaderIsExtendedOffset = 504
+       oldGNUSparseExtendedHeaderNumEntries       = 21
+       oldGNUSparseOffsetSize                     = 12
+       oldGNUSparseNumBytesSize                   = 12
+)
+
 // NewReader creates a new Reader reading from r.
 func NewReader(r io.Reader) *Reader { return &Reader{r: r} }
 
@@ -64,6 +109,18 @@ func (tr *Reader) Next() (*Header, error) {
                tr.skipUnread()
                hdr = tr.readHeader()
                mergePAX(hdr, headers)
+
+               // Check for a PAX format sparse file
+               sp, err := tr.checkForGNUSparsePAXHeaders(hdr, headers)
+               if err != nil {
+                       tr.err = err
+                       return nil, err
+               }
+               if sp != nil {
+                       // Current file is a PAX format GNU sparse file.
+                       // Set the current file reader to a sparse file reader.
+                       tr.curr = &sparseFileReader{rfr: tr.curr.(*regFileReader), sp: sp, tot: hdr.Size}
+               }
                return hdr, nil
        case TypeGNULongName:
                // We have a GNU long name header. Its contents are the real file name.
@@ -87,6 +144,67 @@ func (tr *Reader) Next() (*Header, error) {
        return hdr, tr.err
 }
 
+// checkForGNUSparsePAXHeaders checks the PAX headers for GNU sparse headers. If they are found, then
+// this function reads the sparse map and returns it. Unknown sparse formats are ignored, causing the file to
+// be treated as a regular file.
+func (tr *Reader) checkForGNUSparsePAXHeaders(hdr *Header, headers map[string]string) ([]sparseEntry, error) {
+       var sparseFormat string
+
+       // Check for sparse format indicators
+       major, majorOk := headers[paxGNUSparseMajor]
+       minor, minorOk := headers[paxGNUSparseMinor]
+       sparseName, sparseNameOk := headers[paxGNUSparseName]
+       _, sparseMapOk := headers[paxGNUSparseMap]
+       sparseSize, sparseSizeOk := headers[paxGNUSparseSize]
+       sparseRealSize, sparseRealSizeOk := headers[paxGNUSparseRealSize]
+
+       // Identify which, if any, sparse format applies from which PAX headers are set
+       if majorOk && minorOk {
+               sparseFormat = major + "." + minor
+       } else if sparseNameOk && sparseMapOk {
+               sparseFormat = "0.1"
+       } else if sparseSizeOk {
+               sparseFormat = "0.0"
+       } else {
+               // Not a PAX format GNU sparse file.
+               return nil, nil
+       }
+
+       // Check for unknown sparse format
+       if sparseFormat != "0.0" && sparseFormat != "0.1" && sparseFormat != "1.0" {
+               return nil, nil
+       }
+
+       // Update hdr from GNU sparse PAX headers
+       if sparseNameOk {
+               hdr.Name = sparseName
+       }
+       if sparseSizeOk {
+               realSize, err := strconv.ParseInt(sparseSize, 10, 0)
+               if err != nil {
+                       return nil, ErrHeader
+               }
+               hdr.Size = realSize
+       } else if sparseRealSizeOk {
+               realSize, err := strconv.ParseInt(sparseRealSize, 10, 0)
+               if err != nil {
+                       return nil, ErrHeader
+               }
+               hdr.Size = realSize
+       }
+
+       // Set up the sparse map, according to the particular sparse format in use
+       var sp []sparseEntry
+       var err error
+       switch sparseFormat {
+       case "0.0", "0.1":
+               sp, err = readGNUSparseMap0x1(headers)
+       case "1.0":
+               sp, err = readGNUSparseMap1x0(tr.curr)
+       }
+       return sp, err
+}
+
 // mergePAX merges well known headers according to PAX standard.
 // In general headers with the same name as those found
 // in the header struct overwrite those found in the header
@@ -194,6 +312,11 @@ func parsePAX(r io.Reader) (map[string]string, error) {
        if err != nil {
                return nil, err
        }
+
+       // For GNU PAX sparse format 0.0 support.
+       // This function transforms the sparse format 0.0 headers into sparse format 0.1 headers.
+       var sparseMap bytes.Buffer
+
        headers := make(map[string]string)
        // Each record is constructed as
        //     "%d %s=%s\n", length, keyword, value
@@ -211,7 +334,7 @@ func parsePAX(r io.Reader) (map[string]string, error) {
                        return nil, ErrHeader
                }
                // Extract everything between the decimal and the n -1 on the
-               // beginning to to eat the ' ', -1 on the end to skip the newline.
+               // beginning to eat the ' ', -1 on the end to skip the newline.
                var record []byte
                record, buf = buf[sp+1:n-1], buf[n:]
                // The first equals is guaranteed to mark the end of the key.
@@ -221,7 +344,21 @@ func parsePAX(r io.Reader) (map[string]string, error) {
                        return nil, ErrHeader
                }
                key, value := record[:eq], record[eq+1:]
-               headers[string(key)] = string(value)
+
+               keyStr := string(key)
+               if keyStr == paxGNUSparseOffset || keyStr == paxGNUSparseNumBytes {
+                       // GNU sparse format 0.0 special key. Write to sparseMap instead of using the headers map.
+                       sparseMap.Write(value)
+                       sparseMap.Write([]byte{','})
+               } else {
+                       // Normal key. Set the value in the headers map.
+                       headers[keyStr] = string(value)
+               }
+       }
+       if sparseMap.Len() != 0 {
+               // Add sparse info to headers, chopping off the extra comma
+               sparseMap.Truncate(sparseMap.Len() - 1)
+               headers[paxGNUSparseMap] = sparseMap.String()
        }
        return headers, nil
 }
@@ -268,8 +405,8 @@ func (tr *Reader) octal(b []byte) int64 {
 
 // skipUnread skips any unread bytes in the existing file entry, as well as any alignment padding.
 func (tr *Reader) skipUnread() {
-       nr := tr.nb + tr.pad // number of bytes to skip
-       tr.nb, tr.pad = 0, 0
+       nr := tr.numBytes() + tr.pad // number of bytes to skip
+       tr.curr, tr.pad = nil, 0
        if sr, ok := tr.r.(io.Seeker); ok {
                if _, err := sr.Seek(nr, os.SEEK_CUR); err == nil {
                        return
@@ -331,14 +468,14 @@ func (tr *Reader) readHeader() *Header {
        // so its magic bytes, like the rest of the block, are NULs.
        magic := string(s.next(8)) // contains version field as well.
        var format string
-       switch magic {
-       case "ustar\x0000": // POSIX tar (1003.1-1988)
+       switch {
+       case magic[:6] == "ustar\x00": // POSIX tar (1003.1-1988)
                if string(header[508:512]) == "tar\x00" {
                        format = "star"
                } else {
                        format = "posix"
                }
-       case "ustar  \x00": // old GNU tar
+       case magic == "ustar  \x00": // old GNU tar
                format = "gnu"
        }
 
@@ -373,30 +510,308 @@ func (tr *Reader) readHeader() *Header {
 
        // Maximum value of hdr.Size is 64 GB (12 octal digits),
        // so there's no risk of int64 overflowing.
-       tr.nb = int64(hdr.Size)
-       tr.pad = -tr.nb & (blockSize - 1) // blockSize is a power of two
+       nb := int64(hdr.Size)
+       tr.pad = -nb & (blockSize - 1) // blockSize is a power of two
+
+       // Set the current file reader.
+       tr.curr = &regFileReader{r: tr.r, nb: nb}
+
+       // Check for old GNU sparse format entry.
+       if hdr.Typeflag == TypeGNUSparse {
+               // Get the real size of the file.
+               hdr.Size = tr.octal(header[483:495])
+
+               // Read the sparse map.
+               sp := tr.readOldGNUSparseMap(header)
+               if tr.err != nil {
+                       return nil
+               }
+               // Current file is a GNU sparse file. Update the current file reader.
+               tr.curr = &sparseFileReader{rfr: tr.curr.(*regFileReader), sp: sp, tot: hdr.Size}
+       }
 
        return hdr
 }
 
+// A sparseEntry holds a single entry in a sparse file's sparse map.
+// A sparse entry indicates the offset and size in a sparse file of a
+// block of data.
+type sparseEntry struct {
+       offset   int64
+       numBytes int64
+}
+
+// readOldGNUSparseMap reads the sparse map as stored in the old GNU sparse format.
+// The sparse map is stored in the tar header if it's small enough. If it's larger than four entries,
+// then one or more extension headers are used to store the rest of the sparse map.
+func (tr *Reader) readOldGNUSparseMap(header []byte) []sparseEntry {
+       isExtended := header[oldGNUSparseMainHeaderIsExtendedOffset] != 0
+       spCap := oldGNUSparseMainHeaderNumEntries
+       if isExtended {
+               spCap += oldGNUSparseExtendedHeaderNumEntries
+       }
+       sp := make([]sparseEntry, 0, spCap)
+       s := slicer(header[oldGNUSparseMainHeaderOffset:])
+
+       // Read the four entries from the main tar header
+       for i := 0; i < oldGNUSparseMainHeaderNumEntries; i++ {
+               offset := tr.octal(s.next(oldGNUSparseOffsetSize))
+               numBytes := tr.octal(s.next(oldGNUSparseNumBytesSize))
+               if tr.err != nil {
+                       tr.err = ErrHeader
+                       return nil
+               }
+               if offset == 0 && numBytes == 0 {
+                       break
+               }
+               sp = append(sp, sparseEntry{offset: offset, numBytes: numBytes})
+       }
+
+       for isExtended {
+               // There are more entries. Read an extension header and parse its entries.
+               sparseHeader := make([]byte, blockSize)
+               if _, tr.err = io.ReadFull(tr.r, sparseHeader); tr.err != nil {
+                       return nil
+               }
+               isExtended = sparseHeader[oldGNUSparseExtendedHeaderIsExtendedOffset] != 0
+               s = slicer(sparseHeader)
+               for i := 0; i < oldGNUSparseExtendedHeaderNumEntries; i++ {
+                       offset := tr.octal(s.next(oldGNUSparseOffsetSize))
+                       numBytes := tr.octal(s.next(oldGNUSparseNumBytesSize))
+                       if tr.err != nil {
+                               tr.err = ErrHeader
+                               return nil
+                       }
+                       if offset == 0 && numBytes == 0 {
+                               break
+                       }
+                       sp = append(sp, sparseEntry{offset: offset, numBytes: numBytes})
+               }
+       }
+       return sp
+}
+
+// readGNUSparseMap1x0 reads the sparse map as stored in GNU's PAX sparse format version 1.0.
+// The sparse map is stored just before the file data and padded out to the nearest block boundary.
+func readGNUSparseMap1x0(r io.Reader) ([]sparseEntry, error) {
+       buf := make([]byte, 2*blockSize)
+       sparseHeader := buf[:blockSize]
+
+       // readDecimal is a helper function to read a decimal integer from the sparse map
+       // while making sure to read from the file in blocks of size blockSize
+       readDecimal := func() (int64, error) {
+               // Look for newline
+               nl := bytes.IndexByte(sparseHeader, '\n')
+               if nl == -1 {
+                       if len(sparseHeader) >= blockSize {
+                               // This is an error
+                               return 0, ErrHeader
+                       }
+                       oldLen := len(sparseHeader)
+                       newLen := oldLen + blockSize
+                       if cap(sparseHeader) < newLen {
+                               // There's more header, but we need to make room for the next block
+                               copy(buf, sparseHeader)
+                               sparseHeader = buf[:newLen]
+                       } else {
+                               // There's more header, and we can just reslice
+                               sparseHeader = sparseHeader[:newLen]
+                       }
+
+                       // Now that sparseHeader is large enough, read next block
+                       if _, err := io.ReadFull(r, sparseHeader[oldLen:newLen]); err != nil {
+                               return 0, err
+                       }
+
+                       // Look for a newline in the new data
+                       nl = bytes.IndexByte(sparseHeader[oldLen:newLen], '\n')
+                       if nl == -1 {
+                               // This is an error
+                               return 0, ErrHeader
+                       }
+                       nl += oldLen // We want the position from the beginning
+               }
+               // Now that we've found a newline, read a number
+               n, err := strconv.ParseInt(string(sparseHeader[:nl]), 10, 0)
+               if err != nil {
+                       return 0, ErrHeader
+               }
+
+               // Update sparseHeader to consume this number
+               sparseHeader = sparseHeader[nl+1:]
+               return n, nil
+       }
+
+       // Read the first block
+       if _, err := io.ReadFull(r, sparseHeader); err != nil {
+               return nil, err
+       }
+
+       // The first line contains the number of entries
+       numEntries, err := readDecimal()
+       if err != nil {
+               return nil, err
+       }
+
+       // Read all the entries
+       sp := make([]sparseEntry, 0, numEntries)
+       for i := int64(0); i < numEntries; i++ {
+               // Read the offset
+               offset, err := readDecimal()
+               if err != nil {
+                       return nil, err
+               }
+               // Read numBytes
+               numBytes, err := readDecimal()
+               if err != nil {
+                       return nil, err
+               }
+
+               sp = append(sp, sparseEntry{offset: offset, numBytes: numBytes})
+       }
+
+       return sp, nil
+}
+
+// readGNUSparseMap0x1 reads the sparse map as stored in GNU's PAX sparse format version 0.1.
+// The sparse map is stored in the PAX headers.
+func readGNUSparseMap0x1(headers map[string]string) ([]sparseEntry, error) {
+       // Get number of entries
+       numEntriesStr, ok := headers[paxGNUSparseNumBlocks]
+       if !ok {
+               return nil, ErrHeader
+       }
+       numEntries, err := strconv.ParseInt(numEntriesStr, 10, 0)
+       if err != nil {
+               return nil, ErrHeader
+       }
+
+       sparseMap := strings.Split(headers[paxGNUSparseMap], ",")
+
+       // There should be two numbers in sparseMap for each entry
+       if int64(len(sparseMap)) != 2*numEntries {
+               return nil, ErrHeader
+       }
+
+       // Loop through the entries in the sparse map
+       sp := make([]sparseEntry, 0, numEntries)
+       for i := int64(0); i < numEntries; i++ {
+               offset, err := strconv.ParseInt(sparseMap[2*i], 10, 0)
+               if err != nil {
+                       return nil, ErrHeader
+               }
+               numBytes, err := strconv.ParseInt(sparseMap[2*i+1], 10, 0)
+               if err != nil {
+                       return nil, ErrHeader
+               }
+               sp = append(sp, sparseEntry{offset: offset, numBytes: numBytes})
+       }
+
+       return sp, nil
+}
+
+// numBytes returns the number of bytes left to read in the current file's entry
+// in the tar archive, or 0 if there is no current file.
+func (tr *Reader) numBytes() int64 {
+       if tr.curr == nil {
+               // No current file, so no bytes
+               return 0
+       }
+       return tr.curr.numBytes()
+}
+
 // Read reads from the current entry in the tar archive.
 // It returns 0, io.EOF when it reaches the end of that entry,
 // until Next is called to advance to the next entry.
 func (tr *Reader) Read(b []byte) (n int, err error) {
-       if tr.nb == 0 {
-               // file consumed
+       if tr.curr == nil {
                return 0, io.EOF
        }
+       n, err = tr.curr.Read(b)
+       if err != nil && err != io.EOF {
+               tr.err = err
+       }
+       return
+}
 
-       if int64(len(b)) > tr.nb {
-               b = b[0:tr.nb]
+func (rfr *regFileReader) Read(b []byte) (n int, err error) {
+       if rfr.nb == 0 {
+               // file consumed
+               return 0, io.EOF
        }
-       n, err = tr.r.Read(b)
-       tr.nb -= int64(n)
+       if int64(len(b)) > rfr.nb {
+               b = b[0:rfr.nb]
+       }
+       n, err = rfr.r.Read(b)
+       rfr.nb -= int64(n)
 
-       if err == io.EOF && tr.nb > 0 {
+       if err == io.EOF && rfr.nb > 0 {
                err = io.ErrUnexpectedEOF
        }
-       tr.err = err
        return
 }
+
+// numBytes returns the number of bytes left to read in the file's data in the tar archive.
+func (rfr *regFileReader) numBytes() int64 {
+       return rfr.nb
+}
+
+// readHole reads a sparse file hole ending at offset toOffset
+func (sfr *sparseFileReader) readHole(b []byte, toOffset int64) int {
+       n64 := toOffset - sfr.pos
+       if n64 > int64(len(b)) {
+               n64 = int64(len(b))
+       }
+       n := int(n64)
+       for i := 0; i < n; i++ {
+               b[i] = 0
+       }
+       sfr.pos += n64
+       return n
+}
+
+// Read reads the sparse file data in expanded form.
+func (sfr *sparseFileReader) Read(b []byte) (n int, err error) {
+       if len(sfr.sp) == 0 {
+               // No more data fragments to read from.
+               if sfr.pos < sfr.tot {
+                       // We're in the last hole
+                       n = sfr.readHole(b, sfr.tot)
+                       return
+               }
+               // Otherwise, we're at the end of the file
+               return 0, io.EOF
+       }
+       if sfr.pos < sfr.sp[0].offset {
+               // We're in a hole
+               n = sfr.readHole(b, sfr.sp[0].offset)
+               return
+       }
+
+       // We're not in a hole, so we'll read from the next data fragment
+       posInFragment := sfr.pos - sfr.sp[0].offset
+       bytesLeft := sfr.sp[0].numBytes - posInFragment
+       if int64(len(b)) > bytesLeft {
+               b = b[0:bytesLeft]
+       }
+
+       n, err = sfr.rfr.Read(b)
+       sfr.pos += int64(n)
+
+       if int64(n) == bytesLeft {
+               // We're done with this fragment
+               sfr.sp = sfr.sp[1:]
+       }
+
+       if err == io.EOF && sfr.pos < sfr.tot {
+               // We reached the end of the last fragment's data, but there's a final hole
+               err = nil
+       }
+       return
+}
+
+// numBytes returns the number of bytes left to read in the sparse file's
+// sparse-encoded data in the tar archive.
+func (sfr *sparseFileReader) numBytes() int64 {
+       return sfr.rfr.nb
+}
index f84dbebe98996d18d1cb6eafcf92340b87b03ef7..9601ffe459767b2b0a585331b1008eab0b747829 100644 (file)
@@ -9,6 +9,7 @@ import (
        "crypto/md5"
        "fmt"
        "io"
+       "io/ioutil"
        "os"
        "reflect"
        "strings"
@@ -54,8 +55,92 @@ var gnuTarTest = &untarTest{
        },
 }
 
+var sparseTarTest = &untarTest{
+       file: "testdata/sparse-formats.tar",
+       headers: []*Header{
+               {
+                       Name:     "sparse-gnu",
+                       Mode:     420,
+                       Uid:      1000,
+                       Gid:      1000,
+                       Size:     200,
+                       ModTime:  time.Unix(1392395740, 0),
+                       Typeflag: 0x53,
+                       Linkname: "",
+                       Uname:    "david",
+                       Gname:    "david",
+                       Devmajor: 0,
+                       Devminor: 0,
+               },
+               {
+                       Name:     "sparse-posix-0.0",
+                       Mode:     420,
+                       Uid:      1000,
+                       Gid:      1000,
+                       Size:     200,
+                       ModTime:  time.Unix(1392342187, 0),
+                       Typeflag: 0x30,
+                       Linkname: "",
+                       Uname:    "david",
+                       Gname:    "david",
+                       Devmajor: 0,
+                       Devminor: 0,
+               },
+               {
+                       Name:     "sparse-posix-0.1",
+                       Mode:     420,
+                       Uid:      1000,
+                       Gid:      1000,
+                       Size:     200,
+                       ModTime:  time.Unix(1392340456, 0),
+                       Typeflag: 0x30,
+                       Linkname: "",
+                       Uname:    "david",
+                       Gname:    "david",
+                       Devmajor: 0,
+                       Devminor: 0,
+               },
+               {
+                       Name:     "sparse-posix-1.0",
+                       Mode:     420,
+                       Uid:      1000,
+                       Gid:      1000,
+                       Size:     200,
+                       ModTime:  time.Unix(1392337404, 0),
+                       Typeflag: 0x30,
+                       Linkname: "",
+                       Uname:    "david",
+                       Gname:    "david",
+                       Devmajor: 0,
+                       Devminor: 0,
+               },
+               {
+                       Name:     "end",
+                       Mode:     420,
+                       Uid:      1000,
+                       Gid:      1000,
+                       Size:     4,
+                       ModTime:  time.Unix(1392398319, 0),
+                       Typeflag: 0x30,
+                       Linkname: "",
+                       Uname:    "david",
+                       Gname:    "david",
+                       Devmajor: 0,
+                       Devminor: 0,
+               },
+       },
+       cksums: []string{
+               "6f53234398c2449fe67c1812d993012f",
+               "6f53234398c2449fe67c1812d993012f",
+               "6f53234398c2449fe67c1812d993012f",
+               "6f53234398c2449fe67c1812d993012f",
+               "b0061974914468de549a2af8ced10316",
+       },
+}
+
 var untarTests = []*untarTest{
        gnuTarTest,
+       sparseTarTest,
        {
                file: "testdata/star.tar",
                headers: []*Header{
@@ -386,7 +471,7 @@ func TestParsePAXHeader(t *testing.T) {
 func TestParsePAXTime(t *testing.T) {
        // Some valid PAX time values
        timestamps := map[string]time.Time{
-               "1350244992.023960108":  time.Unix(1350244992, 23960108), // The commoon case
+               "1350244992.023960108":  time.Unix(1350244992, 23960108), // The common case
                "1350244992.02396010":   time.Unix(1350244992, 23960100), // Lower precision value
                "1350244992.0239601089": time.Unix(1350244992, 23960108), // Higher precision value
                "1350244992":            time.Unix(1350244992, 0),        // Low precision value
@@ -423,3 +508,236 @@ func TestMergePAX(t *testing.T) {
                t.Errorf("incorrect merge: got %+v, want %+v", hdr, want)
        }
 }
+
+func TestSparseEndToEnd(t *testing.T) {
+       test := sparseTarTest
+       f, err := os.Open(test.file)
+       if err != nil {
+               t.Fatalf("Unexpected error: %v", err)
+       }
+       defer f.Close()
+
+       tr := NewReader(f)
+
+       headers := test.headers
+       cksums := test.cksums
+       nread := 0
+
+       // loop over all files
+       for ; ; nread++ {
+               hdr, err := tr.Next()
+               if hdr == nil || err == io.EOF {
+                       break
+               }
+
+               // check the header
+               if !reflect.DeepEqual(*hdr, *headers[nread]) {
+                       t.Errorf("Incorrect header:\nhave %+v\nwant %+v",
+                               *hdr, headers[nread])
+               }
+
+               // read and checksum the file data
+               h := md5.New()
+               _, err = io.Copy(h, tr)
+               if err != nil {
+                       t.Fatalf("Unexpected error: %v", err)
+               }
+
+               // verify checksum
+               have := fmt.Sprintf("%x", h.Sum(nil))
+               want := cksums[nread]
+               if want != have {
+                       t.Errorf("Bad checksum on file %s:\nhave %+v\nwant %+v", hdr.Name, have, want)
+               }
+       }
+       if nread != len(headers) {
+               t.Errorf("Didn't process all files\nexpected: %d\nprocessed %d\n", len(headers), nread)
+       }
+}
+
+type sparseFileReadTest struct {
+       sparseData []byte
+       sparseMap  []sparseEntry
+       realSize   int64
+       expected   []byte
+}
+
+var sparseFileReadTests = []sparseFileReadTest{
+       {
+               sparseData: []byte("abcde"),
+               sparseMap: []sparseEntry{
+                       {offset: 0, numBytes: 2},
+                       {offset: 5, numBytes: 3},
+               },
+               realSize: 8,
+               expected: []byte("ab\x00\x00\x00cde"),
+       },
+       {
+               sparseData: []byte("abcde"),
+               sparseMap: []sparseEntry{
+                       {offset: 0, numBytes: 2},
+                       {offset: 5, numBytes: 3},
+               },
+               realSize: 10,
+               expected: []byte("ab\x00\x00\x00cde\x00\x00"),
+       },
+       {
+               sparseData: []byte("abcde"),
+               sparseMap: []sparseEntry{
+                       {offset: 1, numBytes: 3},
+                       {offset: 6, numBytes: 2},
+               },
+               realSize: 8,
+               expected: []byte("\x00abc\x00\x00de"),
+       },
+       {
+               sparseData: []byte("abcde"),
+               sparseMap: []sparseEntry{
+                       {offset: 1, numBytes: 3},
+                       {offset: 6, numBytes: 2},
+               },
+               realSize: 10,
+               expected: []byte("\x00abc\x00\x00de\x00\x00"),
+       },
+       {
+               sparseData: []byte(""),
+               sparseMap:  nil,
+               realSize:   2,
+               expected:   []byte("\x00\x00"),
+       },
+}
+
+func TestSparseFileReader(t *testing.T) {
+       for i, test := range sparseFileReadTests {
+               r := bytes.NewReader(test.sparseData)
+               nb := int64(r.Len())
+               sfr := &sparseFileReader{
+                       rfr: &regFileReader{r: r, nb: nb},
+                       sp:  test.sparseMap,
+                       pos: 0,
+                       tot: test.realSize,
+               }
+               if sfr.numBytes() != nb {
+                       t.Errorf("test %d: Before reading, sfr.numBytes() = %d, want %d", i, sfr.numBytes(), nb)
+               }
+               buf, err := ioutil.ReadAll(sfr)
+               if err != nil {
+                       t.Errorf("test %d: Unexpected error: %v", i, err)
+               }
+               if e := test.expected; !bytes.Equal(buf, e) {
+                       t.Errorf("test %d: Contents = %v, want %v", i, buf, e)
+               }
+               if sfr.numBytes() != 0 {
+                       t.Errorf("test %d: After draining the reader, numBytes() was nonzero", i)
+               }
+       }
+}
+
+func TestSparseIncrementalRead(t *testing.T) {
+       sparseMap := []sparseEntry{{10, 2}}
+       sparseData := []byte("Go")
+       expected := "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00Go\x00\x00\x00\x00\x00\x00\x00\x00"
+
+       r := bytes.NewReader(sparseData)
+       nb := int64(r.Len())
+       sfr := &sparseFileReader{
+               rfr: &regFileReader{r: r, nb: nb},
+               sp:  sparseMap,
+               pos: 0,
+               tot: int64(len(expected)),
+       }
+
+       // We'll read the data 6 bytes at a time, with a hole of size 10 at
+       // the beginning and one of size 8 at the end.
+       var outputBuf bytes.Buffer
+       buf := make([]byte, 6)
+       for {
+               n, err := sfr.Read(buf)
+               if err == io.EOF {
+                       break
+               }
+               if err != nil {
+                       t.Errorf("Read: unexpected error %v\n", err)
+               }
+               if n > 0 {
+                       _, err := outputBuf.Write(buf[:n])
+                       if err != nil {
+                               t.Errorf("Write: unexpected error %v\n", err)
+                       }
+               }
+       }
+       got := outputBuf.String()
+       if got != expected {
+               t.Errorf("Contents = %v, want %v", got, expected)
+       }
+}
+
+func TestReadGNUSparseMap0x1(t *testing.T) {
+       headers := map[string]string{
+               paxGNUSparseNumBlocks: "4",
+               paxGNUSparseMap:       "0,5,10,5,20,5,30,5",
+       }
+       expected := []sparseEntry{
+               {offset: 0, numBytes: 5},
+               {offset: 10, numBytes: 5},
+               {offset: 20, numBytes: 5},
+               {offset: 30, numBytes: 5},
+       }
+
+       sp, err := readGNUSparseMap0x1(headers)
+       if err != nil {
+               t.Errorf("Unexpected error: %v", err)
+       }
+       if !reflect.DeepEqual(sp, expected) {
+               t.Errorf("Incorrect sparse map: got %v, wanted %v", sp, expected)
+       }
+}
+
+func TestReadGNUSparseMap1x0(t *testing.T) {
+       // This test uses lots of holes so the sparse header takes up more than two blocks
+       numEntries := 100
+       expected := make([]sparseEntry, 0, numEntries)
+       sparseMap := new(bytes.Buffer)
+
+       fmt.Fprintf(sparseMap, "%d\n", numEntries)
+       for i := 0; i < numEntries; i++ {
+               offset := int64(2048 * i)
+               numBytes := int64(1024)
+               expected = append(expected, sparseEntry{offset: offset, numBytes: numBytes})
+               fmt.Fprintf(sparseMap, "%d\n%d\n", offset, numBytes)
+       }
+
+       // Make the header the smallest multiple of blockSize that fits the sparseMap
+       headerBlocks := (sparseMap.Len() + blockSize - 1) / blockSize
+       bufLen := blockSize * headerBlocks
+       buf := make([]byte, bufLen)
+       copy(buf, sparseMap.Bytes())
+
+       // Get an reader to read the sparse map
+       r := bytes.NewReader(buf)
+
+       // Read the sparse map
+       sp, err := readGNUSparseMap1x0(r)
+       if err != nil {
+               t.Errorf("Unexpected error: %v", err)
+       }
+       if !reflect.DeepEqual(sp, expected) {
+               t.Errorf("Incorrect sparse map: got %v, wanted %v", sp, expected)
+       }
+}
+
+func TestUninitializedRead(t *testing.T) {
+       test := gnuTarTest
+       f, err := os.Open(test.file)
+       if err != nil {
+               t.Fatalf("Unexpected error: %v", err)
+       }
+       defer f.Close()
+
+       tr := NewReader(f)
+       _, err = tr.Read([]byte{})
+       if err == nil || err != io.EOF {
+               t.Errorf("Unexpected error: %v, wanted %v", err, io.EOF)
+       }
+
+}
diff --git a/libgo/go/archive/tar/testdata/sparse-formats.tar b/libgo/go/archive/tar/testdata/sparse-formats.tar
new file mode 100644 (file)
index 0000000..8bd4e74
Binary files /dev/null and b/libgo/go/archive/tar/testdata/sparse-formats.tar differ
diff --git a/libgo/go/archive/tar/testdata/writer-big-long.tar b/libgo/go/archive/tar/testdata/writer-big-long.tar
new file mode 100644 (file)
index 0000000..5960ee8
Binary files /dev/null and b/libgo/go/archive/tar/testdata/writer-big-long.tar differ
index 9ee94992970eca4bf7b8ec7770616e0ef4767d45..6eff6f6f84d8eaa903b87c135ec4f5f6684b4667 100644 (file)
@@ -218,8 +218,8 @@ func (tw *Writer) writeHeader(hdr *Header, allowPax bool) error {
                                tw.cString(prefixHeaderBytes, prefix, false, paxNone, nil)
 
                                // Use the ustar magic if we used ustar long names.
-                               if len(prefix) > 0 {
-                                       copy(header[257:265], []byte("ustar\000"))
+                               if len(prefix) > 0 && !tw.usedBinary {
+                                       copy(header[257:265], []byte("ustar\x00"))
                                }
                        }
                }
index 2b9ea658db479193aebd5e3f391ea361b2869e79..512fab1a6f1c18496f7ca834dff08347c814f0cf 100644 (file)
@@ -103,6 +103,29 @@ var writerTests = []*writerTest{
                        },
                },
        },
+       // The truncated test file was produced using these commands:
+       //   dd if=/dev/zero bs=1048576 count=16384 > (longname/)*15 /16gig.txt
+       //   tar -b 1 -c -f- (longname/)*15 /16gig.txt | dd bs=512 count=8 > writer-big-long.tar
+       {
+               file: "testdata/writer-big-long.tar",
+               entries: []*writerTestEntry{
+                       {
+                               header: &Header{
+                                       Name:     strings.Repeat("longname/", 15) + "16gig.txt",
+                                       Mode:     0644,
+                                       Uid:      1000,
+                                       Gid:      1000,
+                                       Size:     16 << 30,
+                                       ModTime:  time.Unix(1399583047, 0),
+                                       Typeflag: '0',
+                                       Uname:    "guillaume",
+                                       Gname:    "guillaume",
+                               },
+                               // fake contents
+                               contents: strings.Repeat("\x00", 4<<10),
+                       },
+               },
+       },
        // This file was produced using gnu tar 1.17
        // gnutar  -b 4 --format=ustar (longname/)*15 + file.txt
        {
index 65e5238c3b45cd155f9299881ff37008d059381b..cb28e8324235544fa89130fd3a96f2af1a3837e1 100644 (file)
@@ -174,13 +174,13 @@ func timeToMsDosTime(t time.Time) (fDate uint16, fTime uint16) {
        return
 }
 
-// ModTime returns the modification time.
+// ModTime returns the modification time in UTC.
 // The resolution is 2s.
 func (h *FileHeader) ModTime() time.Time {
        return msDosTimeToTime(h.ModifiedDate, h.ModifiedTime)
 }
 
-// SetModTime sets the ModifiedTime and ModifiedDate fields to the given time.
+// SetModTime sets the ModifiedTime and ModifiedDate fields to the given time in UTC.
 // The resolution is 2s.
 func (h *FileHeader) SetModTime(t time.Time) {
        h.ModifiedDate, h.ModifiedTime = timeToMsDosTime(t)
index d1ff3c9edc16dead49f6d860c71a1a55dc23b192..61ef26191008e74c1d4ba0cde5885f825bb8791c 100644 (file)
@@ -38,6 +38,7 @@ type Reader struct {
 }
 
 const minReadBufferSize = 16
+const maxConsecutiveEmptyReads = 100
 
 // NewReaderSize returns a new Reader whose buffer has at least the specified
 // size. If the argument io.Reader is already a Reader with large enough
@@ -87,15 +88,26 @@ func (b *Reader) fill() {
                b.r = 0
        }
 
-       // Read new data.
-       n, err := b.rd.Read(b.buf[b.w:])
-       if n < 0 {
-               panic(errNegativeRead)
+       if b.w >= len(b.buf) {
+               panic("bufio: tried to fill full buffer")
        }
-       b.w += n
-       if err != nil {
-               b.err = err
+
+       // Read new data: try a limited number of times.
+       for i := maxConsecutiveEmptyReads; i > 0; i-- {
+               n, err := b.rd.Read(b.buf[b.w:])
+               if n < 0 {
+                       panic(errNegativeRead)
+               }
+               b.w += n
+               if err != nil {
+                       b.err = err
+                       return
+               }
+               if n > 0 {
+                       return
+               }
        }
+       b.err = io.ErrNoProgress
 }
 
 func (b *Reader) readErr() error {
@@ -115,8 +127,9 @@ func (b *Reader) Peek(n int) ([]byte, error) {
        if n > len(b.buf) {
                return nil, ErrBufferFull
        }
+       // 0 <= n <= len(b.buf)
        for b.w-b.r < n && b.err == nil {
-               b.fill()
+               b.fill() // b.w-b.r < len(b.buf) => buffer is not full
        }
        m := b.w - b.r
        if m > n {
@@ -142,7 +155,7 @@ func (b *Reader) Read(p []byte) (n int, err error) {
        if n == 0 {
                return 0, b.readErr()
        }
-       if b.w == b.r {
+       if b.r == b.w {
                if b.err != nil {
                        return 0, b.readErr()
                }
@@ -150,13 +163,16 @@ func (b *Reader) Read(p []byte) (n int, err error) {
                        // Large read, empty buffer.
                        // Read directly into p to avoid copy.
                        n, b.err = b.rd.Read(p)
+                       if n < 0 {
+                               panic(errNegativeRead)
+                       }
                        if n > 0 {
                                b.lastByte = int(p[n-1])
                                b.lastRuneSize = -1
                        }
                        return n, b.readErr()
                }
-               b.fill()
+               b.fill() // buffer is empty
                if b.w == b.r {
                        return 0, b.readErr()
                }
@@ -176,11 +192,11 @@ func (b *Reader) Read(p []byte) (n int, err error) {
 // If no byte is available, returns an error.
 func (b *Reader) ReadByte() (c byte, err error) {
        b.lastRuneSize = -1
-       for b.w == b.r {
+       for b.r == b.w {
                if b.err != nil {
                        return 0, b.readErr()
                }
-               b.fill()
+               b.fill() // buffer is empty
        }
        c = b.buf[b.r]
        b.r++
@@ -190,19 +206,19 @@ func (b *Reader) ReadByte() (c byte, err error) {
 
 // UnreadByte unreads the last byte.  Only the most recently read byte can be unread.
 func (b *Reader) UnreadByte() error {
-       b.lastRuneSize = -1
-       if b.r == b.w && b.lastByte >= 0 {
-               b.w = 1
-               b.r = 0
-               b.buf[0] = byte(b.lastByte)
-               b.lastByte = -1
-               return nil
-       }
-       if b.r <= 0 {
+       if b.lastByte < 0 || b.r == 0 && b.w > 0 {
                return ErrInvalidUnreadByte
        }
-       b.r--
+       // b.r > 0 || b.w == 0
+       if b.r > 0 {
+               b.r--
+       } else {
+               // b.r == 0 && b.w == 0
+               b.w = 1
+       }
+       b.buf[b.r] = byte(b.lastByte)
        b.lastByte = -1
+       b.lastRuneSize = -1
        return nil
 }
 
@@ -210,8 +226,8 @@ func (b *Reader) UnreadByte() error {
 // rune and its size in bytes. If the encoded rune is invalid, it consumes one byte
 // and returns unicode.ReplacementChar (U+FFFD) with a size of 1.
 func (b *Reader) ReadRune() (r rune, size int, err error) {
-       for b.r+utf8.UTFMax > b.w && !utf8.FullRune(b.buf[b.r:b.w]) && b.err == nil {
-               b.fill()
+       for b.r+utf8.UTFMax > b.w && !utf8.FullRune(b.buf[b.r:b.w]) && b.err == nil && b.w-b.r < len(b.buf) {
+               b.fill() // b.w-b.r < len(buf) => buffer is not full
        }
        b.lastRuneSize = -1
        if b.r == b.w {
@@ -232,7 +248,7 @@ func (b *Reader) ReadRune() (r rune, size int, err error) {
 // regard it is stricter than UnreadByte, which will unread the last byte
 // from any read operation.)
 func (b *Reader) UnreadRune() error {
-       if b.lastRuneSize < 0 || b.r == 0 {
+       if b.lastRuneSize < 0 || b.r < b.lastRuneSize {
                return ErrInvalidUnreadRune
        }
        b.r -= b.lastRuneSize
@@ -255,37 +271,39 @@ func (b *Reader) Buffered() int { return b.w - b.r }
 // ReadBytes or ReadString instead.
 // ReadSlice returns err != nil if and only if line does not end in delim.
 func (b *Reader) ReadSlice(delim byte) (line []byte, err error) {
-       // Look in buffer.
-       if i := bytes.IndexByte(b.buf[b.r:b.w], delim); i >= 0 {
-               line1 := b.buf[b.r : b.r+i+1]
-               b.r += i + 1
-               return line1, nil
-       }
-
-       // Read more into buffer, until buffer fills or we find delim.
        for {
-               if b.err != nil {
-                       line := b.buf[b.r:b.w]
-                       b.r = b.w
-                       return line, b.readErr()
+               // Search buffer.
+               if i := bytes.IndexByte(b.buf[b.r:b.w], delim); i >= 0 {
+                       line = b.buf[b.r : b.r+i+1]
+                       b.r += i + 1
+                       break
                }
 
-               n := b.Buffered()
-               b.fill()
-
-               // Search new part of buffer
-               if i := bytes.IndexByte(b.buf[n:b.w], delim); i >= 0 {
-                       line := b.buf[0 : n+i+1]
-                       b.r = n + i + 1
-                       return line, nil
+               // Pending error?
+               if b.err != nil {
+                       line = b.buf[b.r:b.w]
+                       b.r = b.w
+                       err = b.readErr()
+                       break
                }
 
-               // Buffer is full?
-               if b.Buffered() >= len(b.buf) {
+               // Buffer full?
+               if n := b.Buffered(); n >= len(b.buf) {
                        b.r = b.w
-                       return b.buf, ErrBufferFull
+                       line = b.buf
+                       err = ErrBufferFull
+                       break
                }
+
+               b.fill() // buffer is not full
        }
+
+       // Handle last byte, if any.
+       if i := len(line) - 1; i >= 0 {
+               b.lastByte = int(line[i])
+       }
+
+       return
 }
 
 // ReadLine is a low-level line-reading primitive. Most callers should use
@@ -301,6 +319,9 @@ func (b *Reader) ReadSlice(delim byte) (line []byte, err error) {
 //
 // 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.
+// Calling UnreadByte after ReadLine will always unread the last byte read
+// (possibly a character belonging to the line end) even if that byte is not
+// part of the line returned by ReadLine.
 func (b *Reader) ReadLine() (line []byte, isPrefix bool, err error) {
        line, err = b.ReadSlice('\n')
        if err == ErrBufferFull {
@@ -410,12 +431,24 @@ func (b *Reader) WriteTo(w io.Writer) (n int64, err error) {
                return n, err
        }
 
-       for b.fill(); b.r < b.w; b.fill() {
+       if w, ok := w.(io.ReaderFrom); ok {
+               m, err := w.ReadFrom(b.rd)
+               n += m
+               return n, err
+       }
+
+       if b.w-b.r < len(b.buf) {
+               b.fill() // buffer not full
+       }
+
+       for b.r < b.w {
+               // b.r < b.w => buffer is not empty
                m, err := b.writeBuf(w)
                n += m
                if err != nil {
                        return n, err
                }
+               b.fill() // buffer is empty
        }
 
        if b.err == io.EOF {
@@ -428,6 +461,9 @@ func (b *Reader) WriteTo(w io.Writer) (n int64, err error) {
 // writeBuf writes the Reader's buffer to the writer.
 func (b *Reader) writeBuf(w io.Writer) (int64, error) {
        n, err := w.Write(b.buf[b.r:b.w])
+       if n < b.r-b.w {
+               panic(errors.New("bufio: writer did not write all data"))
+       }
        b.r += n
        return int64(n), err
 }
@@ -619,9 +655,16 @@ func (b *Writer) ReadFrom(r io.Reader) (n int64, err error) {
                                return n, err1
                        }
                }
-               m, err = r.Read(b.buf[b.n:])
-               if m == 0 {
-                       break
+               nr := 0
+               for nr < maxConsecutiveEmptyReads {
+                       m, err = r.Read(b.buf[b.n:])
+                       if m != 0 || err != nil {
+                               break
+                       }
+                       nr++
+               }
+               if nr == maxConsecutiveEmptyReads {
+                       return n, io.ErrNoProgress
                }
                b.n += m
                n += int64(m)
index 3c86857e107c20be9b9120055f92afa031fd24f1..76d3c8eade814264adf5b46ed2f0ae48f838b28f 100644 (file)
@@ -14,6 +14,7 @@ import (
        "strings"
        "testing"
        "testing/iotest"
+       "time"
        "unicode/utf8"
 )
 
@@ -174,6 +175,34 @@ func TestReader(t *testing.T) {
        }
 }
 
+type zeroReader struct{}
+
+func (zeroReader) Read(p []byte) (int, error) {
+       return 0, nil
+}
+
+func TestZeroReader(t *testing.T) {
+       var z zeroReader
+       r := NewReader(z)
+
+       c := make(chan error)
+       go func() {
+               _, err := r.ReadByte()
+               c <- err
+       }()
+
+       select {
+       case err := <-c:
+               if err == nil {
+                       t.Error("error expected")
+               } else if err != io.ErrNoProgress {
+                       t.Error("unexpected error:", err)
+               }
+       case <-time.After(time.Second):
+               t.Error("test timed out (endless loop in ReadByte?)")
+       }
+}
+
 // A StringReader delivers its data one string segment at a time via Read.
 type StringReader struct {
        data []string
@@ -228,66 +257,150 @@ func TestReadRune(t *testing.T) {
 }
 
 func TestUnreadRune(t *testing.T) {
-       got := ""
        segments := []string{"Hello, world:", "日本語"}
-       data := strings.Join(segments, "")
        r := NewReader(&StringReader{data: segments})
+       got := ""
+       want := strings.Join(segments, "")
        // Normal execution.
        for {
                r1, _, err := r.ReadRune()
                if err != nil {
                        if err != io.EOF {
-                               t.Error("unexpected EOF")
+                               t.Error("unexpected error on ReadRune:", err)
                        }
                        break
                }
                got += string(r1)
-               // Put it back and read it again
+               // Put it back and read it again.
                if err = r.UnreadRune(); err != nil {
-                       t.Error("unexpected error on UnreadRune:", err)
+                       t.Fatal("unexpected error on UnreadRune:", err)
                }
                r2, _, err := r.ReadRune()
                if err != nil {
-                       t.Error("unexpected error reading after unreading:", err)
+                       t.Fatal("unexpected error reading after unreading:", err)
                }
                if r1 != r2 {
-                       t.Errorf("incorrect rune after unread: got %c wanted %c", r1, r2)
+                       t.Fatalf("incorrect rune after unread: got %c, want %c", r1, r2)
                }
        }
-       if got != data {
-               t.Errorf("want=%q got=%q", data, got)
+       if got != want {
+               t.Errorf("got %q, want %q", got, want)
        }
 }
 
 func TestUnreadByte(t *testing.T) {
-       want := "Hello, world"
-       got := ""
        segments := []string{"Hello, ", "world"}
        r := NewReader(&StringReader{data: segments})
+       got := ""
+       want := strings.Join(segments, "")
        // Normal execution.
        for {
                b1, err := r.ReadByte()
                if err != nil {
                        if err != io.EOF {
-                               t.Fatal("unexpected EOF")
+                               t.Error("unexpected error on ReadByte:", err)
                        }
                        break
                }
                got += string(b1)
-               // Put it back and read it again
+               // Put it back and read it again.
                if err = r.UnreadByte(); err != nil {
-                       t.Fatalf("unexpected error on UnreadByte: %v", err)
+                       t.Fatal("unexpected error on UnreadByte:", err)
                }
                b2, err := r.ReadByte()
                if err != nil {
-                       t.Fatalf("unexpected error reading after unreading: %v", err)
+                       t.Fatal("unexpected error reading after unreading:", err)
                }
                if b1 != b2 {
-                       t.Fatalf("incorrect byte after unread: got %c wanted %c", b1, b2)
+                       t.Fatalf("incorrect byte after unread: got %q, want %q", b1, b2)
                }
        }
        if got != want {
-               t.Errorf("got=%q want=%q", got, want)
+               t.Errorf("got %q, want %q", got, want)
+       }
+}
+
+func TestUnreadByteMultiple(t *testing.T) {
+       segments := []string{"Hello, ", "world"}
+       data := strings.Join(segments, "")
+       for n := 0; n <= len(data); n++ {
+               r := NewReader(&StringReader{data: segments})
+               // Read n bytes.
+               for i := 0; i < n; i++ {
+                       b, err := r.ReadByte()
+                       if err != nil {
+                               t.Fatalf("n = %d: unexpected error on ReadByte: %v", n, err)
+                       }
+                       if b != data[i] {
+                               t.Fatalf("n = %d: incorrect byte returned from ReadByte: got %q, want %q", n, b, data[i])
+                       }
+               }
+               // Unread one byte if there is one.
+               if n > 0 {
+                       if err := r.UnreadByte(); err != nil {
+                               t.Errorf("n = %d: unexpected error on UnreadByte: %v", n, err)
+                       }
+               }
+               // Test that we cannot unread any further.
+               if err := r.UnreadByte(); err == nil {
+                       t.Errorf("n = %d: expected error on UnreadByte", n)
+               }
+       }
+}
+
+func TestUnreadByteOthers(t *testing.T) {
+       // A list of readers to use in conjunction with UnreadByte.
+       var readers = []func(*Reader, byte) ([]byte, error){
+               (*Reader).ReadBytes,
+               (*Reader).ReadSlice,
+               func(r *Reader, delim byte) ([]byte, error) {
+                       data, err := r.ReadString(delim)
+                       return []byte(data), err
+               },
+               // ReadLine doesn't fit the data/pattern easily
+               // so we leave it out. It should be covered via
+               // the ReadSlice test since ReadLine simply calls
+               // ReadSlice, and it's that function that handles
+               // the last byte.
+       }
+
+       // Try all readers with UnreadByte.
+       for rno, read := range readers {
+               // Some input data that is longer than the minimum reader buffer size.
+               const n = 10
+               var buf bytes.Buffer
+               for i := 0; i < n; i++ {
+                       buf.WriteString("abcdefg")
+               }
+
+               r := NewReaderSize(&buf, minReadBufferSize)
+               readTo := func(delim byte, want string) {
+                       data, err := read(r, delim)
+                       if err != nil {
+                               t.Fatalf("#%d: unexpected error reading to %c: %v", rno, delim, err)
+                       }
+                       if got := string(data); got != want {
+                               t.Fatalf("#%d: got %q, want %q", rno, got, want)
+                       }
+               }
+
+               // Read the data with occasional UnreadByte calls.
+               for i := 0; i < n; i++ {
+                       readTo('d', "abcd")
+                       for j := 0; j < 3; j++ {
+                               if err := r.UnreadByte(); err != nil {
+                                       t.Fatalf("#%d: unexpected error on UnreadByte: %v", rno, err)
+                               }
+                               readTo('d', "d")
+                       }
+                       readTo('g', "efg")
+               }
+
+               // All data should have been read.
+               _, err := r.ReadByte()
+               if err != io.EOF {
+                       t.Errorf("#%d: got error %v; want EOF", rno, err)
+               }
        }
 }
 
@@ -1056,7 +1169,61 @@ func TestWriterReadFromWhileFull(t *testing.T) {
        // Use ReadFrom to read in some data.
        n2, err := w.ReadFrom(strings.NewReader("abcdef"))
        if n2 != 6 || err != nil {
-               t.Fatalf("ReadFrom returned (%v, %v), want (6, nil)", n, err)
+               t.Fatalf("ReadFrom returned (%v, %v), want (6, nil)", n2, err)
+       }
+}
+
+type emptyThenNonEmptyReader struct {
+       r io.Reader
+       n int
+}
+
+func (r *emptyThenNonEmptyReader) Read(p []byte) (int, error) {
+       if r.n <= 0 {
+               return r.r.Read(p)
+       }
+       r.n--
+       return 0, nil
+}
+
+// Test for golang.org/issue/7611
+func TestWriterReadFromUntilEOF(t *testing.T) {
+       buf := new(bytes.Buffer)
+       w := NewWriterSize(buf, 5)
+
+       // Partially fill buffer
+       n, err := w.Write([]byte("0123"))
+       if n != 4 || err != nil {
+               t.Fatalf("Write returned (%v, %v), want (4, nil)", n, err)
+       }
+
+       // Use ReadFrom to read in some data.
+       r := &emptyThenNonEmptyReader{r: strings.NewReader("abcd"), n: 3}
+       n2, err := w.ReadFrom(r)
+       if n2 != 4 || err != nil {
+               t.Fatalf("ReadFrom returned (%v, %v), want (4, nil)", n2, err)
+       }
+       w.Flush()
+       if got, want := string(buf.Bytes()), "0123abcd"; got != want {
+               t.Fatalf("buf.Bytes() returned %q, want %q", got, want)
+       }
+}
+
+func TestWriterReadFromErrNoProgress(t *testing.T) {
+       buf := new(bytes.Buffer)
+       w := NewWriterSize(buf, 5)
+
+       // Partially fill buffer
+       n, err := w.Write([]byte("0123"))
+       if n != 4 || err != nil {
+               t.Fatalf("Write returned (%v, %v), want (4, nil)", n, err)
+       }
+
+       // Use ReadFrom to read in some data.
+       r := &emptyThenNonEmptyReader{r: strings.NewReader("abcd"), n: 100}
+       n2, err := w.ReadFrom(r)
+       if n2 != 0 || err != io.ErrNoProgress {
+               t.Fatalf("buf.Bytes() returned (%v, %v), want (0, io.ErrNoProgress)", n2, err)
        }
 }
 
@@ -1094,20 +1261,12 @@ func TestWriterReset(t *testing.T) {
 
 // An onlyReader only implements io.Reader, no matter what other methods the underlying implementation may have.
 type onlyReader struct {
-       r io.Reader
-}
-
-func (r onlyReader) Read(b []byte) (int, error) {
-       return r.r.Read(b)
+       io.Reader
 }
 
 // An onlyWriter only implements io.Writer, no matter what other methods the underlying implementation may have.
 type onlyWriter struct {
-       w io.Writer
-}
-
-func (w onlyWriter) Write(b []byte) (int, error) {
-       return w.w.Write(b)
+       io.Writer
 }
 
 func BenchmarkReaderCopyOptimal(b *testing.B) {
@@ -1152,6 +1311,27 @@ func BenchmarkReaderCopyNoWriteTo(b *testing.B) {
        }
 }
 
+func BenchmarkReaderWriteToOptimal(b *testing.B) {
+       const bufSize = 16 << 10
+       buf := make([]byte, bufSize)
+       r := bytes.NewReader(buf)
+       srcReader := NewReaderSize(onlyReader{r}, 1<<10)
+       if _, ok := ioutil.Discard.(io.ReaderFrom); !ok {
+               b.Fatal("ioutil.Discard doesn't support ReaderFrom")
+       }
+       for i := 0; i < b.N; i++ {
+               r.Seek(0, 0)
+               srcReader.Reset(onlyReader{r})
+               n, err := srcReader.WriteTo(ioutil.Discard)
+               if err != nil {
+                       b.Fatal(err)
+               }
+               if n != bufSize {
+                       b.Fatalf("n = %d; want %d", n, bufSize)
+               }
+       }
+}
+
 func BenchmarkWriterCopyOptimal(b *testing.B) {
        // Optimal case is where the underlying writer implements io.ReaderFrom
        srcBuf := bytes.NewBuffer(make([]byte, 8192))
index 77b2c2ac6f8472fa55e56bb8600d17338aa32ea9..715ce071e3bb8113595620067ae11ee7a8834b99 100644 (file)
@@ -135,7 +135,7 @@ func (s *Scanner) Scan() bool {
                }
                // Must read more data.
                // First, shift data to beginning of buffer if there's lots of empty space
-               // or space is neded.
+               // or space is needed.
                if s.start > 0 && (s.end == len(s.buf) || s.start > len(s.buf)/2) {
                        copy(s.buf, s.buf[s.start:s.end])
                        s.end -= s.start
@@ -172,7 +172,7 @@ func (s *Scanner) Scan() bool {
                                break
                        }
                        loop++
-                       if loop > 100 {
+                       if loop > maxConsecutiveEmptyReads {
                                s.setErr(io.ErrNoProgress)
                                break
                        }
index 4ac529fd6dd71ea797e13bfd4383424bd8201081..0db7cad2047545926756ac573a6720d5e37b275b 100644 (file)
@@ -277,7 +277,7 @@ func TestScanLineNoNewline(t *testing.T) {
        testNoNewline(text, lines, t)
 }
 
-// Test that the line splitter handles a final line with a carriage return but nonewline.
+// Test that the line splitter handles a final line with a carriage return but no newline.
 func TestScanLineReturnButNoNewline(t *testing.T) {
        const text = "abcdefghijklmn\nopqrstuvwxyz\r"
        lines := []string{
index 644bf75b894336ea9f7dae7c60ecd73e840d15e4..0c53e4c0b71262b7dc015cb185ce1d486d8dd841 100644 (file)
@@ -356,7 +356,11 @@ func Map(mapping func(r rune) rune, s []byte) []byte {
                }
                r = mapping(r)
                if r >= 0 {
-                       if nbytes+utf8.RuneLen(r) > maxbytes {
+                       rl := utf8.RuneLen(r)
+                       if rl < 0 {
+                               rl = len(string(utf8.RuneError))
+                       }
+                       if nbytes+rl > maxbytes {
                                // Grow the buffer.
                                maxbytes = maxbytes*2 + utf8.UTFMax
                                nb := make([]byte, maxbytes)
index 808655a4a48622c9d4145c6278cdb46937a47a0e..394dd7a443d93b29dbd6b491abd94863a28f1666 100644 (file)
@@ -785,6 +785,16 @@ func TestMap(t *testing.T) {
        if string(m) != expect {
                t.Errorf("drop: expected %q got %q", expect, m)
        }
+
+       // 6. Invalid rune
+       invalidRune := func(r rune) rune {
+               return utf8.MaxRune + 1
+       }
+       m = Map(invalidRune, []byte("x"))
+       expect = "\uFFFD"
+       if string(m) != expect {
+               t.Errorf("invalidRune: expected %q got %q", expect, m)
+       }
 }
 
 func TestToUpper(t *testing.T) { runStringTests(t, ToUpper, "ToUpper", upperTests) }
@@ -1134,7 +1144,7 @@ func TestEqualFold(t *testing.T) {
 func TestBufferGrowNegative(t *testing.T) {
        defer func() {
                if err := recover(); err == nil {
-                       t.Fatal("Grow(-1) should have paniced")
+                       t.Fatal("Grow(-1) should have panicked")
                }
        }()
        var b Buffer
@@ -1144,7 +1154,7 @@ func TestBufferGrowNegative(t *testing.T) {
 func TestBufferTruncateNegative(t *testing.T) {
        defer func() {
                if err := recover(); err == nil {
-                       t.Fatal("Truncate(-1) should have paniced")
+                       t.Fatal("Truncate(-1) should have panicked")
                }
        }()
        var b Buffer
@@ -1154,7 +1164,7 @@ func TestBufferTruncateNegative(t *testing.T) {
 func TestBufferTruncateOutOfRange(t *testing.T) {
        defer func() {
                if err := recover(); err == nil {
-                       t.Fatal("Truncate(20) should have paniced")
+                       t.Fatal("Truncate(20) should have panicked")
                }
        }()
        var b Buffer
index 77511b94555634d1ab934e3bd692802351eebce1..d2d40fa7ca1470faf3186ee731fe824ce329b2f4 100644 (file)
@@ -16,40 +16,41 @@ import (
 // Unlike a Buffer, a Reader is read-only and supports seeking.
 type Reader struct {
        s        []byte
-       i        int // current reading index
-       prevRune int // index of previous rune; or < 0
+       i        int64 // current reading index
+       prevRune int   // index of previous rune; or < 0
 }
 
 // Len returns the number of bytes of the unread portion of the
 // slice.
 func (r *Reader) Len() int {
-       if r.i >= len(r.s) {
+       if r.i >= int64(len(r.s)) {
                return 0
        }
-       return len(r.s) - r.i
+       return int(int64(len(r.s)) - r.i)
 }
 
 func (r *Reader) Read(b []byte) (n int, err error) {
        if len(b) == 0 {
                return 0, nil
        }
-       if r.i >= len(r.s) {
+       if r.i >= int64(len(r.s)) {
                return 0, io.EOF
        }
-       n = copy(b, r.s[r.i:])
-       r.i += n
        r.prevRune = -1
+       n = copy(b, r.s[r.i:])
+       r.i += int64(n)
        return
 }
 
 func (r *Reader) ReadAt(b []byte, off int64) (n int, err error) {
+       // cannot modify state - see io.ReaderAt
        if off < 0 {
-               return 0, errors.New("bytes: invalid offset")
+               return 0, errors.New("bytes.Reader.ReadAt: negative offset")
        }
        if off >= int64(len(r.s)) {
                return 0, io.EOF
        }
-       n = copy(b, r.s[int(off):])
+       n = copy(b, r.s[off:])
        if n < len(b) {
                err = io.EOF
        }
@@ -57,49 +58,51 @@ func (r *Reader) ReadAt(b []byte, off int64) (n int, err error) {
 }
 
 func (r *Reader) ReadByte() (b byte, err error) {
-       if r.i >= len(r.s) {
+       r.prevRune = -1
+       if r.i >= int64(len(r.s)) {
                return 0, io.EOF
        }
        b = r.s[r.i]
        r.i++
-       r.prevRune = -1
        return
 }
 
 func (r *Reader) UnreadByte() error {
+       r.prevRune = -1
        if r.i <= 0 {
-               return errors.New("bytes.Reader: at beginning of slice")
+               return errors.New("bytes.Reader.UnreadByte: at beginning of slice")
        }
        r.i--
-       r.prevRune = -1
        return nil
 }
 
 func (r *Reader) ReadRune() (ch rune, size int, err error) {
-       if r.i >= len(r.s) {
+       if r.i >= int64(len(r.s)) {
+               r.prevRune = -1
                return 0, 0, io.EOF
        }
-       r.prevRune = r.i
+       r.prevRune = int(r.i)
        if c := r.s[r.i]; c < utf8.RuneSelf {
                r.i++
                return rune(c), 1, nil
        }
        ch, size = utf8.DecodeRune(r.s[r.i:])
-       r.i += size
+       r.i += int64(size)
        return
 }
 
 func (r *Reader) UnreadRune() error {
        if r.prevRune < 0 {
-               return errors.New("bytes.Reader: previous operation was not ReadRune")
+               return errors.New("bytes.Reader.UnreadRune: previous operation was not ReadRune")
        }
-       r.i = r.prevRune
+       r.i = int64(r.prevRune)
        r.prevRune = -1
        return nil
 }
 
 // Seek implements the io.Seeker interface.
 func (r *Reader) Seek(offset int64, whence int) (int64, error) {
+       r.prevRune = -1
        var abs int64
        switch whence {
        case 0:
@@ -109,22 +112,19 @@ func (r *Reader) Seek(offset int64, whence int) (int64, error) {
        case 2:
                abs = int64(len(r.s)) + offset
        default:
-               return 0, errors.New("bytes: invalid whence")
+               return 0, errors.New("bytes.Reader.Seek: invalid whence")
        }
        if abs < 0 {
-               return 0, errors.New("bytes: negative position")
-       }
-       if abs >= 1<<31 {
-               return 0, errors.New("bytes: position out of range")
+               return 0, errors.New("bytes.Reader.Seek: negative position")
        }
-       r.i = int(abs)
+       r.i = abs
        return abs, nil
 }
 
 // WriteTo implements the io.WriterTo interface.
 func (r *Reader) WriteTo(w io.Writer) (n int64, err error) {
        r.prevRune = -1
-       if r.i >= len(r.s) {
+       if r.i >= int64(len(r.s)) {
                return 0, nil
        }
        b := r.s[r.i:]
@@ -132,7 +132,7 @@ func (r *Reader) WriteTo(w io.Writer) (n int64, err error) {
        if m > len(b) {
                panic("bytes.Reader.WriteTo: invalid Write count")
        }
-       r.i += m
+       r.i += int64(m)
        n = int64(m)
        if m != len(b) && err == nil {
                err = io.ErrShortWrite
index 19f014da030aa4162ad0b4efa1658aecbe167b71..d3dce53499eda631ea145a49f69113895c3a7d89 100644 (file)
@@ -10,6 +10,7 @@ import (
        "io"
        "io/ioutil"
        "os"
+       "sync"
        "testing"
 )
 
@@ -26,9 +27,9 @@ func TestReader(t *testing.T) {
                {seek: os.SEEK_SET, off: 0, n: 20, want: "0123456789"},
                {seek: os.SEEK_SET, off: 1, n: 1, want: "1"},
                {seek: os.SEEK_CUR, off: 1, wantpos: 3, n: 2, want: "34"},
-               {seek: os.SEEK_SET, off: -1, seekerr: "bytes: negative position"},
-               {seek: os.SEEK_SET, off: 1<<31 - 1},
-               {seek: os.SEEK_CUR, off: 1, seekerr: "bytes: position out of range"},
+               {seek: os.SEEK_SET, off: -1, seekerr: "bytes.Reader.Seek: negative position"},
+               {seek: os.SEEK_SET, off: 1 << 33, wantpos: 1 << 33},
+               {seek: os.SEEK_CUR, off: 1, wantpos: 1<<33 + 1},
                {seek: os.SEEK_SET, n: 5, want: "01234"},
                {seek: os.SEEK_CUR, n: 5, want: "56789"},
                {seek: os.SEEK_END, off: -1, n: 1, wantpos: 9, want: "9"},
@@ -60,6 +61,16 @@ func TestReader(t *testing.T) {
        }
 }
 
+func TestReadAfterBigSeek(t *testing.T) {
+       r := NewReader([]byte("0123456789"))
+       if _, err := r.Seek(1<<31+5, os.SEEK_SET); err != nil {
+               t.Fatal(err)
+       }
+       if n, err := r.Read(make([]byte, 10)); n != 0 || err != io.EOF {
+               t.Errorf("Read = %d, %v; want 0, EOF", n, err)
+       }
+}
+
 func TestReaderAt(t *testing.T) {
        r := NewReader([]byte("0123456789"))
        tests := []struct {
@@ -73,7 +84,7 @@ func TestReaderAt(t *testing.T) {
                {1, 9, "123456789", nil},
                {11, 10, "", io.EOF},
                {0, 0, "", nil},
-               {-1, 0, "", "bytes: invalid offset"},
+               {-1, 0, "", "bytes.Reader.ReadAt: negative offset"},
        }
        for i, tt := range tests {
                b := make([]byte, tt.n)
@@ -88,6 +99,43 @@ func TestReaderAt(t *testing.T) {
        }
 }
 
+func TestReaderAtConcurrent(t *testing.T) {
+       // Test for the race detector, to verify ReadAt doesn't mutate
+       // any state.
+       r := NewReader([]byte("0123456789"))
+       var wg sync.WaitGroup
+       for i := 0; i < 5; i++ {
+               wg.Add(1)
+               go func(i int) {
+                       defer wg.Done()
+                       var buf [1]byte
+                       r.ReadAt(buf[:], int64(i))
+               }(i)
+       }
+       wg.Wait()
+}
+
+func TestEmptyReaderConcurrent(t *testing.T) {
+       // Test for the race detector, to verify a Read that doesn't yield any bytes
+       // is okay to use from multiple goroutines. This was our historic behavior.
+       // See golang.org/issue/7856
+       r := NewReader([]byte{})
+       var wg sync.WaitGroup
+       for i := 0; i < 5; i++ {
+               wg.Add(2)
+               go func() {
+                       defer wg.Done()
+                       var buf [1]byte
+                       r.Read(buf[:])
+               }()
+               go func() {
+                       defer wg.Done()
+                       r.Read(nil)
+               }()
+       }
+       wg.Wait()
+}
+
 func TestReaderWriteTo(t *testing.T) {
        for i := 0; i < 30; i += 3 {
                var l int
@@ -133,6 +181,32 @@ func TestReaderLen(t *testing.T) {
        }
 }
 
+var UnreadRuneErrorTests = []struct {
+       name string
+       f    func(*Reader)
+}{
+       {"Read", func(r *Reader) { r.Read([]byte{0}) }},
+       {"ReadByte", func(r *Reader) { r.ReadByte() }},
+       {"UnreadRune", func(r *Reader) { r.UnreadRune() }},
+       {"Seek", func(r *Reader) { r.Seek(0, 1) }},
+       {"WriteTo", func(r *Reader) { r.WriteTo(&Buffer{}) }},
+}
+
+func TestUnreadRuneError(t *testing.T) {
+       for _, tt := range UnreadRuneErrorTests {
+               reader := NewReader([]byte("0123456789"))
+               if _, _, err := reader.ReadRune(); err != nil {
+                       // should not happen
+                       t.Fatal(err)
+               }
+               tt.f(reader)
+               err := reader.UnreadRune()
+               if err == nil {
+                       t.Errorf("Unreading after %s: expected error", tt.name)
+               }
+       }
+}
+
 func TestReaderDoubleUnreadRune(t *testing.T) {
        buf := NewBuffer([]byte("groucho"))
        if _, _, err := buf.ReadRune(); err != nil {
index cd647e5ae0048031b17d2f2331cf969a24475ada..727249dc46254cd5960efc6e9aeacf1c96b09fef 100644 (file)
@@ -177,7 +177,7 @@ const (
 
 var testfiles = []string{
        // Digits is the digits of the irrational number e. Its decimal representation
-       // does not repeat, but there are only 10 posible digits, so it should be
+       // does not repeat, but there are only 10 possible digits, so it should be
        // reasonably compressible.
        digits: "testdata/e.txt.bz2",
        // Twain is Project Gutenberg's edition of Mark Twain's classic English novel.
index bbe4c5a16fbc9aad762409c610d8233e0dd1f494..ce4923eca3738f6a00ed0ce99439534dafe0bf14 100644 (file)
@@ -54,7 +54,7 @@ func (e *WriteError) Error() string {
        return "flate: write error at offset " + strconv.FormatInt(e.Offset, 10) + ": " + e.Err.Error()
 }
 
-// Note that much of the implemenation of huffmanDecoder is also copied
+// Note that much of the implementation of huffmanDecoder is also copied
 // into gen.go (in package main) for the purpose of precomputing the
 // fixed huffman tables so they can be included statically.
 
index 2a8ebbc9438d5952eba84d2130c1f8c4970de7d0..a62ef741df336161397cc132cc4bec2ca656cfc1 100644 (file)
@@ -29,7 +29,7 @@ const (
 
 var testfiles = []string{
        // Digits is the digits of the irrational number e. Its decimal representation
-       // does not repeat, but there are only 10 posible digits, so it should be
+       // does not repeat, but there are only 10 possible digits, so it should be
        // reasonably compressible.
        digits: "../testdata/e.txt",
        // Twain is Project Gutenberg's edition of Mark Twain's classic English novel.
index 1fb9b0964ce33399f768da7435f7db0cfc29eb62..4f398b194a06b3fd96146696cf1d10c7849c96cf 100644 (file)
@@ -89,6 +89,21 @@ func NewReader(r io.Reader) (*Reader, error) {
        return z, nil
 }
 
+// Reset discards the Reader z's state and makes it equivalent to the
+// result of its original state from NewReader, but reading from r instead.
+// This permits reusing a Reader rather than allocating a new one.
+func (z *Reader) Reset(r io.Reader) error {
+       z.r = makeReader(r)
+       if z.digest == nil {
+               z.digest = crc32.NewIEEE()
+       } else {
+               z.digest.Reset()
+       }
+       z.size = 0
+       z.err = nil
+       return z.readHeader(true)
+}
+
 // GZIP (RFC 1952) is little-endian, unlike ZLIB (RFC 1950).
 func get4(p []byte) uint32 {
        return uint32(p[0]) | uint32(p[1])<<8 | uint32(p[2])<<16 | uint32(p[3])<<24
index 561537373770fdb5713aa151dba523ba008dca36..2471038f53690476f06db1ce68c62e805e7a67d7 100644 (file)
@@ -303,6 +303,26 @@ func TestDecompressor(t *testing.T) {
                if s != tt.raw {
                        t.Errorf("%s: got %d-byte %q want %d-byte %q", tt.name, n, s, len(tt.raw), tt.raw)
                }
+
+               // Test Reader Reset.
+               in = bytes.NewReader(tt.gzip)
+               err = gzip.Reset(in)
+               if err != nil {
+                       t.Errorf("%s: Reset: %s", tt.name, err)
+                       continue
+               }
+               if tt.name != gzip.Name {
+                       t.Errorf("%s: got name %s", tt.name, gzip.Name)
+               }
+               b.Reset()
+               n, err = io.Copy(b, gzip)
+               if err != tt.err {
+                       t.Errorf("%s: io.Copy: %v want %v", tt.name, err, tt.err)
+               }
+               s = b.String()
+               if s != tt.raw {
+                       t.Errorf("%s: got %d-byte %q want %d-byte %q", tt.name, n, s, len(tt.raw), tt.raw)
+               }
        }
 }
 
@@ -333,3 +353,17 @@ func TestIssue6550(t *testing.T) {
                // ok
        }
 }
+
+func TestInitialReset(t *testing.T) {
+       var r Reader
+       if err := r.Reset(bytes.NewReader(gunzipTests[1].gzip)); err != nil {
+               t.Error(err)
+       }
+       var buf bytes.Buffer
+       if _, err := io.Copy(&buf, &r); err != nil {
+               t.Error(err)
+       }
+       if s := buf.String(); s != gunzipTests[1].raw {
+               t.Errorf("got %q want %q", s, gunzipTests[1].raw)
+       }
+}
index fe32d6871ae950985b1775c354745fca73f16048..3a0bf54e1b978bf789713536a032d2b76a65275d 100644 (file)
@@ -22,8 +22,8 @@ const (
        DefaultCompression = flate.DefaultCompression
 )
 
-// A Writer is an io.WriteCloser that satisfies writes by compressing data written
-// to its wrapped io.Writer.
+// A Writer is an io.WriteCloser.
+// Writes to a Writer are compressed and written to w.
 type Writer struct {
        Header
        w           io.Writer
@@ -37,8 +37,8 @@ type Writer struct {
        err         error
 }
 
-// NewWriter creates a new Writer that satisfies writes by compressing data
-// written to w.
+// NewWriter returns a new Writer.
+// Writes to the returned writer are compressed and written to w.
 //
 // It is the caller's responsibility to call Close on the WriteCloser when done.
 // Writes may be buffered and not flushed until Close.
index efbc758f94b8d0b80e1c02d64248eea210563c07..ef596991032f4bb14ed0b7cd60e58e53078d5480 100644 (file)
@@ -216,8 +216,8 @@ func (d *decoder) Close() error {
        return nil
 }
 
-// NewReader creates a new io.ReadCloser that satisfies reads by decompressing
-// the data read from r.
+// NewReader creates a new io.ReadCloser.
+// Reads from the returned io.ReadCloser read and decompress data from r.
 // It is the caller's responsibility to call Close on the ReadCloser when
 // finished reading.
 // The number of bits to use for literal codes, litWidth, must be in the
index b20691864b586d45aac9eba1224e21cd5a89bd16..961b25f94f5fa6d03bcbdd549534548d69486fcb 100644 (file)
@@ -225,8 +225,8 @@ func (e *encoder) Close() error {
        return e.w.Flush()
 }
 
-// NewWriter creates a new io.WriteCloser that satisfies writes by compressing
-// the data and writing it to w.
+// NewWriter creates a new io.WriteCloser.
+// Writes to the returned io.WriteCloser are compressed and written to w.
 // It is the caller's responsibility to call Close on the WriteCloser when
 // finished writing.
 // The number of bits to use for literal codes, litWidth, must be in the
index d54746f4c02418971619ae83cc4a627e3b3b4cbf..9e1aafda9b61e4a9a7a6a4a1d458fc3871275937 100644 (file)
@@ -51,7 +51,8 @@ type reader struct {
        scratch      [4]byte
 }
 
-// NewReader creates a new io.ReadCloser that satisfies reads by decompressing data read from r.
+// NewReader creates a new io.ReadCloser.
+// Reads from the returned io.ReadCloser read and decompress data from r.
 // The implementation buffers input and may read more data than necessary from r.
 // It is the caller's responsibility to call Close on the ReadCloser when done.
 func NewReader(r io.Reader) (io.ReadCloser, error) {
index 99ff6549acb751494fdc9a649cbabb485bc231f2..fac7e15a7e3a04a3f8f33a0e6976a806fa31cd76 100644 (file)
@@ -34,8 +34,8 @@ type Writer struct {
        wroteHeader bool
 }
 
-// NewWriter creates a new Writer that satisfies writes by compressing data
-// written to w.
+// NewWriter creates a new Writer.
+// Writes to the returned Writer are compressed and written to w.
 //
 // It is the caller's responsibility to call Close on the WriteCloser when done.
 // Writes may be buffered and not flushed until Close.
index cf9c832545554639bf1ec81384ca5bdf0bd90ce0..71ba81aaa764b5a471ae06a9b903692b4f3c1a1f 100644 (file)
@@ -120,7 +120,7 @@ func testFileLevelDictReset(t *testing.T, fn string, level int, dict []byte) {
        }
        out := buf.String()
 
-       // Reset and comprses again.
+       // Reset and compress again.
        buf2 := new(bytes.Buffer)
        zlibw.Reset(buf2)
        _, err = zlibw.Write(b0)
index 3fe2327ad2dab76673edc1b63e6a1eb07d5b9d6c..c467a11910c9f5e395d30c75c7078fbdcf53c574 100644 (file)
@@ -78,7 +78,7 @@ func Remove(h Interface, i int) interface{} {
        return h.Pop()
 }
 
-// Fix reestablishes the heap ordering after the element at index i has changed its value.
+// Fix re-establishes the heap ordering after the element at index i has changed its value.
 // Changing the value of the element at index i and then calling Fix is equivalent to,
 // but less expensive than, calling Remove(h, i) followed by a Push of the new value.
 // The complexity is O(log(n)) where n = h.Len().
index 6261dd09fb5e3d230e6a7c663dbc4bb31f51c38a..363180931c77182c73e620bcc51557d3881066d8 100644 (file)
@@ -354,6 +354,34 @@ func TestCipherDecrypt(t *testing.T) {
        }
 }
 
+// Test short input/output.
+// Assembly used to not notice.
+// See issue 7928.
+func TestShortBlocks(t *testing.T) {
+       bytes := func(n int) []byte { return make([]byte, n) }
+
+       c, _ := NewCipher(bytes(16))
+
+       mustPanic(t, "crypto/aes: input not full block", func() { c.Encrypt(bytes(1), bytes(1)) })
+       mustPanic(t, "crypto/aes: input not full block", func() { c.Decrypt(bytes(1), bytes(1)) })
+       mustPanic(t, "crypto/aes: input not full block", func() { c.Encrypt(bytes(100), bytes(1)) })
+       mustPanic(t, "crypto/aes: input not full block", func() { c.Decrypt(bytes(100), bytes(1)) })
+       mustPanic(t, "crypto/aes: output not full block", func() { c.Encrypt(bytes(1), bytes(100)) })
+       mustPanic(t, "crypto/aes: output not full block", func() { c.Decrypt(bytes(1), bytes(100)) })
+}
+
+func mustPanic(t *testing.T, msg string, f func()) {
+       defer func() {
+               err := recover()
+               if err == nil {
+                       t.Errorf("function did not panic, wanted %q", msg)
+               } else if err != msg {
+                       t.Errorf("got panic %v, wanted %q", err, msg)
+               }
+       }()
+       f()
+}
+
 func BenchmarkEncrypt(b *testing.B) {
        tt := encryptTests[0]
        c, err := NewCipher(tt.key)
index d931134a70ef176cc545ab03026f993ddc2c3db9..2c6bb0a89c7b753359f862081c0f858879b7c99b 100644 (file)
@@ -46,9 +46,21 @@ func NewCipher(key []byte) (cipher.Block, error) {
 func (c *aesCipher) BlockSize() int { return BlockSize }
 
 func (c *aesCipher) Encrypt(dst, src []byte) {
+       if len(src) < BlockSize {
+               panic("crypto/aes: input not full block")
+       }
+       if len(dst) < BlockSize {
+               panic("crypto/aes: output not full block")
+       }
        encryptBlock(c.enc, dst, src)
 }
 
 func (c *aesCipher) Decrypt(dst, src []byte) {
+       if len(src) < BlockSize {
+               panic("crypto/aes: input not full block")
+       }
+       if len(dst) < BlockSize {
+               panic("crypto/aes: output not full block")
+       }
        decryptBlock(c.dec, dst, src)
 }
index 21369fc382c43462d5c6c3d20585547bf355ccec..964eaaa6f886f9f474be72e1832fa5a7c95d7e1f 100644 (file)
@@ -21,6 +21,7 @@ func encryptBlock(xk []uint32, dst, src []byte) {
                encryptBlockGo(xk, dst, src)
        }
 }
+
 func decryptBlock(xk []uint32, dst, src []byte) {
        if useAsm {
                decryptBlockAsm(len(xk)/4-1, &xk[0], &dst[0], &src[0])
@@ -28,6 +29,7 @@ func decryptBlock(xk []uint32, dst, src []byte) {
                decryptBlockGo(xk, dst, src)
        }
 }
+
 func expandKey(key []byte, enc, dec []uint32) {
        if useAsm {
                rounds := 10
index 0b173a4f3f228710c832efb9b22a44ff3c1f6194..027b24851055080af962b010609e510d7c7c8b36 100644 (file)
@@ -47,7 +47,7 @@ func BenchmarkAESGCMOpen1K(b *testing.B) {
 }
 
 // If we test exactly 1K blocks, we would generate exact multiples of
-// the cipher's block size, and and the cipher stream fragments would
+// the cipher's block size, and the cipher stream fragments would
 // always be wordsize aligned, whereas non-aligned is a more typical
 // use-case.
 const almost1K = 1024 - 5
index 2f748f02f7ca8a4498d98633cb464d2819c836bf..bdafd85fc30db2f7afbdb9a919d7af39cd3037b3 100644 (file)
@@ -30,9 +30,9 @@ type AEAD interface {
 
        // Open decrypts and authenticates ciphertext, authenticates the
        // additional data and, if successful, appends the resulting plaintext
-       // to dst, returning the updated slice and true. On error, nil and
-       // false is returned. The nonce must be NonceSize() bytes long and both
-       // it and the additional data must match the value passed to Seal.
+       // to dst, returning the updated slice. The nonce must be NonceSize()
+       // bytes long and both it and the additional data must match the
+       // value passed to Seal.
        //
        // The ciphertext and dst may alias exactly or not at all.
        Open(dst, nonce, ciphertext, data []byte) ([]byte, error)
index 5a2a65744ed301e79bbf83653cf9601b6217966f..b7565a61b0293899a9fd8e722f6d66fafb3471eb 100644 (file)
@@ -173,6 +173,16 @@ func GenerateKey(priv *PrivateKey, rand io.Reader) error {
        return nil
 }
 
+// fermatInverse calculates the inverse of k in GF(P) using Fermat's method.
+// This has better constant-time properties than Euclid's method (implemented
+// in math/big.Int.ModInverse) although math/big itself isn't strictly
+// constant-time so it's not perfect.
+func fermatInverse(k, P *big.Int) *big.Int {
+       two := big.NewInt(2)
+       pMinus2 := new(big.Int).Sub(P, two)
+       return new(big.Int).Exp(k, pMinus2, P)
+}
+
 // Sign signs an arbitrary length hash (which should be the result of hashing a
 // larger message) using the private key, priv. It returns the signature as a
 // pair of integers. The security of the private key depends on the entropy of
@@ -205,7 +215,7 @@ func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err err
                        }
                }
 
-               kInv := new(big.Int).ModInverse(k, priv.Q)
+               kInv := fermatInverse(k, priv.Q)
 
                r = new(big.Int).Exp(priv.G, k, priv.P)
                r.Mod(r, priv.Q)
index d02f15c34d9ab8eab9f2016e6a011c052eec6211..1bec7437a5368d1a3bf61a5c888dce95e012fbe2 100644 (file)
@@ -84,6 +84,16 @@ func hashToInt(hash []byte, c elliptic.Curve) *big.Int {
        return ret
 }
 
+// fermatInverse calculates the inverse of k in GF(P) using Fermat's method.
+// This has better constant-time properties than Euclid's method (implemented
+// in math/big.Int.ModInverse) although math/big itself isn't strictly
+// constant-time so it's not perfect.
+func fermatInverse(k, N *big.Int) *big.Int {
+       two := big.NewInt(2)
+       nMinus2 := new(big.Int).Sub(N, two)
+       return new(big.Int).Exp(k, nMinus2, N)
+}
+
 // Sign signs an arbitrary length hash (which should be the result of hashing a
 // larger message) using the private key, priv. It returns the signature as a
 // pair of integers. The security of the private key depends on the entropy of
@@ -102,7 +112,7 @@ func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err err
                                return
                        }
 
-                       kInv = new(big.Int).ModInverse(k, N)
+                       kInv = fermatInverse(k, N)
                        r, _ = priv.Curve.ScalarBaseMult(k.Bytes())
                        r.Mod(r, N)
                        if r.Sign() != 0 {
index c4d6aaaf03a1e33b46feac4bfda30c3639ae69f1..d7956a6d203ee753bd3197650cc6a1aab9b0bde7 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.
 
-// +build amd64 386 arm
+// +build amd64 amd64p32 386 arm
 
 package md5
 
index 239bf4d2152b2f6b5a541bd1300abecabd7b3435..263463e51cdc9cc538287e94e07474da5867ac82 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.
 
-// +build !amd64,!386,!arm
+// +build !amd64,!amd64p32,!386,!arm
 
 package md5
 
index 0fbd7eaf5794bab6492f4e84d894df833cf42bf6..1e741fda193b111eaa421f200291677538560439 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.
 
-// +build darwin dragonfly freebsd linux netbsd openbsd plan9 solaris
+// +build darwin dragonfly freebsd linux nacl netbsd openbsd plan9 solaris
 
 // Unix cryptographically secure pseudorandom number
 // generator.
index c582a4488b85d98c09e3c5373fe2304659cbce6a..fc71b9a6fa2f50199eb95a4f3e361b500a82f009 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.
 
-// +build amd64 arm 386
+// +build amd64 amd64p32 arm 386
 
 package rc4
 
index bdf5e1db2ddf127801d7b182bacf0e08d726adf6..1ecce1a7fbcd457ed0a0be54bc39536937debebb 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.
 
-// +build !amd64,!arm,!386
+// +build !amd64,!amd64p32,!arm,!386
 
 package rc4
 
index cf174b6a21dbe8d4bd6b2e92bebc3d25f68158d8..d9957aec1d64f0f8b3ed72aeb1ae4f012124f325 100644 (file)
@@ -214,7 +214,7 @@ func SignPKCS1v15(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed []b
 // hashed is the result of hashing the input message using the given hash
 // function and sig is the signature. A valid signature is indicated by
 // returning a nil error. If hash is zero then hashed is used directly. This
-// isn't advisable except for interopability.
+// isn't advisable except for interoperability.
 func VerifyPKCS1v15(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte) (err error) {
        hashLen, prefix, err := pkcs1v15HashInfo(hash, len(hashed))
        if err != nil {
index f9abec39490052209649619c53ef4935b5314d80..18eafbc05f7cf547b4339fbe1f1840d0ae7613c8 100644 (file)
@@ -4,7 +4,7 @@
 
 package rsa
 
-// This file implementes the PSS signature scheme [1].
+// This file implements the PSS signature scheme [1].
 //
 // [1] http://www.rsa.com/rsalabs/pkcs/files/h11300-wp-pkcs-1v2-2-rsa-cryptography-standard.pdf
 
@@ -189,7 +189,7 @@ func emsaPSSVerify(mHash, em []byte, emBits, sLen int, hash hash.Hash) error {
 
 // signPSSWithSalt calculates the signature of hashed using PSS [1] with specified salt.
 // Note that hashed must be the result of hashing the input message using the
-// given hash funcion. salt is a random sequence of bytes whose length will be
+// given hash function. salt is a random sequence of bytes whose length will be
 // later used to verify the signature.
 func signPSSWithSalt(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed, salt []byte) (s []byte, err error) {
        nBits := priv.N.BitLen()
@@ -233,7 +233,7 @@ func (opts *PSSOptions) saltLength() int {
 
 // SignPSS calculates the signature of hashed using RSASSA-PSS [1].
 // Note that hashed must be the result of hashing the input message using the
-// given hash funcion. The opts argument may be nil, in which case sensible
+// given hash function. The opts argument may be nil, in which case sensible
 // defaults are used.
 func SignPSS(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed []byte, opts *PSSOptions) (s []byte, err error) {
        saltLength := opts.saltLength()
index c8f1febe64705e2306c794036c7c64045d371050..bce6ba4eba31121edfb394f6112b23500461ac14 100644 (file)
@@ -60,7 +60,7 @@ type PrivateKey struct {
 
 type PrecomputedValues struct {
        Dp, Dq *big.Int // D mod (P-1) (or mod Q-1)
-       Qinv   *big.Int // Q^-1 mod Q
+       Qinv   *big.Int // Q^-1 mod P
 
        // CRTValues is used for the 3rd and subsequent primes. Due to a
        // historical accident, the CRT for the first two primes is handled
index b2c68f0e8baaf2f1d65d18f278530e4ebe1afea4..24e521af1fcbcf0278be93a0226cd10a90dbf16f 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.
 
-// +build amd64 386 arm
+// +build amd64 amd64p32 arm 386
 
 package sha1
 
index 2c78683aa49c9e2cd31486479fc8728e889fa3a3..696e26b6257fdfa411f7bc7fc8dd50cc1f10f604 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.
 
-// +build !amd64,!386,!arm
+// +build !amd64,!amd64p32,!386,!arm
 
 package sha1
 
index 7ce2077de41aaf70b3f687c63f0414e435f8bf7d..fca98bdd11c05e55de68cf482ecdda4696c28f78 100644 (file)
@@ -82,12 +82,14 @@ const (
        scsvRenegotiation uint16 = 0x00ff
 )
 
-// TLS Elliptic Curves
+// CurveID is the type of a TLS identifier for an elliptic curve. See
 // http://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-8
+type CurveID uint16
+
 const (
-       curveP256 uint16 = 23
-       curveP384 uint16 = 24
-       curveP521 uint16 = 25
+       CurveP256 CurveID = 23
+       CurveP384 CurveID = 24
+       CurveP521 CurveID = 25
 )
 
 // TLS Elliptic Curve Point Formats
@@ -153,6 +155,7 @@ var supportedClientCertSignatureAlgorithms = []signatureAndHash{
 
 // ConnectionState records basic TLS details about the connection.
 type ConnectionState struct {
+       Version                    uint16                // TLS version used by the connection (e.g. VersionTLS12)
        HandshakeComplete          bool                  // TLS handshake is complete
        DidResume                  bool                  // connection resumes a previous TLS connection
        CipherSuite                uint16                // cipher suite in use (TLS_RSA_WITH_RC4_128_SHA, ...)
@@ -198,12 +201,15 @@ type ClientSessionCache interface {
        Put(sessionKey string, cs *ClientSessionState)
 }
 
-// A Config structure is used to configure a TLS client or server. After one
-// has been passed to a TLS function it must not be modified.
+// A Config structure is used to configure a TLS client or server.
+// After one has been passed to a TLS function it must not be
+// modified. A Config may be reused; the tls package will also not
+// modify it.
 type Config struct {
        // Rand provides the source of entropy for nonces and RSA blinding.
        // If Rand is nil, TLS uses the cryptographic random reader in package
        // crypto/rand.
+       // The Reader must be safe for use by multiple goroutines.
        Rand io.Reader
 
        // Time returns the current time as the number of seconds since the epoch.
@@ -290,6 +296,11 @@ type Config struct {
        // which is currently TLS 1.2.
        MaxVersion uint16
 
+       // CurvePreferences contains the elliptic curves that will be used in
+       // an ECDHE handshake, in preference order. If empty, the default will
+       // be used.
+       CurvePreferences []CurveID
+
        serverInitOnce sync.Once // guards calling (*Config).serverInit
 }
 
@@ -348,6 +359,15 @@ func (c *Config) maxVersion() uint16 {
        return c.MaxVersion
 }
 
+var defaultCurvePreferences = []CurveID{CurveP256, CurveP384, CurveP521}
+
+func (c *Config) curvePreferences() []CurveID {
+       if c == nil || len(c.CurvePreferences) == 0 {
+               return defaultCurvePreferences
+       }
+       return c.CurvePreferences
+}
+
 // mutualVersion returns the protocol version to use given the advertised
 // version of the peer.
 func (c *Config) mutualVersion(vers uint16) (uint16, bool) {
index c33549c9efffa3ee83d17b6f6d21e0eaef213483..8f7d2c144ff23a9a56735ff88728b51494c07522 100644 (file)
@@ -28,6 +28,7 @@ type Conn struct {
 
        // constant after handshake; protected by handshakeMutex
        handshakeMutex    sync.Mutex // handshakeMutex < in.Mutex, out.Mutex, errMutex
+       handshakeErr      error      // error resulting from handshake
        vers              uint16     // TLS version
        haveVers          bool       // version has been negotiated
        config            *Config    // configuration passed to constructor
@@ -45,9 +46,6 @@ type Conn struct {
        clientProtocol         string
        clientProtocolFallback bool
 
-       // first permanent error
-       connErr
-
        // input/output
        in, out  halfConn     // in.Mutex < out.Mutex
        rawInput *block       // raw input, right off the wire
@@ -57,27 +55,6 @@ type Conn struct {
        tmp [16]byte
 }
 
-type connErr struct {
-       mu    sync.Mutex
-       value error
-}
-
-func (e *connErr) setError(err error) error {
-       e.mu.Lock()
-       defer e.mu.Unlock()
-
-       if e.value == nil {
-               e.value = err
-       }
-       return err
-}
-
-func (e *connErr) error() error {
-       e.mu.Lock()
-       defer e.mu.Unlock()
-       return e.value
-}
-
 // Access to net.Conn methods.
 // Cannot just embed net.Conn because that would
 // export the struct field too.
@@ -105,7 +82,7 @@ func (c *Conn) SetReadDeadline(t time.Time) error {
        return c.conn.SetReadDeadline(t)
 }
 
-// SetWriteDeadline sets the write deadline on the underlying conneciton.
+// SetWriteDeadline sets the write deadline on the underlying connection.
 // A zero value for t means Write will not time out.
 // After a Write has timed out, the TLS state is corrupt and all future writes will return the same error.
 func (c *Conn) SetWriteDeadline(t time.Time) error {
@@ -116,6 +93,8 @@ func (c *Conn) SetWriteDeadline(t time.Time) error {
 // connection, either sending or receiving.
 type halfConn struct {
        sync.Mutex
+
+       err     error       // first permanent error
        version uint16      // protocol version
        cipher  interface{} // cipher algorithm
        mac     macFunction
@@ -129,6 +108,18 @@ type halfConn struct {
        inDigestBuf, outDigestBuf []byte
 }
 
+func (hc *halfConn) setErrorLocked(err error) error {
+       hc.err = err
+       return err
+}
+
+func (hc *halfConn) error() error {
+       hc.Lock()
+       err := hc.err
+       hc.Unlock()
+       return err
+}
+
 // prepareCipherSpec sets the encryption and MAC states
 // that a subsequent changeCipherSpec will use.
 func (hc *halfConn) prepareCipherSpec(version uint16, cipher interface{}, mac macFunction) {
@@ -460,6 +451,8 @@ func (b *block) readFromUntil(r io.Reader, n int) error {
                m, err := r.Read(b.data[len(b.data):cap(b.data)])
                b.data = b.data[0 : len(b.data)+m]
                if len(b.data) >= n {
+                       // TODO(bradfitz,agl): slightly suspicious
+                       // that we're throwing away r.Read's err here.
                        break
                }
                if err != nil {
@@ -520,16 +513,16 @@ func (c *Conn) readRecord(want recordType) error {
        switch want {
        default:
                c.sendAlert(alertInternalError)
-               return errors.New("tls: unknown record type requested")
+               return c.in.setErrorLocked(errors.New("tls: unknown record type requested"))
        case recordTypeHandshake, recordTypeChangeCipherSpec:
                if c.handshakeComplete {
                        c.sendAlert(alertInternalError)
-                       return errors.New("tls: handshake or ChangeCipherSpec requested after handshake complete")
+                       return c.in.setErrorLocked(errors.New("tls: handshake or ChangeCipherSpec requested after handshake complete"))
                }
        case recordTypeApplicationData:
                if !c.handshakeComplete {
                        c.sendAlert(alertInternalError)
-                       return errors.New("tls: application data record requested before handshake complete")
+                       return c.in.setErrorLocked(errors.New("tls: application data record requested before handshake complete"))
                }
        }
 
@@ -548,7 +541,7 @@ Again:
                //      err = io.ErrUnexpectedEOF
                // }
                if e, ok := err.(net.Error); !ok || !e.Temporary() {
-                       c.setError(err)
+                       c.in.setErrorLocked(err)
                }
                return err
        }
@@ -560,18 +553,18 @@ Again:
        // an SSLv2 client.
        if want == recordTypeHandshake && typ == 0x80 {
                c.sendAlert(alertProtocolVersion)
-               return errors.New("tls: unsupported SSLv2 handshake received")
+               return c.in.setErrorLocked(errors.New("tls: unsupported SSLv2 handshake received"))
        }
 
        vers := uint16(b.data[1])<<8 | uint16(b.data[2])
        n := int(b.data[3])<<8 | int(b.data[4])
        if c.haveVers && vers != c.vers {
                c.sendAlert(alertProtocolVersion)
-               return fmt.Errorf("tls: received record with version %x when expecting version %x", vers, c.vers)
+               return c.in.setErrorLocked(fmt.Errorf("tls: received record with version %x when expecting version %x", vers, c.vers))
        }
        if n > maxCiphertext {
                c.sendAlert(alertRecordOverflow)
-               return fmt.Errorf("tls: oversized record received with length %d", n)
+               return c.in.setErrorLocked(fmt.Errorf("tls: oversized record received with length %d", n))
        }
        if !c.haveVers {
                // First message, be extra suspicious:
@@ -584,7 +577,7 @@ Again:
                // it's probably not real.
                if (typ != recordTypeAlert && typ != want) || vers >= 0x1000 || n >= 0x3000 {
                        c.sendAlert(alertUnexpectedMessage)
-                       return fmt.Errorf("tls: first record does not look like a TLS handshake")
+                       return c.in.setErrorLocked(fmt.Errorf("tls: first record does not look like a TLS handshake"))
                }
        }
        if err := b.readFromUntil(c.conn, recordHeaderLen+n); err != nil {
@@ -592,7 +585,7 @@ Again:
                        err = io.ErrUnexpectedEOF
                }
                if e, ok := err.(net.Error); !ok || !e.Temporary() {
-                       c.setError(err)
+                       c.in.setErrorLocked(err)
                }
                return err
        }
@@ -601,27 +594,27 @@ Again:
        b, c.rawInput = c.in.splitBlock(b, recordHeaderLen+n)
        ok, off, err := c.in.decrypt(b)
        if !ok {
-               return c.sendAlert(err)
+               c.in.setErrorLocked(c.sendAlert(err))
        }
        b.off = off
        data := b.data[b.off:]
        if len(data) > maxPlaintext {
-               c.sendAlert(alertRecordOverflow)
+               err := c.sendAlert(alertRecordOverflow)
                c.in.freeBlock(b)
-               return c.error()
+               return c.in.setErrorLocked(err)
        }
 
        switch typ {
        default:
-               c.sendAlert(alertUnexpectedMessage)
+               c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
 
        case recordTypeAlert:
                if len(data) != 2 {
-                       c.sendAlert(alertUnexpectedMessage)
+                       c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
                        break
                }
                if alert(data[1]) == alertCloseNotify {
-                       c.setError(io.EOF)
+                       c.in.setErrorLocked(io.EOF)
                        break
                }
                switch data[0] {
@@ -630,24 +623,24 @@ Again:
                        c.in.freeBlock(b)
                        goto Again
                case alertLevelError:
-                       c.setError(&net.OpError{Op: "remote error", Err: alert(data[1])})
+                       c.in.setErrorLocked(&net.OpError{Op: "remote error", Err: alert(data[1])})
                default:
-                       c.sendAlert(alertUnexpectedMessage)
+                       c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
                }
 
        case recordTypeChangeCipherSpec:
                if typ != want || len(data) != 1 || data[0] != 1 {
-                       c.sendAlert(alertUnexpectedMessage)
+                       c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
                        break
                }
                err := c.in.changeCipherSpec()
                if err != nil {
-                       c.sendAlert(err.(alert))
+                       c.in.setErrorLocked(c.sendAlert(err.(alert)))
                }
 
        case recordTypeApplicationData:
                if typ != want {
-                       c.sendAlert(alertUnexpectedMessage)
+                       c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
                        break
                }
                c.input = b
@@ -656,7 +649,7 @@ Again:
        case recordTypeHandshake:
                // TODO(rsc): Should at least pick off connection close.
                if typ != want {
-                       return c.sendAlert(alertNoRenegotiation)
+                       return c.in.setErrorLocked(c.sendAlert(alertNoRenegotiation))
                }
                c.hand.Write(data)
        }
@@ -664,7 +657,7 @@ Again:
        if b != nil {
                c.in.freeBlock(b)
        }
-       return c.error()
+       return c.in.err
 }
 
 // sendAlert sends a TLS alert message.
@@ -680,7 +673,7 @@ func (c *Conn) sendAlertLocked(err alert) error {
        c.writeRecord(recordTypeAlert, c.tmp[0:2])
        // closeNotify is a special case in that it isn't an error:
        if err != alertCloseNotify {
-               return c.setError(&net.OpError{Op: "local error", Err: err})
+               return c.out.setErrorLocked(&net.OpError{Op: "local error", Err: err})
        }
        return nil
 }
@@ -766,7 +759,7 @@ func (c *Conn) writeRecord(typ recordType, data []byte) (n int, err error) {
                        c.tmp[0] = alertLevelError
                        c.tmp[1] = byte(err.(alert))
                        c.writeRecord(recordTypeAlert, c.tmp[0:2])
-                       return n, c.setError(&net.OpError{Op: "local error", Err: err})
+                       return n, c.out.setErrorLocked(&net.OpError{Op: "local error", Err: err})
                }
        }
        return
@@ -777,7 +770,7 @@ func (c *Conn) writeRecord(typ recordType, data []byte) (n int, err error) {
 // c.in.Mutex < L; c.out.Mutex < L.
 func (c *Conn) readHandshake() (interface{}, error) {
        for c.hand.Len() < 4 {
-               if err := c.error(); err != nil {
+               if err := c.in.err; err != nil {
                        return nil, err
                }
                if err := c.readRecord(recordTypeHandshake); err != nil {
@@ -788,11 +781,10 @@ func (c *Conn) readHandshake() (interface{}, error) {
        data := c.hand.Bytes()
        n := int(data[1])<<16 | int(data[2])<<8 | int(data[3])
        if n > maxHandshake {
-               c.sendAlert(alertInternalError)
-               return nil, c.error()
+               return nil, c.in.setErrorLocked(c.sendAlert(alertInternalError))
        }
        for c.hand.Len() < 4+n {
-               if err := c.error(); err != nil {
+               if err := c.in.err; err != nil {
                        return nil, err
                }
                if err := c.readRecord(recordTypeHandshake); err != nil {
@@ -831,8 +823,7 @@ func (c *Conn) readHandshake() (interface{}, error) {
        case typeFinished:
                m = new(finishedMsg)
        default:
-               c.sendAlert(alertUnexpectedMessage)
-               return nil, alertUnexpectedMessage
+               return nil, c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
        }
 
        // The handshake message unmarshallers
@@ -841,25 +832,24 @@ func (c *Conn) readHandshake() (interface{}, error) {
        data = append([]byte(nil), data...)
 
        if !m.unmarshal(data) {
-               c.sendAlert(alertUnexpectedMessage)
-               return nil, alertUnexpectedMessage
+               return nil, c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
        }
        return m, nil
 }
 
 // Write writes data to the connection.
 func (c *Conn) Write(b []byte) (int, error) {
-       if err := c.error(); err != nil {
-               return 0, err
-       }
-
        if err := c.Handshake(); err != nil {
-               return 0, c.setError(err)
+               return 0, err
        }
 
        c.out.Lock()
        defer c.out.Unlock()
 
+       if err := c.out.err; err != nil {
+               return 0, err
+       }
+
        if !c.handshakeComplete {
                return 0, alertInternalError
        }
@@ -878,14 +868,14 @@ func (c *Conn) Write(b []byte) (int, error) {
                if _, ok := c.out.cipher.(cipher.BlockMode); ok {
                        n, err := c.writeRecord(recordTypeApplicationData, b[:1])
                        if err != nil {
-                               return n, c.setError(err)
+                               return n, c.out.setErrorLocked(err)
                        }
                        m, b = 1, b[1:]
                }
        }
 
        n, err := c.writeRecord(recordTypeApplicationData, b)
-       return n + m, c.setError(err)
+       return n + m, c.out.setErrorLocked(err)
 }
 
 // Read can be made to time out and return a net.Error with Timeout() == true
@@ -894,6 +884,11 @@ func (c *Conn) Read(b []byte) (n int, err error) {
        if err = c.Handshake(); err != nil {
                return
        }
+       if len(b) == 0 {
+               // Put this after Handshake, in case people were calling
+               // Read(nil) for the side effect of the Handshake.
+               return
+       }
 
        c.in.Lock()
        defer c.in.Unlock()
@@ -902,13 +897,13 @@ func (c *Conn) Read(b []byte) (n int, err error) {
        // CBC IV. So this loop ignores a limited number of empty records.
        const maxConsecutiveEmptyRecords = 100
        for emptyRecordCount := 0; emptyRecordCount <= maxConsecutiveEmptyRecords; emptyRecordCount++ {
-               for c.input == nil && c.error() == nil {
+               for c.input == nil && c.in.err == nil {
                        if err := c.readRecord(recordTypeApplicationData); err != nil {
                                // Soft error, like EAGAIN
                                return 0, err
                        }
                }
-               if err := c.error(); err != nil {
+               if err := c.in.err; err != nil {
                        return 0, err
                }
 
@@ -918,6 +913,25 @@ func (c *Conn) Read(b []byte) (n int, err error) {
                        c.input = nil
                }
 
+               // If a close-notify alert is waiting, read it so that
+               // we can return (n, EOF) instead of (n, nil), to signal
+               // to the HTTP response reading goroutine that the
+               // connection is now closed. This eliminates a race
+               // where the HTTP response reading goroutine would
+               // otherwise not observe the EOF until its next read,
+               // by which time a client goroutine might have already
+               // tried to reuse the HTTP connection for a new
+               // request.
+               // See https://codereview.appspot.com/76400046
+               // and http://golang.org/issue/3514
+               if ri := c.rawInput; ri != nil &&
+                       n != 0 && err == nil &&
+                       c.input == nil && len(ri.data) > 0 && recordType(ri.data[0]) == recordTypeAlert {
+                       if recErr := c.readRecord(recordTypeApplicationData); recErr != nil {
+                               err = recErr // will be io.EOF on closeNotify
+                       }
+               }
+
                if n != 0 || err != nil {
                        return n, err
                }
@@ -949,16 +963,19 @@ func (c *Conn) Close() error {
 func (c *Conn) Handshake() error {
        c.handshakeMutex.Lock()
        defer c.handshakeMutex.Unlock()
-       if err := c.error(); err != nil {
+       if err := c.handshakeErr; err != nil {
                return err
        }
        if c.handshakeComplete {
                return nil
        }
+
        if c.isClient {
-               return c.clientHandshake()
+               c.handshakeErr = c.clientHandshake()
+       } else {
+               c.handshakeErr = c.serverHandshake()
        }
-       return c.serverHandshake()
+       return c.handshakeErr
 }
 
 // ConnectionState returns basic TLS details about the connection.
@@ -969,6 +986,7 @@ func (c *Conn) ConnectionState() ConnectionState {
        var state ConnectionState
        state.HandshakeComplete = c.handshakeComplete
        if c.handshakeComplete {
+               state.Version = c.vers
                state.NegotiatedProtocol = c.clientProtocol
                state.DidResume = c.didResume
                state.NegotiatedProtocolIsMutual = !c.clientProtocolFallback
index 1b4830c725cb40f0766f354442ed74e4ffbd9807..5c6d8396d5208ea7a998718b2f154d3e688291b0 100644 (file)
@@ -58,12 +58,6 @@ func main() {
 
        notAfter := notBefore.Add(*validFor)
 
-       // end of ASN.1 time
-       endOfTime := time.Date(2049, 12, 31, 23, 59, 59, 0, time.UTC)
-       if notAfter.After(endOfTime) {
-               notAfter = endOfTime
-       }
-
        serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
        serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
        if err != nil {
index fd1303eebb947ff7d3a35ad46530c44f6460a05c..a320fde1bc7cb82c2f91f76cbf089b783179bc9c 100644 (file)
@@ -33,13 +33,17 @@ func (c *Conn) clientHandshake() error {
                c.config = defaultConfig()
        }
 
+       if len(c.config.ServerName) == 0 && !c.config.InsecureSkipVerify {
+               return errors.New("tls: either ServerName or InsecureSkipVerify must be specified in the tls.Config")
+       }
+
        hello := &clientHelloMsg{
                vers:                c.config.maxVersion(),
                compressionMethods:  []uint8{compressionNone},
                random:              make([]byte, 32),
                ocspStapling:        true,
                serverName:          c.config.ServerName,
-               supportedCurves:     []uint16{curveP256, curveP384, curveP521},
+               supportedCurves:     c.config.curvePreferences(),
                supportedPoints:     []uint8{pointFormatUncompressed},
                nextProtoNeg:        len(c.config.NextProtos) > 0,
                secureRenegotiation: true,
@@ -497,7 +501,7 @@ func (hs *clientHandshakeState) readFinished() error {
        c := hs.c
 
        c.readRecord(recordTypeChangeCipherSpec)
-       if err := c.error(); err != nil {
+       if err := c.in.error(); err != nil {
                return err
        }
 
index fbdd0b9a78f4433ef10d9274ac56584cef116104..7bcaa5eb9292977d2403a11f53102df64d161a24 100644 (file)
@@ -16,7 +16,7 @@ type clientHelloMsg struct {
        nextProtoNeg        bool
        serverName          string
        ocspStapling        bool
-       supportedCurves     []uint16
+       supportedCurves     []CurveID
        supportedPoints     []uint8
        ticketSupported     bool
        sessionTicket       []uint8
@@ -39,7 +39,7 @@ func (m *clientHelloMsg) equal(i interface{}) bool {
                m.nextProtoNeg == m1.nextProtoNeg &&
                m.serverName == m1.serverName &&
                m.ocspStapling == m1.ocspStapling &&
-               eqUint16s(m.supportedCurves, m1.supportedCurves) &&
+               eqCurveIDs(m.supportedCurves, m1.supportedCurves) &&
                bytes.Equal(m.supportedPoints, m1.supportedPoints) &&
                m.ticketSupported == m1.ticketSupported &&
                bytes.Equal(m.sessionTicket, m1.sessionTicket) &&
@@ -357,10 +357,10 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool {
                                return false
                        }
                        numCurves := l / 2
-                       m.supportedCurves = make([]uint16, numCurves)
+                       m.supportedCurves = make([]CurveID, numCurves)
                        d := data[2:]
                        for i := 0; i < numCurves; i++ {
-                               m.supportedCurves[i] = uint16(d[0])<<8 | uint16(d[1])
+                               m.supportedCurves[i] = CurveID(d[0])<<8 | CurveID(d[1])
                                d = d[2:]
                        }
                case extensionSupportedPoints:
@@ -1294,6 +1294,18 @@ func eqUint16s(x, y []uint16) bool {
        return true
 }
 
+func eqCurveIDs(x, y []CurveID) bool {
+       if len(x) != len(y) {
+               return false
+       }
+       for i, v := range x {
+               if y[i] != v {
+                       return false
+               }
+       }
+       return true
+}
+
 func eqStrings(x, y []string) bool {
        if len(x) != len(y) {
                return false
index 4f569eeb1389eddae09ea48f84bd85733ccbafe4..f46aabdfd5f2445bb1b4e54e58da09903e3ed1a6 100644 (file)
@@ -125,9 +125,9 @@ func (*clientHelloMsg) Generate(rand *rand.Rand, size int) reflect.Value {
        }
        m.ocspStapling = rand.Intn(10) > 5
        m.supportedPoints = randomBytes(rand.Intn(5)+1, rand)
-       m.supportedCurves = make([]uint16, rand.Intn(5)+1)
+       m.supportedCurves = make([]CurveID, rand.Intn(5)+1)
        for i := range m.supportedCurves {
-               m.supportedCurves[i] = uint16(rand.Intn(30000))
+               m.supportedCurves[i] = CurveID(rand.Intn(30000))
        }
        if rand.Intn(10) > 5 {
                m.ticketSupported = true
index 12e5ff1e5895b9052b2751e043a51c8db7e39a78..75111eba0042f54c2cd42b59bd1f82161c957c84 100644 (file)
@@ -117,12 +117,14 @@ func (hs *serverHandshakeState) readClientHello() (isResume bool, err error) {
        hs.hello = new(serverHelloMsg)
 
        supportedCurve := false
+       preferredCurves := config.curvePreferences()
 Curves:
        for _, curve := range hs.clientHello.supportedCurves {
-               switch curve {
-               case curveP256, curveP384, curveP521:
-                       supportedCurve = true
-                       break Curves
+               for _, supported := range preferredCurves {
+                       if supported == curve {
+                               supportedCurve = true
+                               break Curves
+                       }
                }
        }
 
@@ -468,7 +470,7 @@ func (hs *serverHandshakeState) readFinished() error {
        c := hs.c
 
        c.readRecord(recordTypeChangeCipherSpec)
-       if err := c.error(); err != nil {
+       if err := c.in.error(); err != nil {
                return err
        }
 
index 4f41ab9b78c2866aa4c0495413a28e793fbffc2a..c3e36785b5d0fff4a0413d934b91372d12e73ab7 100644 (file)
@@ -121,7 +121,7 @@ func TestTLS12OnlyCipherSuites(t *testing.T) {
                        TLS_RSA_WITH_RC4_128_SHA,
                },
                compressionMethods: []uint8{compressionNone},
-               supportedCurves:    []uint16{curveP256, curveP384, curveP521},
+               supportedCurves:    []CurveID{CurveP256, CurveP384, CurveP521},
                supportedPoints:    []uint8{pointFormatUncompressed},
        }
 
@@ -195,6 +195,23 @@ func testHandshake(clientConfig, serverConfig *Config) (state ConnectionState, e
        return
 }
 
+func TestVersion(t *testing.T) {
+       serverConfig := &Config{
+               Certificates: testConfig.Certificates,
+               MaxVersion:   VersionTLS11,
+       }
+       clientConfig := &Config{
+               InsecureSkipVerify: true,
+       }
+       state, err := testHandshake(clientConfig, serverConfig)
+       if err != nil {
+               t.Fatalf("handshake failed: %s", err)
+       }
+       if state.Version != VersionTLS11 {
+               t.Fatalf("Incorrect version %x, should be %x", state.Version, VersionTLS11)
+       }
+}
+
 func TestCipherSuitePreference(t *testing.T) {
        serverConfig := &Config{
                CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA},
index 861faf0e85a0495c058de1f543986ad4dd04121f..f38b701f1bab5a6a8fbb64d2a2375f9ccd404930 100644 (file)
@@ -141,7 +141,7 @@ func hashForServerKeyExchange(sigType, hashFunc uint8, version uint16, slices ..
 
 // pickTLS12HashForSignature returns a TLS 1.2 hash identifier for signing a
 // ServerKeyExchange given the signature type being used and the client's
-// advertized list of supported signature and hash combinations.
+// advertised list of supported signature and hash combinations.
 func pickTLS12HashForSignature(sigType uint8, clientSignatureAndHashes []signatureAndHash) (uint8, error) {
        if len(clientSignatureAndHashes) == 0 {
                // If the client didn't specify any signature_algorithms
@@ -163,6 +163,20 @@ func pickTLS12HashForSignature(sigType uint8, clientSignatureAndHashes []signatu
        return 0, errors.New("tls: client doesn't support any common hash functions")
 }
 
+func curveForCurveID(id CurveID) (elliptic.Curve, bool) {
+       switch id {
+       case CurveP256:
+               return elliptic.P256(), true
+       case CurveP384:
+               return elliptic.P384(), true
+       case CurveP521:
+               return elliptic.P521(), true
+       default:
+               return nil, false
+       }
+
+}
+
 // ecdheRSAKeyAgreement implements a TLS key agreement where the server
 // generates a ephemeral EC public/private key pair and signs it. The
 // pre-master secret is then calculated using ECDH. The signature may
@@ -176,23 +190,16 @@ type ecdheKeyAgreement struct {
 }
 
 func (ka *ecdheKeyAgreement) generateServerKeyExchange(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) {
-       var curveid uint16
-
-Curve:
-       for _, c := range clientHello.supportedCurves {
-               switch c {
-               case curveP256:
-                       ka.curve = elliptic.P256()
-                       curveid = c
-                       break Curve
-               case curveP384:
-                       ka.curve = elliptic.P384()
-                       curveid = c
-                       break Curve
-               case curveP521:
-                       ka.curve = elliptic.P521()
-                       curveid = c
-                       break Curve
+       var curveid CurveID
+       preferredCurves := config.curvePreferences()
+
+NextCandidate:
+       for _, candidate := range preferredCurves {
+               for _, c := range clientHello.supportedCurves {
+                       if candidate == c {
+                               curveid = c
+                               break NextCandidate
+                       }
                }
        }
 
@@ -200,6 +207,11 @@ Curve:
                return nil, errors.New("tls: no supported elliptic curves offered")
        }
 
+       var ok bool
+       if ka.curve, ok = curveForCurveID(curveid); !ok {
+               return nil, errors.New("tls: preferredCurves includes unsupported curve")
+       }
+
        var x, y *big.Int
        var err error
        ka.privateKey, x, y, err = elliptic.GenerateKey(ka.curve, config.rand())
@@ -293,19 +305,13 @@ func (ka *ecdheKeyAgreement) processServerKeyExchange(config *Config, clientHell
                return errServerKeyExchange
        }
        if skx.key[0] != 3 { // named curve
-               return errors.New("server selected unsupported curve")
+               return errors.New("tls: server selected unsupported curve")
        }
-       curveid := uint16(skx.key[1])<<8 | uint16(skx.key[2])
+       curveid := CurveID(skx.key[1])<<8 | CurveID(skx.key[2])
 
-       switch curveid {
-       case curveP256:
-               ka.curve = elliptic.P256()
-       case curveP384:
-               ka.curve = elliptic.P384()
-       case curveP521:
-               ka.curve = elliptic.P521()
-       default:
-               return errors.New("server selected unsupported curve")
+       var ok bool
+       if ka.curve, ok = curveForCurveID(curveid); !ok {
+               return errors.New("tls: server selected unsupported curve")
        }
 
        publicLen := int(skx.key[3])
index 0d2294a710713292c5f7e4ca579aaa62f0e70cfc..00722cba945f875effd1353c2f1eebb6011c38fa 100644 (file)
@@ -8,11 +8,11 @@
 00000060  19 00 0b 00 02 01 00 00  0d 00 0a 00 08 04 01 04  |................|
 00000070  03 02 01 02 03 ff 01 00  01 00                    |..........|
 >>> Flow 2 (server to client)
-00000000  16 03 01 00 59 02 00 00  55 03 01 52 cc 57 59 e6  |....Y...U..R.WY.|
-00000010  12 4c 15 38 e6 b7 2c 9e  b5 82 bb b5 9d f5 71 4a  |.L.8..,.......qJ|
-00000020  66 21 30 c2 44 69 ec 7c  8a 37 8b 20 1e 9c 78 3a  |f!0.Di.|.7. ..x:|
-00000030  bf d0 e0 37 6b 88 5c 8f  90 a8 92 c3 f6 b7 ad fc  |...7k.\.........|
-00000040  56 4a 50 34 ce 8f 08 e5  08 40 71 9b c0 09 00 00  |VJP4.....@q.....|
+00000000  16 03 01 00 59 02 00 00  55 03 01 53 04 f1 03 46  |....Y...U..S...F|
+00000010  0f 84 c4 cb 55 ef 85 f6  4f d7 0e e1 4b 10 d4 bb  |....U...O...K...|
+00000020  35 87 2d f3 d7 18 ec 4e  95 4b f4 20 28 82 94 d9  |5.-....N.K. (...|
+00000030  df c4 fc ee 21 23 c1 e2  76 3e 7b 09 af 2c 39 23  |....!#..v>{..,9#|
+00000040  f8 46 6c 31 88 42 f0 79  de 37 2b 00 c0 09 00 00  |.Fl1.B.y.7+.....|
 00000050  0d ff 01 00 01 00 00 0b  00 04 03 00 01 02 16 03  |................|
 00000060  01 02 0e 0b 00 02 0a 00  02 07 00 02 04 30 82 02  |.............0..|
 00000070  00 30 82 01 62 02 09 00  b8 bf 2d 47 a0 d2 eb f4  |.0..b.....-G....|
 00000240  13 83 0d 94 06 bb d4 37  7a f6 ec 7a c9 86 2e dd  |.......7z..z....|
 00000250  d7 11 69 7f 85 7c 56 de  fb 31 78 2b e4 c7 78 0d  |..i..|V..1x+..x.|
 00000260  ae cb be 9e 4e 36 24 31  7b 6a 0f 39 95 12 07 8f  |....N6$1{j.9....|
-00000270  2a 16 03 01 00 d5 0c 00  00 d1 03 00 17 41 04 00  |*............A..|
-00000280  3d 87 12 2c c5 fd db 6e  ab 0c 7c 54 85 fc d3 13  |=..,...n..|T....|
-00000290  34 1e 13 83 2c 60 05 67  83 f0 3a cc c6 27 84 63  |4...,`.g..:..'.c|
-000002a0  90 4e 25 26 0f 03 ca f3  ae 7e 44 c6 94 0d e4 1b  |.N%&.....~D.....|
-000002b0  4a 53 e2 d7 f0 5c 83 64  37 c0 0f d1 9e 86 de 00  |JS...\.d7.......|
-000002c0  8a 30 81 87 02 41 4f 85  6d 11 af d1 27 9c de 21  |.0...AO.m...'..!|
-000002d0  d7 e5 96 ad 4f 6a a1 5c  2b 63 22 0e 4f 22 77 16  |....Oj.\+c".O"w.|
-000002e0  ec 6e db 38 1a df 28 4e  ee 9e a4 c1 e7 d3 02 74  |.n.8..(N.......t|
-000002f0  07 3f 58 7f 82 51 2d 9e  78 6b bd 28 77 20 c1 40  |.?X..Q-.xk.(w .@|
-00000300  09 71 ba b9 23 c7 82 02  42 01 13 1b 64 c4 1f c6  |.q..#...B...d...|
-00000310  1f f3 f4 51 8c 64 92 37  5e f0 5c 11 c9 ee 43 55  |...Q.d.7^.\...CU|
-00000320  a0 83 52 8b d9 2d d1 22  2c 2e 2c e0 04 a6 59 b3  |..R..-.",.,...Y.|
-00000330  f9 33 d8 e4 9a 0b 2b 95  c6 41 5d 60 a3 d1 9f 31  |.3....+..A]`...1|
-00000340  14 5d a3 31 6e 70 f1 e7  35 5a f9 16 03 01 00 0e  |.].1np..5Z......|
+00000270  2a 16 03 01 00 d5 0c 00  00 d1 03 00 17 41 04 4f  |*............A.O|
+00000280  47 16 72 98 9e 9f 2e 8e  78 e9 0f fe 95 83 7b aa  |G.r.....x.....{.|
+00000290  e5 3d c0 7d cf 83 bd 22  0b fd 48 f1 a7 49 a5 7d  |.=.}..."..H..I.}|
+000002a0  8e 0c 83 7f e1 2d 71 03  cc 90 09 ab f7 35 81 48  |.....-q......5.H|
+000002b0  a4 1e 7d 87 21 23 12 58  2c 47 f3 af c7 6c 71 00  |..}.!#.X,G...lq.|
+000002c0  8a 30 81 87 02 42 00 b4  03 38 60 43 d9 32 ef 64  |.0...B...8`C.2.d|
+000002d0  5a 9c 91 95 0d 10 21 53  c7 78 f8 bf 50 ed 13 5d  |Z.....!S.x..P..]|
+000002e0  c3 e7 71 d6 11 04 f1 e4  9d ce 17 99 8d 1a 87 1f  |..q.............|
+000002f0  cb dd f8 1b ae cd bc 4a  77 ab 7c 50 bf 73 c3 ea  |.......Jw.|P.s..|
+00000300  d6 df 88 56 f6 b1 03 83  02 41 66 3d fb 4e 7e af  |...V.....Af=.N~.|
+00000310  4e c1 60 fe 09 fa 7e 74  99 66 7f de b4 b2 74 89  |N.`...~t.f....t.|
+00000320  1c a4 cf 74 1a 55 a5 be  74 f9 36 21 3d ae c8 c3  |...t.U..t.6!=...|
+00000330  24 8e ad db a3 26 67 8f  98 27 e3 93 ee d9 5c fb  |$....&g..'....\.|
+00000340  85 82 e2 13 c3 50 ab e9  f6 39 2b 16 03 01 00 0e  |.....P...9+.....|
 00000350  0d 00 00 06 03 01 02 40  00 00 0e 00 00 00        |.......@......|
 >>> Flow 3 (client to server)
 00000000  16 03 01 02 0a 0b 00 02  06 00 02 03 00 02 00 30  |...............0|
 000002a0  85 6a 42 9b f9 7e 7e 31  c2 e5 bd 66 02 41 4b 49  |.jB..~~1...f.AKI|
 000002b0  c6 cd 02 e3 83 f7 03 50  18 6d b4 c9 51 02 c0 ab  |.......P.m..Q...|
 000002c0  87 bc e0 3e 4b 89 53 3a  e2 65 89 97 02 c1 87 f1  |...>K.S:.e......|
-000002d0  67 d0 f2 06 28 4e 51 4e  fd f0 01 c1 1f 1d 1c 32  |g...(NQN.......2|
-000002e0  1f 91 5d 06 f5 44 1b a0  1b 61 43 6b de 81 bd 14  |..]..D...aCk....|
-000002f0  03 01 00 01 01 16 03 01  00 30 fe bb 82 52 2d 8a  |.........0...R-.|
-00000300  81 87 ba 2c 18 5b 93 07  78 30 85 f3 5f 4f df 3f  |...,.[..x0.._O.?|
-00000310  1a fc 01 b9 a5 32 99 d3  40 0b ef c5 b8 32 f4 7e  |.....2..@....2.~|
-00000320  d2 93 0f 19 24 87 c5 18  e2 8b                    |....$.....|
+000002d0  67 d0 f2 06 28 4e 51 4e  fd f0 01 be 41 3c 52 42  |g...(NQN....A<RB|
+000002e0  10 44 73 88 3e 44 24 bb  2e 77 01 77 6f a8 ac 14  |.Ds.>D$..w.wo...|
+000002f0  03 01 00 01 01 16 03 01  00 30 a3 da 45 22 96 83  |.........0..E"..|
+00000300  59 90 e9 6b ec 3b 77 50  05 89 e6 0c 61 d1 1d 2b  |Y..k.;wP....a..+|
+00000310  da d4 49 bf b9 c6 dd ad  c3 9c 82 bd 53 62 e8 57  |..I.........Sb.W|
+00000320  a4 6a e7 9f b1 d5 39 77  88 6d                    |.j....9w.m|
 >>> Flow 4 (server to client)
-00000000  14 03 01 00 01 01 16 03  01 00 30 84 a0 90 cb 8b  |..........0.....|
-00000010  43 ad 66 06 ef f1 4b 5c  85 cc 4e 12 60 44 b4 be  |C.f...K\..N.`D..|
-00000020  ed 94 23 69 bf 7a cc e9  a7 17 db 9a ec d5 9b 15  |..#i.z..........|
-00000030  92 62 5e bb ac db 78 50  d1 b2 0c                 |.b^...xP...|
+00000000  14 03 01 00 01 01 16 03  01 00 30 a4 45 dd 99 df  |..........0.E...|
+00000010  66 ae f5 c7 bd 1a eb 6a  ff ac a6 38 14 81 b5 07  |f......j...8....|
+00000020  86 24 80 f1 09 59 ad 33  3d 43 ed 9e 43 b1 1e 9f  |.$...Y.3=C..C...|
+00000030  bd 8c b3 e0 41 83 a1 34  91 c5 a1                 |....A..4...|
 >>> Flow 5 (client to server)
-00000000  17 03 01 00 20 b4 fd 9e  0a 69 49 16 72 52 8b 81  |.... ....iI.rR..|
-00000010  50 87 07 77 d4 9a 07 06  88 e0 43 43 32 a0 7c f9  |P..w......CC2.|.|
-00000020  13 88 3f 87 36 17 03 01  00 20 6e 96 f6 4c f8 8d  |..?.6.... n..L..|
-00000030  1b 15 ff 3e 7c 5e fc f0  81 6e 5e 26 3c 42 96 dc  |...>|^...n^&<B..|
-00000040  69 79 b8 73 9c 1d eb 31  5d 94 15 03 01 00 20 b4  |iy.s...1]..... .|
-00000050  68 2c 4d 74 ed a5 bc f4  e3 56 bd 3e 19 78 fc 8a  |h,Mt.....V.>.x..|
-00000060  1e f0 4c 7e f9 11 de e1  15 38 9b ed 6f 9b 34     |..L~.....8..o.4|
+00000000  17 03 01 00 20 ae e3 ae  7f 2d e3 a2 f7 1b 4e 69  |.... ....-....Ni|
+00000010  cb 18 c6 68 42 f8 de 61  92 4c fa d6 19 7c 8c 09  |...hB..a.L...|..|
+00000020  82 e2 f2 32 19 17 03 01  00 20 2a 77 65 1f c1 fd  |...2..... *we...|
+00000030  5e 37 b7 15 f6 1f 4c 7f  5f 89 52 b4 32 27 4d 17  |^7....L._.R.2'M.|
+00000040  33 c6 e8 50 ac 70 c8 b9  2d 0a 15 03 01 00 20 e0  |3..P.p..-..... .|
+00000050  cb ce 07 80 55 a0 46 ca  a7 25 4c 5f 9d 7c 73 37  |....U.F..%L_.|s7|
+00000060  de 72 6d 36 a8 e4 be fd  2a e7 f8 8d 14 80 b7     |.rm6....*......|
index 36f6eb16371c2fe355b02bde25c055a392320187..c0be82491e250c453a659cc65e06a0506c251267 100644 (file)
@@ -8,11 +8,11 @@
 00000060  19 00 0b 00 02 01 00 00  0d 00 0a 00 08 04 01 04  |................|
 00000070  03 02 01 02 03 ff 01 00  01 00                    |..........|
 >>> Flow 2 (server to client)
-00000000  16 03 01 00 51 02 00 00  4d 03 01 52 cc 57 59 78  |....Q...M..R.WYx|
-00000010  63 f8 d6 ea 32 6f 22 7c  7b fb ab 48 c8 3b d9 ed  |c...2o"|{..H.;..|
-00000020  5b 01 e0 0a 36 d8 26 57  56 dd e4 20 ed c9 d8 e5  |[...6.&WV.. ....|
-00000030  5c 2a 1b b6 d2 bd 32 5d  42 fe 4b d1 89 4c 1f b0  |\*....2]B.K..L..|
-00000040  5d fc 1f d4 4a f3 ef 06  28 1a d3 09 00 05 00 00  |]...J...(.......|
+00000000  16 03 01 00 51 02 00 00  4d 03 01 53 04 f1 02 ed  |....Q...M..S....|
+00000010  86 9c 56 84 5a d3 7d d7  f3 4e 6f 2c 69 0d f0 59  |..V.Z.}..No,i..Y|
+00000020  a5 d1 de 2d 03 2f dd 63  c3 ab fa 20 30 d6 5a 24  |...-./.c... 0.Z$|
+00000030  5c 31 67 36 8d 4c 43 e1  64 c4 8a 2c a5 fd 39 92  |\1g6.LC.d..,..9.|
+00000040  c5 6f 58 47 a3 fe 63 14  98 92 11 90 00 05 00 00  |.oXG..c.........|
 00000050  05 ff 01 00 01 00 16 03  01 02 be 0b 00 02 ba 00  |................|
 00000060  02 b7 00 02 b4 30 82 02  b0 30 82 02 19 a0 03 02  |.....0...0......|
 00000070  01 02 02 09 00 85 b0 bb  a4 8a 7f b8 ca 30 0d 06  |.............0..|
 000002e0  85 6a 42 9b f9 7e 7e 31  c2 e5 bd 66 02 41 4b 49  |.jB..~~1...f.AKI|
 000002f0  c6 cd 02 e3 83 f7 03 50  18 6d b4 c9 51 02 c0 ab  |.......P.m..Q...|
 00000300  87 bc e0 3e 4b 89 53 3a  e2 65 89 97 02 c1 87 f1  |...>K.S:.e......|
-00000310  67 d0 f2 06 28 4e 51 4e  fd f0 01 65 a4 80 ad 7e  |g...(NQN...e...~|
-00000320  a3 bc 1d 1a 83 3a db 30  c7 67 96 cf e7 aa dc 14  |.....:.0.g......|
-00000330  03 01 00 01 01 16 03 01  00 24 d9 38 91 04 a4 ac  |.........$.8....|
-00000340  eb d7 4c 7a c6 07 25 72  a0 f7 3f 6d 37 cc db 40  |..Lz..%r..?m7..@|
-00000350  c0 26 9b be 03 55 2d 24  c5 b3 4f 6d 56 c4        |.&...U-$..OmV.|
+00000310  67 d0 f2 06 28 4e 51 4e  fd f0 01 47 e7 c9 d9 23  |g...(NQN...G...#|
+00000320  21 6b 87 d2 55 e3 c9 f7  eb 86 d5 1e 50 df d5 14  |!k..U.......P...|
+00000330  03 01 00 01 01 16 03 01  00 24 95 62 42 be 90 39  |.........$.bB..9|
+00000340  68 ae f5 77 47 21 14 b9  ac ee 81 2d e3 9e c7 34  |h..wG!.....-...4|
+00000350  3a 00 5c c9 12 1d c0 5a  7c e7 ef e0 cd fd        |:.\....Z|.....|
 >>> Flow 4 (server to client)
-00000000  14 03 01 00 01 01 16 03  01 00 24 b8 8d b1 db 80  |..........$.....|
-00000010  1d 26 9e 41 d0 48 40 0d  6a 94 e6 42 93 60 58 d1  |.&.A.H@.j..B.`X.|
-00000020  b2 13 7c 6f ec 1c f5 2a  1e 82 14 6f 0f 13 a7     |..|o...*...o...|
+00000000  14 03 01 00 01 01 16 03  01 00 24 ea 98 c0 fb 86  |..........$.....|
+00000010  87 7a 2e e1 c7 68 61 3e  5b cc da 1f d6 7b ab 5a  |.z...ha>[....{.Z|
+00000020  a0 ae a2 cf d0 54 44 19  12 db 75 2b 8c 73 8c     |.....TD...u+.s.|
 >>> Flow 5 (client to server)
-00000000  17 03 01 00 1a ea f3 99  a3 b1 7e 9a 29 ad 31 18  |..........~.).1.|
-00000010  1b 5b 7c 71 ae 5b c6 ba  8e 8e 36 f6 ab 2c dd 15  |.[|q.[....6..,..|
-00000020  03 01 00 16 82 cf f4 83  05 4c 86 e2 47 cc 6f 2c  |.........L..G.o,|
-00000030  6d 50 c6 09 3b fd a8 5e  12 3c                    |mP..;..^.<|
+00000000  17 03 01 00 1a f3 28 77  31 33 4c b3 7c 4b 75 61  |......(w13L.|Kua|
+00000010  38 69 6b ae c9 36 ab 2e  56 16 29 6a 9a 00 2f 15  |8ik..6..V.)j../.|
+00000020  03 01 00 16 6b ed 68 18  ed ff 44 39 9b 4a e4 a2  |....k.h...D9.J..|
+00000030  cd 79 ef 2a 3e 5a 4d b1  5d 56                    |.y.*>ZM.]V|
index 9e4ea6654431f6503bbc3b77a69e8b8cb5b29d5f..3e6dbc271a963c1856a9097a8a0feb32b2a94927 100644 (file)
@@ -8,11 +8,11 @@
 00000060  19 00 0b 00 02 01 00 00  0d 00 0a 00 08 04 01 04  |................|
 00000070  03 02 01 02 03 ff 01 00  01 00                    |..........|
 >>> Flow 2 (server to client)
-00000000  16 03 01 00 59 02 00 00  55 03 01 52 cc 57 58 fc  |....Y...U..R.WX.|
-00000010  46 e4 45 fc 07 cb ec 16  cc ce 87 96 0e f6 d9 c7  |F.E.............|
-00000020  49 2e ec 53 30 07 cd 01  1d 49 2e 20 2b bc 5c 11  |I..S0....I. +.\.|
-00000030  90 55 88 3f ec e4 30 b3  58 e7 d2 82 32 15 dd b4  |.U.?..0.X...2...|
-00000040  1f e2 4c 1d 08 f9 a0 8d  75 8b 63 c2 c0 09 00 00  |..L.....u.c.....|
+00000000  16 03 01 00 59 02 00 00  55 03 01 53 04 f1 02 4f  |....Y...U..S...O|
+00000010  73 06 2d 72 41 36 a1 b2  d3 50 97 55 8c c5 f1 43  |s.-rA6...P.U...C|
+00000020  37 1f 1a 2a fe 51 70 0b  2f 25 9e 20 50 61 86 80  |7..*.Qp./%. Pa..|
+00000030  9a 9c 6d 6f c9 ea 5c ce  0c b7 7c ce e3 be d0 e5  |..mo..\...|.....|
+00000040  be d0 c4 80 78 c3 c7 17  0c 2d 8e c8 c0 09 00 00  |....x....-......|
 00000050  0d ff 01 00 01 00 00 0b  00 04 03 00 01 02 16 03  |................|
 00000060  01 02 0e 0b 00 02 0a 00  02 07 00 02 04 30 82 02  |.............0..|
 00000070  00 30 82 01 62 02 09 00  b8 bf 2d 47 a0 d2 eb f4  |.0..b.....-G....|
 00000240  13 83 0d 94 06 bb d4 37  7a f6 ec 7a c9 86 2e dd  |.......7z..z....|
 00000250  d7 11 69 7f 85 7c 56 de  fb 31 78 2b e4 c7 78 0d  |..i..|V..1x+..x.|
 00000260  ae cb be 9e 4e 36 24 31  7b 6a 0f 39 95 12 07 8f  |....N6$1{j.9....|
-00000270  2a 16 03 01 00 d5 0c 00  00 d1 03 00 17 41 04 5e  |*............A.^|
-00000280  6d f1 9c b6 ec 5b c9 fc  16 b2 75 63 90 82 23 8f  |m....[....uc..#.|
-00000290  14 6f 53 b1 23 06 fa c7  b9 b4 d2 59 3d 4f 94 a2  |.oS.#......Y=O..|
-000002a0  02 af 0a 1e 94 f7 4d 31  c1 b3 a7 c4 67 89 67 da  |......M1....g.g.|
-000002b0  71 de 5a 1d ca 79 b8 f3  9e 6a 42 f8 60 8b 2a 00  |q.Z..y...jB.`.*.|
-000002c0  8a 30 81 87 02 42 00 85  32 00 54 ab af 7a 95 32  |.0...B..2.T..z.2|
-000002d0  f9 c7 e3 34 23 ed 1c 5d  7b a2 78 bf f1 a3 3f 53  |...4#..]{.x...?S|
-000002e0  ad c4 56 1f f6 98 9e 29  66 ef 52 21 80 d6 21 e7  |..V....)f.R!..!.|
-000002f0  61 fd 75 90 37 ec 13 0a  f4 c2 47 a4 a7 41 87 24  |a.u.7.....G..A.$|
-00000300  0c c6 c0 09 f0 54 c4 df  02 41 30 3e a5 6c a2 5d  |.....T...A0>.l.]|
-00000310  76 66 fb a2 f5 2d ef ee  63 63 b4 9a db 84 23 db  |vf...-..cc....#.|
-00000320  e9 0d 5f 43 cc 6b ef 4a  7f a2 1d 6b 6c 7f 1f ba  |.._C.k.J...kl...|
-00000330  52 5f 6c 3f cc 77 54 ef  75 1f c2 1e da 6f d3 07  |R_l?.wT.u....o..|
-00000340  23 7d 54 6d 40 88 0d 6e  72 90 8e 16 03 01 00 0e  |#}Tm@..nr.......|
-00000350  0d 00 00 06 03 01 02 40  00 00 0e 00 00 00        |.......@......|
+00000270  2a 16 03 01 00 d6 0c 00  00 d2 03 00 17 41 04 b1  |*............A..|
+00000280  0f 0f 4a 18 ed 25 32 b3  a3 19 ed 4b 61 b6 eb e4  |..J..%2....Ka...|
+00000290  d3 f7 77 13 ac 9f 60 c7  8d 6d cb f1 ee 99 1a 71  |..w...`..m.....q|
+000002a0  68 aa d3 a7 70 7f 38 d0  f6 23 ab 9a f6 dd 19 4f  |h...p.8..#.....O|
+000002b0  ce 10 ef d5 cf 64 85 2f  75 f6 20 06 4b f0 b9 00  |.....d./u. .K...|
+000002c0  8b 30 81 88 02 42 01 00  b9 6b 80 91 59 0a 48 3f  |.0...B...k..Y.H?|
+000002d0  72 16 96 8f 21 2c 28 e4  6d 03 74 66 35 16 7d ec  |r...!,(.m.tf5.}.|
+000002e0  c7 08 9b 52 b5 05 d9 38  d8 b7 51 42 a7 4a 9f 9b  |...R...8..QB.J..|
+000002f0  1a 37 14 de c5 f5 16 96  83 81 58 d3 a6 1e ce 8a  |.7........X.....|
+00000300  bc 19 47 30 fe c5 85 55  02 42 01 4f 61 59 68 85  |..G0...U.B.OaYh.|
+00000310  c7 64 23 22 f6 83 53 cc  58 38 25 b5 ce 74 c1 68  |.d#"..S.X8%..t.h|
+00000320  9f 32 72 33 ea c9 62 e0  26 63 92 e3 5f 34 10 0b  |.2r3..b.&c.._4..|
+00000330  3c d5 83 fe 9f 67 69 ef  33 6b 19 c1 ec d6 6c 35  |<....gi.3k....l5|
+00000340  89 33 17 d3 9d 93 e2 e5  6e 89 9a a1 16 03 01 00  |.3......n.......|
+00000350  0e 0d 00 00 06 03 01 02  40 00 00 0e 00 00 00     |........@......|
 >>> Flow 3 (client to server)
 00000000  16 03 01 01 fb 0b 00 01  f7 00 01 f4 00 01 f1 30  |...............0|
 00000010  82 01 ed 30 82 01 58 a0  03 02 01 02 02 01 00 30  |...0..X........0|
 00000220  a7 24 20 3e b2 56 1c ce  97 28 5e f8 2b 2d 4f 9e  |.$ >.V...(^.+-O.|
 00000230  f1 07 9f 6c 4b 5b 83 56  e2 32 42 e9 58 b6 d7 49  |...lK[.V.2B.X..I|
 00000240  a6 b5 68 1a 41 03 56 6b  dc 5a 89 16 03 01 00 86  |..h.A.Vk.Z......|
-00000250  0f 00 00 82 00 80 0d cf  9a c6 5e 57 60 b9 a4 87  |..........^W`...|
-00000260  ae 83 25 4a d5 af 02 69  4d a1 0b ac 0c 97 58 30  |..%J...iM.....X0|
-00000270  cc 9d 45 6a eb e3 a5 b1  f9 63 9c 05 04 8f 55 b3  |..Ej.....c....U.|
-00000280  5b 7c 5b f6 36 2d b1 89  84 21 ce a6 ce be 66 c6  |[|[.6-...!....f.|
-00000290  06 4a 07 8a a0 13 ee f1  52 ea 65 71 b7 49 b7 49  |.J......R.eq.I.I|
-000002a0  dc 0a d0 c6 c3 69 ef 67  97 6b d8 41 e0 d1 a4 66  |.....i.g.k.A...f|
-000002b0  cc 3c ba 4a 63 46 af e1  7f 16 2e 73 5c 69 17 45  |.<.JcF.....s\i.E|
-000002c0  b3 2d e2 7c 92 4c de 02  c6 97 d3 9c e0 d5 9c 22  |.-.|.L........."|
-000002d0  30 21 11 5e 1c 1a 14 03  01 00 01 01 16 03 01 00  |0!.^............|
-000002e0  30 db d7 9e 46 3e 5a 60  5d 21 22 34 ca 9a 3b 3c  |0...F>Z`]!"4..;<|
-000002f0  a6 41 12 42 b9 78 d7 39  4b 51 d0 d0 3e 7e ba 4c  |.A.B.x.9KQ..>~.L|
-00000300  7c 8d 4b 03 e5 fe 4c 59  a6 9f a1 5d 46 54 56 61  ||.K...LY...]FTVa|
-00000310  33                                                |3|
+00000250  0f 00 00 82 00 80 20 2c  5a 08 3a 00 33 50 19 b2  |...... ,Z.:.3P..|
+00000260  0f ba 6c 76 7f 5c 92 e2  78 55 3e 32 32 bb 33 bc  |..lv.\..xU>22.3.|
+00000270  ab a9 34 e0 83 cf 82 cd  9e 6b 3f 9d e6 49 61 29  |..4......k?..Ia)|
+00000280  8b b4 ed e8 12 cd a9 52  86 11 48 64 08 61 72 8d  |.......R..Hd.ar.|
+00000290  d6 6a ac 42 cc e4 07 5f  08 56 9f 2f c5 35 d3 9b  |.j.B..._.V./.5..|
+000002a0  e9 0d 91 82 c0 e9 bb 9f  a9 8f df 96 85 08 9a 69  |...............i|
+000002b0  a4 93 b3 72 37 ba f9 b1  a4 0b b0 9f 43 6a 15 ec  |...r7.......Cj..|
+000002c0  79 b8 fd 9c 1f 5f 0d 2c  56 33 c7 15 d5 4a b7 82  |y...._.,V3...J..|
+000002d0  ea 44 80 20 c5 80 14 03  01 00 01 01 16 03 01 00  |.D. ............|
+000002e0  30 c9 c0 7c d7 57 d3 00  ab 87 eb 78 56 6b a1 69  |0..|.W.....xVk.i|
+000002f0  1d fa ec ae 38 f3 ef 5d  49 19 0d 4b f0 73 63 af  |....8..]I..K.sc.|
+00000300  89 b6 cb 76 cf fb b9 c1  99 98 06 0a 54 67 a0 6e  |...v........Tg.n|
+00000310  e7                                                |.|
 >>> Flow 4 (server to client)
-00000000  14 03 01 00 01 01 16 03  01 00 30 72 c2 59 5a f3  |..........0r.YZ.|
-00000010  6f db 45 d0 4b 8c b9 49  25 25 cd eb 00 c7 99 6c  |o.E.K..I%%.....l|
-00000020  a7 4d 5a 4c f3 05 7d b0  fb 97 1a 40 0d 42 ca ad  |.MZL..}....@.B..|
-00000030  df 57 6c 47 40 13 49 47  09 7c 2e                 |.WlG@.IG.|.|
+00000000  14 03 01 00 01 01 16 03  01 00 30 20 db fd ed ed  |..........0 ....|
+00000010  7c d5 bf 8f 06 3b 86 1b  c1 60 7d a4 74 e9 a6 c9  ||....;...`}.t...|
+00000020  f5 7c c7 f4 65 91 06 d5  53 88 d7 57 a4 22 b6 1f  |.|..e...S..W."..|
+00000030  f1 02 e9 79 36 e6 a1 22  51 3a 4c                 |...y6.."Q:L|
 >>> Flow 5 (client to server)
-00000000  17 03 01 00 20 c5 99 ba  cc 39 bb db 07 27 3c 05  |.... ....9...'<.|
-00000010  fb 79 ce bd ec 8c 67 20  c8 46 3b ad bf 8c 66 fc  |.y....g .F;...f.|
-00000020  55 de 73 0f 71 17 03 01  00 20 7b 3e 2b 60 c0 df  |U.s.q.... {>+`..|
-00000030  ca b1 c4 19 28 de 3e 8e  20 8b 4a 4a 07 52 5f 8c  |....(.>. .JJ.R_.|
-00000040  1b 4f 54 15 69 31 f7 46  03 94 15 03 01 00 20 fc  |.OT.i1.F...... .|
-00000050  b9 06 b4 6d 60 28 3f 4f  b2 9a e8 1c 74 d3 15 a6  |...m`(?O....t...|
-00000060  7b 49 85 d0 2f 83 bf 4d  7f 60 6e 18 bf c7 34     |{I../..M.`n...4|
+00000000  17 03 01 00 20 00 66 51  6a 14 ca ea e2 21 48 74  |.... .fQj....!Ht|
+00000010  c4 c1 6e b9 8b 23 af 7c  33 c9 00 f8 0b ec ab 35  |..n..#.|3......5|
+00000020  e7 42 0a d1 ae 17 03 01  00 20 00 1c 6d 60 75 5d  |.B....... ..m`u]|
+00000030  b3 fb 40 2e e0 b7 0d 48  f4 87 ac d4 bf ea 01 0d  |..@....H........|
+00000040  fe 10 0d 05 04 43 6b 19  ed f2 15 03 01 00 20 f8  |.....Ck....... .|
+00000050  03 ac 62 4b 1f db 2e d2  4e 00 c3 a4 57 3c 0a 62  |..bK....N...W<.b|
+00000060  05 a0 ef bd 2b 9b 9a 63  27 72 d7 d8 f1 8d 84     |....+..c'r.....|
index c21579c93297adbb0a9d9a83f33ad97e1a4deb01..94e686004f99d3b86261d90a3cbbc97463be5643 100644 (file)
@@ -8,11 +8,11 @@
 00000060  19 00 0b 00 02 01 00 00  0d 00 0a 00 08 04 01 04  |................|
 00000070  03 02 01 02 03 ff 01 00  01 00                    |..........|
 >>> Flow 2 (server to client)
-00000000  16 03 01 00 51 02 00 00  4d 03 01 52 cc 57 58 bd  |....Q...M..R.WX.|
-00000010  25 74 73 5e 31 99 73 f1  c6 a1 9b f0 07 09 97 d7  |%ts^1.s.........|
-00000020  b6 b9 65 a3 08 16 0b 4c  4a 06 00 20 ef 0b 97 cb  |..e....LJ.. ....|
-00000030  3c cf 05 b6 fe 62 d7 15  29 cf c8 56 e0 7e d7 92  |<....b..)..V.~..|
-00000040  11 86 86 49 a0 b1 12 2f  dc 15 f7 67 00 05 00 00  |...I.../...g....|
+00000000  16 03 01 00 51 02 00 00  4d 03 01 53 04 f1 02 73  |....Q...M..S...s|
+00000010  ee 5f 70 a4 aa 0d be d7  46 a3 25 3f e3 5d ef 7b  |._p.....F.%?.].{|
+00000020  73 49 7c b6 82 4d 99 2f  31 fc 8b 20 2d a3 33 7c  |sI|..M./1.. -.3||
+00000030  a5 c3 85 86 ba 61 4d 05  b0 5e d3 5e 88 6e c3 4b  |.....aM..^.^.n.K|
+00000040  95 d3 e9 67 f1 96 24 58  7a 6f e6 c5 00 05 00 00  |...g..$Xzo......|
 00000050  05 ff 01 00 01 00 16 03  01 02 be 0b 00 02 ba 00  |................|
 00000060  02 b7 00 02 b4 30 82 02  b0 30 82 02 19 a0 03 02  |.....0...0......|
 00000070  01 02 02 09 00 85 b0 bb  a4 8a 7f b8 ca 30 0d 06  |.............0..|
 00000260  e6 bd 77 82 6f 23 b6 e0  bd a2 92 b7 3a ac e8 56  |..w.o#......:..V|
 00000270  f1 af 54 5e 46 87 e9 3b  33 e7 b8 28 b7 d6 c8 90  |..T^F..;3..(....|
 00000280  35 d4 1c 43 d1 30 6f 55  4e 0a 70 16 03 01 00 86  |5..C.0oUN.p.....|
-00000290  0f 00 00 82 00 80 41 42  bb be 9d a4 d3 e9 24 f2  |......AB......$.|
-000002a0  6c 7d b0 68 10 7a 88 e3  41 5d 24 f9 b2 a6 4c 31  |l}.h.z..A]$...L1|
-000002b0  2b 90 51 49 65 3d d4 4b  1f 69 93 91 c0 a2 ec 2c  |+.QIe=.K.i.....,|
-000002c0  d0 48 e1 64 7f ef 8b da  be 8c 0a 19 8b ff c7 37  |.H.d...........7|
-000002d0  3d b8 8c 6d 2f 28 bd ba  96 6e c5 ed fa 29 c5 42  |=..m/(...n...).B|
-000002e0  f2 24 b9 b5 55 1e 12 46  a5 bb 7c 41 2a b0 02 44  |.$..U..F..|A*..D|
-000002f0  68 89 e9 92 e3 ad 50 44  f1 18 e7 e4 ee 2c 74 40  |h.....PD.....,t@|
-00000300  82 39 a3 cf 30 54 fb 53  42 f1 18 28 8a f4 ef 65  |.9..0T.SB..(...e|
-00000310  f2 33 6a e7 2a 48 14 03  01 00 01 01 16 03 01 00  |.3j.*H..........|
-00000320  24 d8 a8 6d 1c 31 f7 cc  06 57 ef cf 7e 9b ac d3  |$..m.1...W..~...|
-00000330  6b 50 b2 d8 c6 41 a2 c8  a6 f5 53 b3 d4 af e8 71  |kP...A....S....q|
-00000340  88 9e 7e d9 57                                    |..~.W|
+00000290  0f 00 00 82 00 80 0f 4c  d2 b2 f0 94 6d 61 d1 2c  |.......L....ma.,|
+000002a0  db 6f 79 03 bd 40 b2 d2  1d 61 ef 83 1b 4a 0c 7b  |.oy..@...a...J.{|
+000002b0  c5 73 1e 1a 81 e7 67 0a  d6 aa 2d 04 04 cc 0e 4b  |.s....g...-....K|
+000002c0  2e da 96 7f 15 6c 05 ee  c4 53 7e 33 89 28 7d db  |.....l...S~3.(}.|
+000002d0  a1 77 43 ba a3 51 a9 1c  b9 f5 ec 9a 8d eb 2c 46  |.wC..Q........,F|
+000002e0  5c 33 59 6b 16 af de f4  9b 80 76 a3 22 30 5d bb  |\3Yk......v."0].|
+000002f0  02 b9 77 96 8a db 36 9f  54 95 00 d8 58 e1 aa 04  |..w...6.T...X...|
+00000300  98 c9 0c 32 ae 62 81 12  0c f6 1b 76 c6 58 a7 8c  |...2.b.....v.X..|
+00000310  0e d8 b7 8e ed 0f 14 03  01 00 01 01 16 03 01 00  |................|
+00000320  24 1d c0 20 02 2d da 69  54 29 8c ff af 5c 56 a8  |$.. .-.iT)...\V.|
+00000330  eb d0 09 95 29 8f 52 8c  e2 7b 9f 36 3e 47 a0 33  |....).R..{.6>G.3|
+00000340  2e 63 a2 24 93                                    |.c.$.|
 >>> Flow 4 (server to client)
-00000000  14 03 01 00 01 01 16 03  01 00 24 71 90 c8 76 87  |..........$q..v.|
-00000010  7f 07 da de 41 93 59 f4  e9 64 73 49 d3 15 b0 7d  |....A.Y..dsI...}|
-00000020  d7 86 06 85 62 71 9f 57  db 1a 81 82 ed 9b df     |....bq.W.......|
+00000000  14 03 01 00 01 01 16 03  01 00 24 99 e8 fb 65 f4  |..........$...e.|
+00000010  95 ae 8b 71 cc 5d a4 95  a7 27 98 fd 16 3f 7a 1a  |...q.]...'...?z.|
+00000020  b6 bd bf 0a 58 72 77 97  1f 8e b1 dd 4b 12 12     |....Xrw.....K..|
 >>> Flow 5 (client to server)
-00000000  17 03 01 00 1a 35 83 3e  0a 4d 7b 58 4d 6b 9e d7  |.....5.>.M{XMk..|
-00000010  77 32 dd b0 93 25 d1 fc  e2 08 ad 2c 91 fd ce 15  |w2...%.....,....|
-00000020  03 01 00 16 56 9c d9 6e  45 4c 95 8a 2d 75 7e 52  |....V..nEL..-u~R|
-00000030  0a 75 0a f0 a1 52 91 c4  d2 b4                    |.u...R....|
+00000000  17 03 01 00 1a 42 70 c0  89 78 12 5c 91 7e 88 2d  |.....Bp..x.\.~.-|
+00000010  2f 8f be f2 f2 12 9d 81  ae 78 08 38 5e 6d 1b 15  |/........x.8^m..|
+00000020  03 01 00 16 1a 64 b1 6f  8a ff d3 63 6a c7 b8 95  |.....d.o...cj...|
+00000030  3d b0 87 bc 62 e9 88 5b  26 bd                    |=...b..[&.|
index b89d02c3687f08e792c4015f7ce230566a08da9f..30c4c6b831af9000b001cba1c8526aa6a28fa1c0 100644 (file)
@@ -8,11 +8,11 @@
 00000060  19 00 0b 00 02 01 00 00  0d 00 0a 00 08 04 01 04  |................|
 00000070  03 02 01 02 03 ff 01 00  01 00                    |..........|
 >>> Flow 2 (server to client)
-00000000  16 03 01 00 59 02 00 00  55 03 01 52 cc 57 58 a5  |....Y...U..R.WX.|
-00000010  0d f3 9c e1 2f 44 99 dc  f2 26 bc c2 44 4a 0f ab  |..../D...&..DJ..|
-00000020  6d 40 3b 5a 98 21 43 9e  c0 39 03 20 03 46 6f c2  |m@;Z.!C..9. .Fo.|
-00000030  bd 1f bc cc 2d b2 eb 22  91 23 df 20 28 c5 df ea  |....-..".#. (...|
-00000040  70 1a dc 07 b9 19 a1 d0  03 74 81 a8 c0 09 00 00  |p........t......|
+00000000  16 03 01 00 59 02 00 00  55 03 01 53 04 f1 02 b2  |....Y...U..S....|
+00000010  e0 f6 f6 b5 c9 5b 28 d0  5d 58 1b 6f 4e 2b 9d 05  |.....[(.]X.oN+..|
+00000020  2a b9 b4 da 45 cf f3 10  b2 23 44 20 f8 4d 59 05  |*...E....#D .MY.|
+00000030  ad 27 f2 a0 ee 7f ec cc  20 dc e7 a2 1b 07 b3 a5  |.'...... .......|
+00000040  37 7e 61 3d d6 5c 03 cf  cc f5 9b ca c0 09 00 00  |7~a=.\..........|
 00000050  0d ff 01 00 01 00 00 0b  00 04 03 00 01 02 16 03  |................|
 00000060  01 02 0e 0b 00 02 0a 00  02 07 00 02 04 30 82 02  |.............0..|
 00000070  00 30 82 01 62 02 09 00  b8 bf 2d 47 a0 d2 eb f4  |.0..b.....-G....|
 00000240  13 83 0d 94 06 bb d4 37  7a f6 ec 7a c9 86 2e dd  |.......7z..z....|
 00000250  d7 11 69 7f 85 7c 56 de  fb 31 78 2b e4 c7 78 0d  |..i..|V..1x+..x.|
 00000260  ae cb be 9e 4e 36 24 31  7b 6a 0f 39 95 12 07 8f  |....N6$1{j.9....|
-00000270  2a 16 03 01 00 d5 0c 00  00 d1 03 00 17 41 04 3c  |*............A.<|
-00000280  f8 33 94 22 ad fb 45 01  10 5c 8e b1 09 19 da de  |.3."..E..\......|
-00000290  21 0a 09 72 af 25 6f 2f  63 4d 8c 6b b6 a6 00 02  |!..r.%o/cM.k....|
-000002a0  4d 6c fe 71 9e 45 74 13  db b5 7a a1 74 f0 16 35  |Ml.q.Et...z.t..5|
-000002b0  50 fd ab 45 4c 64 9f 4d  80 a9 5c 85 ee 20 33 00  |P..ELd.M..\.. 3.|
-000002c0  8a 30 81 87 02 42 01 73  bc ac 1a 30 11 5f 93 95  |.0...B.s...0._..|
-000002d0  00 03 ba d6 f3 02 cd c9  a0 15 bf 9a 1a 59 24 1e  |.............Y$.|
-000002e0  3e 99 6b da b1 81 e1 eb  da fd 8e 73 d6 94 1e ce  |>.k........s....|
-000002f0  04 cf 41 33 cd e5 8d f0  e2 50 b9 9b 49 df bc 51  |..A3.....P..I..Q|
-00000300  2d 5c 12 5c b3 8b 81 73  02 41 3c bf 11 5c 10 be  |-\.\...s.A<..\..|
-00000310  f2 e7 59 2f 9a d7 0e 49  a3 17 7d 20 cc bc 17 1f  |..Y/...I..} ....|
-00000320  2d 1b 19 90 52 c5 16 08  b3 3c b0 33 9d 03 45 75  |-...R....<.3..Eu|
-00000330  0c 3f ac 7b 0b b2 69 f4  a5 7c fc 49 a6 54 55 84  |.?.{..i..|.I.TU.|
-00000340  2f 19 f1 de 1b 92 3d fb  68 5d ea 16 03 01 00 04  |/.....=.h]......|
+00000270  2a 16 03 01 00 d5 0c 00  00 d1 03 00 17 41 04 da  |*............A..|
+00000280  5a fd 09 e5 d6 c0 70 41  5e 3a 87 eb df 0c ad 90  |Z.....pA^:......|
+00000290  22 8a 2f 90 81 0c 24 00  68 92 f3 d5 95 2f 93 43  |"./...$.h..../.C|
+000002a0  e9 58 2d 18 28 62 ee 33  5b 21 2e 49 87 21 4d 32  |.X-.(b.3[!.I.!M2|
+000002b0  32 19 b3 ba fe 2d 9a 85  12 0e a1 77 08 06 75 00  |2....-.....w..u.|
+000002c0  8a 30 81 87 02 42 01 91  14 fc 68 74 95 10 4b d4  |.0...B....ht..K.|
+000002d0  67 60 12 46 bb b0 f6 98  77 a3 41 b8 01 5c 49 54  |g`.F....w.A..\IT|
+000002e0  9e 3e 81 e7 97 a3 b9 73  6e 15 74 67 be e5 d9 eb  |.>.....sn.tg....|
+000002f0  8b 87 c5 22 ab ab 58 28  4f d1 b6 80 94 1b f5 f7  |..."..X(O.......|
+00000300  12 43 ef 0a c7 3e 1a 76  02 41 7a 00 49 cb 9f 3b  |.C...>.v.Az.I..;|
+00000310  91 6e 38 58 0a d3 d0 d1  ee 67 f0 b6 5d cd fa 23  |.n8X.....g..]..#|
+00000320  b6 98 43 af 9c 71 90 1e  1d 50 a2 6e 61 5b f2 92  |..C..q...P.na[..|
+00000330  b4 69 73 f2 3b 54 bf 1c  9d 05 19 97 e4 4e 41 9e  |.is.;T.......NA.|
+00000340  f2 9a 76 77 9a 86 43 1f  1f 30 a2 16 03 01 00 04  |..vw..C..0......|
 00000350  0e 00 00 00                                       |....|
 >>> Flow 3 (client to server)
 00000000  16 03 01 00 46 10 00 00  42 41 04 1e 18 37 ef 0d  |....F...BA...7..|
 00000020  a7 24 20 3e b2 56 1c ce  97 28 5e f8 2b 2d 4f 9e  |.$ >.V...(^.+-O.|
 00000030  f1 07 9f 6c 4b 5b 83 56  e2 32 42 e9 58 b6 d7 49  |...lK[.V.2B.X..I|
 00000040  a6 b5 68 1a 41 03 56 6b  dc 5a 89 14 03 01 00 01  |..h.A.Vk.Z......|
-00000050  01 16 03 01 00 30 c7 cf  21 f8 0c 94 5b 11 ef ee  |.....0..!...[...|
-00000060  d0 d3 3a d5 ba 2d 19 8b  5b 53 68 94 f1 49 8b 19  |..:..-..[Sh..I..|
-00000070  f3 80 d5 55 52 7d 72 af  38 73 35 df 6e 04 4b ca  |...UR}r.8s5.n.K.|
-00000080  64 b7 ff c3 e1 eb                                 |d.....|
+00000050  01 16 03 01 00 30 88 60  65 b2 d7 51 1f ad 96 56  |.....0.`e..Q...V|
+00000060  4e 0a 20 eb b5 b0 1a dd  4c f6 1a cf d4 5c 47 c4  |N. .....L....\G.|
+00000070  9c 7c a0 36 dd d1 1b 96  91 99 c0 a7 2d 9a 7c 42  |.|.6........-.|B|
+00000080  51 d1 de 87 2b a4                                 |Q...+.|
 >>> Flow 4 (server to client)
-00000000  14 03 01 00 01 01 16 03  01 00 30 59 2c 4c 03 7f  |..........0Y,L..|
-00000010  aa a8 ca d8 2a b0 35 71  ea 3a 2b d4 b1 9f 13 f9  |....*.5q.:+.....|
-00000020  c2 7b fb 21 52 15 73 e6  71 d4 65 8e 21 25 1a 63  |.{.!R.s.q.e.!%.c|
-00000030  03 d3 a5 6b 17 0e e7 18  84 17 d8                 |...k.......|
+00000000  14 03 01 00 01 01 16 03  01 00 30 86 6c b5 94 69  |..........0.l..i|
+00000010  2e e0 55 a2 4d a8 63 f2  5b 1f ae 34 21 c8 21 6a  |..U.M.c.[..4!.!j|
+00000020  00 b6 56 ed 4e 2a b0 ff  01 2f da ce a1 c0 41 03  |..V.N*.../....A.|
+00000030  a9 1b 6e 2e e1 88 50 ba  62 14 88                 |..n...P.b..|
 >>> Flow 5 (client to server)
-00000000  17 03 01 00 20 8d 73 59  92 ce 93 78 e6 63 cf 9d  |.... .sY...x.c..|
-00000010  bc 44 40 76 c7 66 3f 15  3a 0a 91 9b 94 49 ec cc  |.D@v.f?.:....I..|
-00000020  7c 1a 6c b2 85 17 03 01  00 20 2e 6a dd 85 be 12  ||.l...... .j....|
-00000030  c1 45 e4 04 bf 46 70 19  12 d1 1a 28 f6 22 30 a2  |.E...Fp....(."0.|
-00000040  fe 98 a8 11 c2 c4 19 46  c6 7e 15 03 01 00 20 a7  |.......F.~.... .|
-00000050  71 3d 87 94 9d 6c c5 dd  de 44 54 47 d5 06 37 82  |q=...l...DTG..7.|
-00000060  36 e0 c2 e5 91 74 a8 88  28 2f 87 7d a1 a7 e3     |6....t..(/.}...|
+00000000  17 03 01 00 20 a6 63 0a  2f a5 dc e1 fb cb 7b 1f  |.... .c./.....{.|
+00000010  f2 da 74 c3 ff e9 f5 8b  9c 5f 0c d3 f7 1f 44 e6  |..t......_....D.|
+00000020  90 13 5c 48 50 17 03 01  00 20 c7 75 b5 ff bc 09  |..\HP.... .u....|
+00000030  34 f2 45 db 0d 22 08 8e  f1 35 cd b6 0f b0 eb 2a  |4.E.."...5.....*|
+00000040  b7 1a d0 8e 14 a4 54 84  f9 dc 15 03 01 00 20 e0  |......T....... .|
+00000050  36 3d aa b3 a9 b4 20 23  ca 9e 8c 5d fc a8 c8 b7  |6=.... #...]....|
+00000060  f5 c2 b6 d0 5a e2 ce a5  7b 68 a0 48 86 95 6a     |....Z...{h.H..j|
index ae03ea4584dab042c8d12164ea6bb0fdd74cac97..868f0ceb0e5ec00eac528b31abd27607633420a7 100644 (file)
@@ -8,11 +8,11 @@
 00000060  19 00 0b 00 02 01 00 00  0d 00 0a 00 08 04 01 04  |................|
 00000070  03 02 01 02 03 ff 01 00  01 00                    |..........|
 >>> Flow 2 (server to client)
-00000000  16 03 01 00 59 02 00 00  55 03 01 52 cc 57 58 f3  |....Y...U..R.WX.|
-00000010  e4 e3 02 40 35 40 24 91  0a 0b 54 1b 46 0d c1 46  |...@5@$...T.F..F|
-00000020  7f 9c dd 08 ec 9c 63 73  13 cd e8 20 3c c2 72 a5  |......cs... <.r.|
-00000030  8d 4e 66 14 83 b1 27 c9  51 7e a6 46 7c 38 e1 66  |.Nf...'.Q~.F|8.f|
-00000040  3f f6 9c e2 8d e0 51 29  fc 76 ee d0 c0 13 00 00  |?.....Q).v......|
+00000000  16 03 01 00 59 02 00 00  55 03 01 53 04 f1 02 21  |....Y...U..S...!|
+00000010  67 b5 2b 34 fb 62 d7 36  4f cf 68 2e 29 39 d0 28  |g.+4.b.6O.h.)9.(|
+00000020  3a 02 32 82 8f 95 de 62  d6 03 77 20 e6 98 56 cd  |:.2....b..w ..V.|
+00000030  96 24 d1 b9 4d eb 51 19  bb b7 71 f4 9c 29 32 d4  |.$..M.Q...q..)2.|
+00000040  e5 c6 0a 54 e0 4a 20 29  3e bd 06 0d c0 13 00 00  |...T.J )>.......|
 00000050  0d ff 01 00 01 00 00 0b  00 04 03 00 01 02 16 03  |................|
 00000060  01 02 be 0b 00 02 ba 00  02 b7 00 02 b4 30 82 02  |.............0..|
 00000070  b0 30 82 02 19 a0 03 02  01 02 02 09 00 85 b0 bb  |.0..............|
 000002f0  5f 33 c4 b6 d8 c9 75 90  96 8c 0f 52 98 b5 cd 98  |_3....u....R....|
 00000300  1f 89 20 5f f2 a0 1c a3  1b 96 94 dd a9 fd 57 e9  |.. _..........W.|
 00000310  70 e8 26 6d 71 99 9b 26  6e 38 50 29 6c 90 a7 bd  |p.&mq..&n8P)l...|
-00000320  d9 16 03 01 00 cb 0c 00  00 c7 03 00 17 41 04 30  |.............A.0|
-00000330  a3 31 3d 66 ce 80 2a fb  1d 11 66 f6 35 cb c9 d2  |.1=f..*...f.5...|
-00000340  53 fd 18 37 84 75 7e 00  55 92 0c 6d e5 40 46 77  |S..7.u~.U..m.@Fw|
-00000350  d8 17 d4 81 13 74 90 e0  7c 4b 58 25 65 99 8e 18  |.....t..|KX%e...|
-00000360  9f d7 af e0 70 f8 c6 99  13 2d 0e 28 32 02 40 00  |....p....-.(2.@.|
-00000370  80 b3 34 d4 4a d7 c1 8e  1d f6 23 ef 04 0c d6 bb  |..4.J.....#.....|
-00000380  40 8c 85 0f ce a6 8e d2  29 3d 81 0f 47 ce 59 dc  |@.......)=..G.Y.|
-00000390  dc 56 68 b1 11 af 98 3a  07 4b 7a d9 6e 08 c5 ff  |.Vh....:.Kz.n...|
-000003a0  b2 54 06 72 f3 d2 19 22  df ee 90 fc 8a 4d 76 c1  |.T.r...".....Mv.|
-000003b0  c2 d4 af d7 77 82 79 3d  12 0f 9c 56 28 a6 43 ea  |....w.y=...V(.C.|
-000003c0  a3 71 c2 af bf 52 40 4a  fa c7 3e d3 ae 8e 84 42  |.q...R@J..>....B|
-000003d0  5f fd 9f a7 0f 94 8c fa  15 86 23 28 be 2b 3a 32  |_.........#(.+:2|
-000003e0  cb e5 18 5c 2d d6 d9 94  5f a4 b7 05 d0 a0 ab aa  |...\-..._.......|
-000003f0  c3 16 03 01 00 04 0e 00  00 00                    |..........|
+00000320  d9 16 03 01 00 cb 0c 00  00 c7 03 00 17 41 04 05  |.............A..|
+00000330  45 33 f8 4b e9 96 0e 4a  fd ec 54 76 21 9b 24 8a  |E3.K...J..Tv!.$.|
+00000340  75 0b 80 84 c7 30 2b 22  f0 85 57 a4 a9 79 d6 f6  |u....0+"..W..y..|
+00000350  6d 80 b0 71 d9 66 c9 6c  dd 76 fc 32 d0 c6 bc 52  |m..q.f.l.v.2...R|
+00000360  2f f1 c9 62 17 53 76 ec  be a6 1c 93 f2 b4 5d 00  |/..b.Sv.......].|
+00000370  80 72 d9 20 52 70 7c 03  b1 33 fa 51 23 cd 05 97  |.r. Rp|..3.Q#...|
+00000380  6f d6 89 2f 8d 2e 3a 17  32 eb f2 ff 6b 39 70 5e  |o../..:.2...k9p^|
+00000390  21 41 8d 69 02 c8 9a 17  19 e4 48 9b 51 c3 7f 9b  |!A.i......H.Q...|
+000003a0  8d 4a 83 97 07 0e 30 f1  8b 6b e9 92 12 01 d6 96  |.J....0..k......|
+000003b0  f2 1a a2 10 7f 59 87 16  1a fb 55 67 68 fc 78 c6  |.....Y....Ugh.x.|
+000003c0  57 ac 05 dd f3 6f 77 84  eb ae b0 33 2d 19 2c ba  |W....ow....3-.,.|
+000003d0  b8 ae 9f 95 69 85 95 45  5e 37 f4 17 17 9b 03 c1  |....i..E^7......|
+000003e0  50 b1 36 42 bd 60 5c 8b  d8 b6 f3 c8 34 c8 9d 9d  |P.6B.`\.....4...|
+000003f0  75 16 03 01 00 04 0e 00  00 00                    |u.........|
 >>> Flow 3 (client to server)
 00000000  16 03 01 00 46 10 00 00  42 41 04 1e 18 37 ef 0d  |....F...BA...7..|
 00000010  19 51 88 35 75 71 b5 e5  54 5b 12 2e 8f 09 67 fd  |.Q.5uq..T[....g.|
 00000020  a7 24 20 3e b2 56 1c ce  97 28 5e f8 2b 2d 4f 9e  |.$ >.V...(^.+-O.|
 00000030  f1 07 9f 6c 4b 5b 83 56  e2 32 42 e9 58 b6 d7 49  |...lK[.V.2B.X..I|
 00000040  a6 b5 68 1a 41 03 56 6b  dc 5a 89 14 03 01 00 01  |..h.A.Vk.Z......|
-00000050  01 16 03 01 00 30 34 e6  9a 20 8c 33 d7 85 ae 83  |.....04.. .3....|
-00000060  f1 8f 46 68 63 3f a3 23  88 f8 00 9b 01 a7 80 c6  |..Fhc?.#........|
-00000070  8b 45 6e 5a c1 2f 62 5f  70 b6 20 1e 58 18 53 6b  |.EnZ./b_p. .X.Sk|
-00000080  e2 cb ce 2c 97 7c                                 |...,.||
+00000050  01 16 03 01 00 30 ca d1  1b 08 27 9b 44 e7 e9 b4  |.....0....'.D...|
+00000060  90 16 4d 30 4e 65 5c 0d  47 ba 46 86 cf c9 80 e7  |..M0Ne\.G.F.....|
+00000070  64 31 f5 a1 9e dc 39 15  d3 be 16 4f c7 90 b6 62  |d1....9....O...b|
+00000080  5d 6d 7f 41 4e 3e                                 |]m.AN>|
 >>> Flow 4 (server to client)
-00000000  14 03 01 00 01 01 16 03  01 00 30 ff 81 ba a6 cc  |..........0.....|
-00000010  c5 25 83 eb 65 99 39 4d  19 39 c1 8c cf b5 30 58  |.%..e.9M.9....0X|
-00000020  aa 6f e1 f4 f7 da 88 14  39 c0 1b 5a f3 05 bd 8c  |.o......9..Z....|
-00000030  6b af 52 32 0f 1e 87 0c  7a 39 3a                 |k.R2....z9:|
+00000000  14 03 01 00 01 01 16 03  01 00 30 98 81 24 8e cd  |..........0..$..|
+00000010  b6 48 2f 80 de 8e 24 3c  cd 02 67 80 34 97 d7 92  |.H/...$<..g.4...|
+00000020  78 c2 44 3d 5d 05 eb 88  76 79 46 7a c3 fa ca 73  |x.D=]...vyFz...s|
+00000030  45 82 ad c1 81 00 ca 40  c1 2f 13                 |E......@./.|
 >>> Flow 5 (client to server)
-00000000  17 03 01 00 20 72 b0 d7  a5 88 02 64 29 65 4b d5  |.... r.....d)eK.|
-00000010  f7 0b 74 bd df af ce 2d  02 6b 01 37 fb 44 80 e6  |..t....-.k.7.D..|
-00000020  f4 e1 17 e6 7f 17 03 01  00 20 18 8a 94 81 86 56  |......... .....V|
-00000030  eb 7e 77 48 03 ce b4 8c  2b 75 b8 06 eb 66 5f 77  |.~wH....+u...f_w|
-00000040  df 98 1f cd 6d bd 39 38  06 fc 15 03 01 00 20 d1  |....m.98...... .|
-00000050  bd 74 3b eb a2 f6 be ad  ab df 5f 99 c2 92 fd 9b  |.t;......._.....|
-00000060  e7 9a 03 a5 f4 00 99 8c  f8 85 34 2d 15 a0 30     |..........4-..0|
+00000000  17 03 01 00 20 ee 19 59  67 67 a9 8b db 99 87 50  |.... ..Ygg.....P|
+00000010  01 e2 02 c1 d5 6d 36 79  af aa ec 1b 80 0e b6 5e  |.....m6y.......^|
+00000020  5f fa 03 01 cc 17 03 01  00 20 ec e2 04 b7 3b a5  |_........ ....;.|
+00000030  f2 e0 13 1f 17 48 e7 6e  d3 eb f0 fa 36 ef 6e 2e  |.....H.n....6.n.|
+00000040  fb ea c8 39 c4 5f 4b 28  d4 50 15 03 01 00 20 c7  |...9._K(.P.... .|
+00000050  45 ff fb c7 07 0c d8 0e  35 a3 c5 31 47 b7 03 0e  |E.......5..1G...|
+00000060  14 c8 29 fd 53 70 5f 15  ac d2 1c 4c 69 fb d6     |..).Sp_....Li..|
index 1a6c42c14581e82068c5041faac002d2db4de2a3..395d53bbabb0fb1e4b09974a1abba3c6506df36d 100644 (file)
@@ -8,11 +8,11 @@
 00000060  19 00 0b 00 02 01 00 00  0d 00 0a 00 08 04 01 04  |................|
 00000070  03 02 01 02 03 ff 01 00  01 00                    |..........|
 >>> Flow 2 (server to client)
-00000000  16 03 01 00 51 02 00 00  4d 03 01 52 cc 57 58 d1  |....Q...M..R.WX.|
-00000010  c7 22 72 55 68 fb 69 12  2d 85 02 b7 66 93 40 d1  |."rUh.i.-...f.@.|
-00000020  2d 32 57 a8 f2 06 62 93  a0 39 b6 20 08 37 44 11  |-2W...b..9. .7D.|
-00000030  73 db 68 79 6d 98 30 f7  bf a2 9b a3 cb c0 03 cb  |s.hym.0.........|
-00000040  7f 79 e9 31 08 ce 93 17  25 6d c3 c4 00 05 00 00  |.y.1....%m......|
+00000000  16 03 01 00 51 02 00 00  4d 03 01 53 04 f1 02 76  |....Q...M..S...v|
+00000010  e8 45 7f 57 f3 42 4b 33  0b 06 fa a6 fa c4 3d 84  |.E.W.BK3......=.|
+00000020  5a 45 dc 93 41 a5 8d 79  6e 8f 11 20 e7 c6 29 2b  |ZE..A..yn.. ..)+|
+00000030  ff 4a 6e 63 67 a6 10 cb  49 19 46 1e 5e 0a d5 70  |.Jncg...I.F.^..p|
+00000040  96 88 9a 32 48 ef c3 4a  45 4c 6d e0 00 05 00 00  |...2H..JELm.....|
 00000050  05 ff 01 00 01 00 16 03  01 02 be 0b 00 02 ba 00  |................|
 00000060  02 b7 00 02 b4 30 82 02  b0 30 82 02 19 a0 03 02  |.....0...0......|
 00000070  01 02 02 09 00 85 b0 bb  a4 8a 7f b8 ca 30 0d 06  |.............0..|
 00000060  e6 bd 77 82 6f 23 b6 e0  bd a2 92 b7 3a ac e8 56  |..w.o#......:..V|
 00000070  f1 af 54 5e 46 87 e9 3b  33 e7 b8 28 b7 d6 c8 90  |..T^F..;3..(....|
 00000080  35 d4 1c 43 d1 30 6f 55  4e 0a 70 14 03 01 00 01  |5..C.0oUN.p.....|
-00000090  01 16 03 01 00 24 54 26  aa 7d 70 99 51 f1 bc d6  |.....$T&.}p.Q...|
-000000a0  e9 0b 4b c7 b2 02 8c ed  36 c9 7f b8 f7 82 29 3a  |..K.....6.....):|
-000000b0  0b 6a c0 b4 ef 38 3f e8  8a bf                    |.j...8?...|
+00000090  01 16 03 01 00 24 cd c0  68 dc 2e 69 cc c7 5b c5  |.....$..h..i..[.|
+000000a0  3f bd 40 cf a0 0f 41 34  ce 16 37 10 26 c8 3f d1  |?.@...A4..7.&.?.|
+000000b0  46 3b ad 7b b0 31 f3 c5  36 e7                    |F;.{.1..6.|
 >>> Flow 4 (server to client)
-00000000  14 03 01 00 01 01 16 03  01 00 24 9a 52 2f b1 3c  |..........$.R/.<|
-00000010  71 92 5c 81 68 a8 27 4c  b8 a9 4e 69 81 41 01 a2  |q.\.h.'L..Ni.A..|
-00000020  99 d4 0c dc 15 7b 52 b5  ee be a6 6f 70 88 91     |.....{R....op..|
+00000000  14 03 01 00 01 01 16 03  01 00 24 ea 77 6f 3c 42  |..........$.wo<B|
+00000010  12 16 51 de e8 b6 f9 85  06 d9 6d 05 75 50 2b 27  |..Q.......m.uP+'|
+00000020  93 b7 6b 65 e9 14 99 48  53 3e be e4 be 03 5d     |..ke...HS>....]|
 >>> Flow 5 (client to server)
-00000000  17 03 01 00 1a b5 06 d1  18 a6 4b 26 21 47 be 14  |..........K&!G..|
-00000010  79 2e 63 49 84 1e 83 31  3b cb 97 14 94 5b 09 15  |y.cI...1;....[..|
-00000020  03 01 00 16 9d d6 d0 2a  65 f2 7e 31 20 e6 63 89  |.......*e.~1 .c.|
-00000030  b3 76 92 20 db b8 e6 25  54 1b                    |.v. ...%T.|
+00000000  17 03 01 00 1a 9e ae ca  55 df c4 d9 47 04 55 dd  |........U...G.U.|
+00000010  3b 33 e1 a6 16 6f a1 94  b1 9b 4d 0d cb 6c 3b 15  |;3...o....M..l;.|
+00000020  03 01 00 16 92 5d 76 07  e9 b7 31 29 09 c5 b1 09  |.....]v...1)....|
+00000030  2d 64 3d 85 8d f1 d1 40  54 b8                    |-d=....@T.|
index 935aac14910dbe64a59d4bdfe777518da2fb7b78..9f941f8ef18578130f61081153c3b7a461a64c72 100644 (file)
@@ -8,11 +8,11 @@
 00000060  19 00 0b 00 02 01 00 00  0d 00 0a 00 08 04 01 04  |................|
 00000070  03 02 01 02 03 ff 01 00  01 00                    |..........|
 >>> Flow 2 (server to client)
-00000000  16 03 02 00 59 02 00 00  55 03 02 52 cc 57 58 b6  |....Y...U..R.WX.|
-00000010  e0 47 f9 28 99 af 27 4c  21 20 d6 b1 ae 4e d4 62  |.G.(..'L! ...N.b|
-00000020  8a 24 f4 62 47 77 a5 78  4b d0 09 20 5b 17 6b 88  |.$.bGw.xK.. [.k.|
-00000030  8a ab 8c 0b 63 d9 c3 06  6d 13 e3 27 22 38 ee 69  |....c...m..'"8.i|
-00000040  88 7d e1 18 f8 ef 93 62  f4 06 1f ab c0 09 00 00  |.}.....b........|
+00000000  16 03 02 00 59 02 00 00  55 03 02 53 04 f1 02 1c  |....Y...U..S....|
+00000010  d1 1c 6a 5f 7a 5c 26 69  92 cd ee c3 57 ed 96 90  |..j_z\&i....W...|
+00000020  e3 c5 f1 ee 8b ee 99 5f  46 2c e6 20 c8 50 6a a4  |......._F,. .Pj.|
+00000030  4b 93 e6 da ba 6d d4 87  f6 75 a8 9d 44 db b5 43  |K....m...u..D..C|
+00000040  df 12 57 de a4 f1 bc fb  b8 7a 3f 6a c0 09 00 00  |..W......z?j....|
 00000050  0d ff 01 00 01 00 00 0b  00 04 03 00 01 02 16 03  |................|
 00000060  02 02 0e 0b 00 02 0a 00  02 07 00 02 04 30 82 02  |.............0..|
 00000070  00 30 82 01 62 02 09 00  b8 bf 2d 47 a0 d2 eb f4  |.0..b.....-G....|
 00000240  13 83 0d 94 06 bb d4 37  7a f6 ec 7a c9 86 2e dd  |.......7z..z....|
 00000250  d7 11 69 7f 85 7c 56 de  fb 31 78 2b e4 c7 78 0d  |..i..|V..1x+..x.|
 00000260  ae cb be 9e 4e 36 24 31  7b 6a 0f 39 95 12 07 8f  |....N6$1{j.9....|
-00000270  2a 16 03 02 00 d6 0c 00  00 d2 03 00 17 41 04 79  |*............A.y|
-00000280  48 bb 4c 30 4f 5c fe 5e  37 65 dc 65 90 d4 4a 7f  |H.L0O\.^7e.e..J.|
-00000290  8c fc 45 b6 4f fb 11 2b  7c 20 cf 94 83 79 f6 cc  |..E.O..+| ...y..|
-000002a0  c6 d6 44 0c ad ae 5c fc  fd 87 0f b9 51 03 dc 38  |..D...\.....Q..8|
-000002b0  d8 44 a8 75 18 7e d1 c6  3e 9e 0a 45 85 92 4b 00  |.D.u.~..>..E..K.|
-000002c0  8b 30 81 88 02 42 01 dd  a7 0b d6 69 3c b5 fc 06  |.0...B.....i<...|
-000002d0  8b c3 37 1d 12 be 0d 80  4a e4 d5 4e df c0 5c 7d  |..7.....J..N..\}|
-000002e0  ca 61 f2 3c 87 83 e0 de  22 0f 25 78 f0 21 ec ca  |.a.<....".%x.!..|
-000002f0  8e 62 28 24 10 3e d9 7d  30 d7 f1 bd dc c6 98 a9  |.b($.>.}0.......|
-00000300  c4 3f 85 8f 47 8c 8e b7  02 42 01 3c 61 1e ee e9  |.?..G....B.<a...|
-00000310  44 2d ae 57 d1 2f 04 59  16 f0 80 03 3e a1 fe 1f  |D-.W./.Y....>...|
-00000320  5f 54 b2 e1 b2 c0 07 3c  ad d9 9f 95 8d 79 7d 9b  |_T.....<.....y}.|
-00000330  8e 68 cc b9 7f 08 b3 c9  24 1f a9 37 18 60 52 54  |.h......$..7.`RT|
-00000340  5e 55 9c 36 1c 06 29 6b  7b 95 3e 73 16 03 02 00  |^U.6..)k{.>s....|
-00000350  04 0e 00 00 00                                    |.....|
+00000270  2a 16 03 02 00 d4 0c 00  00 d0 03 00 17 41 04 7b  |*............A.{|
+00000280  c4 00 37 35 51 de c3 f2  a4 95 2c 19 21 3e a6 94  |..75Q.....,.!>..|
+00000290  7b fd 04 d7 b7 1c 56 e6  af 3c ee 36 cb 55 e6 f0  |{.....V..<.6.U..|
+000002a0  e6 24 34 6b 8a 02 66 71  f9 e2 f5 a6 c9 d7 6c dc  |.$4k..fq......l.|
+000002b0  65 59 ff 1c c9 ec a9 8b  07 d6 52 2c 01 3c c3 00  |eY........R,.<..|
+000002c0  89 30 81 86 02 41 74 89  1a 31 72 e6 8b c0 4a ce  |.0...At..1r...J.|
+000002d0  8f 5a 49 a7 52 2d 6d b9  8b 50 17 62 2a 99 d6 3b  |.ZI.R-m..P.b*..;|
+000002e0  02 85 41 4d 34 53 b5 09  bd e3 ac 16 c1 9b e9 83  |..AM4S..........|
+000002f0  cc 83 e3 9c 23 34 67 71  72 d4 05 a2 34 f7 08 29  |....#4gqr...4..)|
+00000300  62 43 2e cc bc 08 01 02  41 59 de 5a d0 dd d7 6b  |bC......AY.Z...k|
+00000310  db 9c 35 29 79 f8 96 91  56 74 1f 18 7b ee 25 83  |..5)y...Vt..{.%.|
+00000320  f2 37 0e 77 ab 38 fb 5e  04 0b 09 d9 b4 1f 3f be  |.7.w.8.^......?.|
+00000330  2e e3 60 e3 96 f3 29 c1  6d 8f 56 1b fd 62 14 48  |..`...).m.V..b.H|
+00000340  e3 d9 2a ea 2f be 93 d0  8b 31 16 03 02 00 04 0e  |..*./....1......|
+00000350  00 00 00                                          |...|
 >>> Flow 3 (client to server)
 00000000  16 03 02 00 46 10 00 00  42 41 04 1e 18 37 ef 0d  |....F...BA...7..|
 00000010  19 51 88 35 75 71 b5 e5  54 5b 12 2e 8f 09 67 fd  |.Q.5uq..T[....g.|
 00000030  f1 07 9f 6c 4b 5b 83 56  e2 32 42 e9 58 b6 d7 49  |...lK[.V.2B.X..I|
 00000040  a6 b5 68 1a 41 03 56 6b  dc 5a 89 14 03 02 00 01  |..h.A.Vk.Z......|
 00000050  01 16 03 02 00 40 00 00  00 00 00 00 00 00 00 00  |.....@..........|
-00000060  00 00 00 00 00 00 42 9f  9d 29 fc 0e e3 9d 6d 89  |......B..)....m.|
-00000070  38 c3 6f d8 0f 0f 41 69  24 b8 44 a8 81 28 56 80  |8.o...Ai$.D..(V.|
-00000080  c2 a2 cd b8 27 84 4d f1  f0 5c dc df 94 a7 a4 05  |....'.M..\......|
-00000090  84 b9 f6 5c b4 50                                 |...\.P|
+00000060  00 00 00 00 00 00 b6 98  a2 a9 48 34 12 6b 0a 94  |..........H4.k..|
+00000070  89 fc 38 04 63 5a 6f 63  36 3e d9 35 12 64 8c 28  |..8.cZoc6>.5.d.(|
+00000080  99 a6 cf 2e 57 e3 14 6d  0a 8a ab f0 a6 58 37 7c  |....W..m.....X7||
+00000090  96 04 d3 71 bc d4                                 |...q..|
 >>> Flow 4 (server to client)
-00000000  14 03 02 00 01 01 16 03  02 00 40 5f 24 a7 68 9c  |..........@_$.h.|
-00000010  cc 93 4c fc a1 25 3d 71  43 88 d9 17 9b c9 99 6b  |..L..%=qC......k|
-00000020  01 00 24 a8 ca b3 52 3e  cf 2f f5 3e 80 16 c9 cb  |..$...R>./.>....|
-00000030  52 20 c3 f5 e0 8f a2 d9  f9 3d 86 c5 44 f6 52 31  |R .......=..D.R1|
-00000040  82 3a f9 fb d1 f6 43 5b  b8 28 bc                 |.:....C[.(.|
+00000000  14 03 02 00 01 01 16 03  02 00 40 c5 01 c9 0a b0  |..........@.....|
+00000010  d8 ca 5e c1 19 dc 37 6c  2e a0 b3 11 a8 87 65 5a  |..^...7l......eZ|
+00000020  09 41 b9 fe 53 c4 c9 76  97 6d 7f ac c0 be d2 07  |.A..S..v.m......|
+00000030  84 e5 5b 78 37 34 ee da  3b cb 3e 82 52 79 91 44  |..[x74..;.>.Ry.D|
+00000040  b4 e4 1c ec 3a c0 c0 9d  cd ff 13                 |....:......|
 >>> Flow 5 (client to server)
 00000000  17 03 02 00 30 00 00 00  00 00 00 00 00 00 00 00  |....0...........|
-00000010  00 00 00 00 00 18 60 dd  b8 15 76 04 ae 8c e9 09  |......`...v.....|
-00000020  84 8c c2 04 38 b2 45 3d  cb ea 9b cf 11 1a 38 67  |....8.E=......8g|
-00000030  cd ff 89 4c 8c 15 03 02  00 30 00 00 00 00 00 00  |...L.....0......|
-00000040  00 00 00 00 00 00 00 00  00 00 f3 b4 a0 68 1b 3b  |.............h.;|
-00000050  5c c7 e7 70 32 5f 19 4f  a4 bf 0a 57 bc a0 7d be  |\..p2_.O...W..}.|
-00000060  91 46 5a 8e 02 25 67 20  d0 c2                    |.FZ..%g ..|
+00000010  00 00 00 00 00 46 60 13  39 2b 2f 72 95 ed 0e aa  |.....F`.9+/r....|
+00000020  69 6e b4 64 3e 83 43 d0  f9 7f 37 7c 1d b9 ce 11  |in.d>.C...7|....|
+00000030  d9 41 66 60 6d 15 03 02  00 30 00 00 00 00 00 00  |.Af`m....0......|
+00000040  00 00 00 00 00 00 00 00  00 00 b1 26 d0 5d 08 98  |...........&.]..|
+00000050  eb 28 42 74 31 58 42 95  c5 ad 1a 92 0a f5 5f ed  |.(Bt1XB......._.|
+00000060  45 98 e0 90 e5 a3 b6 8b  8d 18                    |E.........|
index 9bfaa75deb12541b105e9b647aa0d38b6a07312c..fc723396a4f746d72d1c390331a0b22e50dcb24c 100644 (file)
@@ -8,11 +8,11 @@
 00000060  19 00 0b 00 02 01 00 00  0d 00 0a 00 08 04 01 04  |................|
 00000070  03 02 01 02 03 ff 01 00  01 00                    |..........|
 >>> Flow 2 (server to client)
-00000000  16 03 02 00 59 02 00 00  55 03 02 52 cc 57 58 39  |....Y...U..R.WX9|
-00000010  f8 48 dc e0 ba ad e3 f6  6e bf 6b 9e e0 34 53 4f  |.H......n.k..4SO|
-00000020  cd 16 7a 31 ac 23 de 31  3c 4b 0d 20 41 92 79 b5  |..z1.#.1<K. A.y.|
-00000030  e0 f6 1c 25 da db 35 3b  58 61 04 52 7f ac da 64  |...%..5;Xa.R...d|
-00000040  0d 31 f0 52 55 5d ec 37  94 21 aa 6f c0 13 00 00  |.1.RU].7.!.o....|
+00000000  16 03 02 00 59 02 00 00  55 03 02 53 04 f1 02 fe  |....Y...U..S....|
+00000010  17 8b 79 ad 93 2e d3 89  66 9b 5d 9b b4 03 3e ba  |..y.....f.]...>.|
+00000020  65 2a f1 55 f9 3c 33 de  2c a7 47 20 fa 4f 82 11  |e*.U.<3.,.G .O..|
+00000030  96 81 d0 70 2e 65 b3 68  2e 3a 6d d7 6c 74 22 33  |...p.e.h.:m.lt"3|
+00000040  d4 ae 6c aa c8 f0 c7 20  8b 10 21 e7 c0 13 00 00  |..l.... ..!.....|
 00000050  0d ff 01 00 01 00 00 0b  00 04 03 00 01 02 16 03  |................|
 00000060  02 02 be 0b 00 02 ba 00  02 b7 00 02 b4 30 82 02  |.............0..|
 00000070  b0 30 82 02 19 a0 03 02  01 02 02 09 00 85 b0 bb  |.0..............|
 000002f0  5f 33 c4 b6 d8 c9 75 90  96 8c 0f 52 98 b5 cd 98  |_3....u....R....|
 00000300  1f 89 20 5f f2 a0 1c a3  1b 96 94 dd a9 fd 57 e9  |.. _..........W.|
 00000310  70 e8 26 6d 71 99 9b 26  6e 38 50 29 6c 90 a7 bd  |p.&mq..&n8P)l...|
-00000320  d9 16 03 02 00 cb 0c 00  00 c7 03 00 17 41 04 0a  |.............A..|
-00000330  be f8 00 24 e0 50 bf 90  12 25 b9 26 3b 7f 4a a2  |...$.P...%.&;.J.|
-00000340  b3 4b b1 76 db 53 0d 83  9b 35 57 2f 64 ad 03 d1  |.K.v.S...5W/d...|
-00000350  f9 1b ef cf 48 8a a8 a9  4c df d3 7b 7d e8 a0 68  |....H...L..{}..h|
-00000360  2b 20 b4 4f 1c e3 11 1d  bf a9 bd 58 e4 4a 3e 00  |+ .O.......X.J>.|
-00000370  80 2a 64 fb 5f 8f f5 bb  6d 48 2c 7d a6 c0 f6 b2  |.*d._...mH,}....|
-00000380  3d 2b 75 83 07 fd b8 9d  50 a1 ec 1c 09 85 69 4f  |=+u.....P.....iO|
-00000390  a3 39 8e 2f b4 94 76 b1  8d 03 3f 76 01 7e 22 90  |.9./..v...?v.~".|
-000003a0  08 58 40 0d d7 65 c1 49  d5 7e 0d 28 62 ec b6 58  |.X@..e.I.~.(b..X|
-000003b0  6f ff 83 21 65 e7 8b f5  51 32 5f 39 e8 9e 85 d4  |o..!e...Q2_9....|
-000003c0  65 1c a8 a8 70 82 5e db  d7 c6 4a 2d 2e ef c3 b3  |e...p.^...J-....|
-000003d0  73 ca 13 5b 99 19 e3 4d  cb 4a 9f 42 3c b7 79 fb  |s..[...M.J.B<.y.|
-000003e0  70 33 f7 a7 59 cb 76 a7  7c f3 8a 9a 5d fd 12 74  |p3..Y.v.|...]..t|
-000003f0  5a 16 03 02 00 04 0e 00  00 00                    |Z.........|
+00000320  d9 16 03 02 00 cb 0c 00  00 c7 03 00 17 41 04 26  |.............A.&|
+00000330  56 18 02 e5 66 d4 aa 24  7e ae 39 e5 ca 78 6c c1  |V...f..$~.9..xl.|
+00000340  90 02 c3 c4 ad 79 2c 47  a8 bf 54 e2 8a 22 b6 ef  |.....y,G..T.."..|
+00000350  99 d4 7a 7f 8f 78 6a 78  4e 14 2a 16 0d bb 54 38  |..z..xjxN.*...T8|
+00000360  59 1f 7a 53 1b c7 73 10  89 4b de c3 66 39 7a 00  |Y.zS..s..K..f9z.|
+00000370  80 3a 88 38 c8 15 07 ab  2f 0f 0d cb 19 07 84 ac  |.:.8..../.......|
+00000380  24 fd 8b d2 9d 05 45 c6  11 c3 d6 84 58 95 5a 08  |$.....E.....X.Z.|
+00000390  b9 a4 2c c0 41 4e 34 e0  b2 24 98 94 b7 67 27 50  |..,.AN4..$...g'P|
+000003a0  ba 82 35 28 a9 bf 16 ee  e3 7b 49 9c 4c 81 80 69  |..5(.....{I.L..i|
+000003b0  d7 aa ed 46 ea 9a 68 c4  97 b7 11 d4 35 91 74 5e  |...F..h.....5.t^|
+000003c0  54 10 34 83 cd c4 06 18  49 7d 7a 28 c9 53 06 73  |T.4.....I}z(.S.s|
+000003d0  00 7b 04 b6 d8 36 a7 4b  67 7f 81 30 94 de 40 4d  |.{...6.Kg..0..@M|
+000003e0  18 f8 c4 b7 02 00 44 8e  bc 72 06 24 53 15 74 72  |......D..r.$S.tr|
+000003f0  8d 16 03 02 00 04 0e 00  00 00                    |..........|
 >>> Flow 3 (client to server)
 00000000  16 03 02 00 46 10 00 00  42 41 04 1e 18 37 ef 0d  |....F...BA...7..|
 00000010  19 51 88 35 75 71 b5 e5  54 5b 12 2e 8f 09 67 fd  |.Q.5uq..T[....g.|
 00000030  f1 07 9f 6c 4b 5b 83 56  e2 32 42 e9 58 b6 d7 49  |...lK[.V.2B.X..I|
 00000040  a6 b5 68 1a 41 03 56 6b  dc 5a 89 14 03 02 00 01  |..h.A.Vk.Z......|
 00000050  01 16 03 02 00 40 00 00  00 00 00 00 00 00 00 00  |.....@..........|
-00000060  00 00 00 00 00 00 9a 7d  f8 d5 af e5 5d 16 a2 39  |.......}....]..9|
-00000070  94 a5 de f6 ae 71 ba b7  b5 6e 9e a7 05 37 ed ff  |.....q...n...7..|
-00000080  b4 c3 d9 4e d5 d5 3c 84  7e 3f a4 68 23 3b 52 ab  |...N..<.~?.h#;R.|
-00000090  d7 30 3b 63 ed b1                                 |.0;c..|
+00000060  00 00 00 00 00 00 8a 87  81 38 35 c0 4c bb f8 12  |.........85.L...|
+00000070  fa 75 04 cd 1e 3a 61 96  93 c8 fb 07 d1 6d b4 55  |.u...:a......m.U|
+00000080  0f b5 0f 07 35 0a 96 ce  5c 6f 24 62 d3 68 e4 b0  |....5...\o$b.h..|
+00000090  5d be 81 37 c2 9c                                 |]..7..|
 >>> Flow 4 (server to client)
-00000000  14 03 02 00 01 01 16 03  02 00 40 df 38 36 f9 ee  |..........@.86..|
-00000010  03 f3 92 e1 3b 81 21 60  3a 46 3a 2c fc 2f 6a 01  |....;.!`:F:,./j.|
-00000020  a4 04 a6 f4 09 f9 bf 1f  73 a6 c6 04 83 8a ae 39  |........s......9|
-00000030  43 d7 8d 88 8e 6c f2 da  0d a9 82 b5 4e b0 41 c9  |C....l......N.A.|
-00000040  5a 00 93 9b 8c 60 3c 08  fa aa 91                 |Z....`<....|
+00000000  14 03 02 00 01 01 16 03  02 00 40 66 36 8d f8 8c  |..........@f6...|
+00000010  7f db 38 e8 39 df f8 2f  cb 88 9c 14 d9 89 10 b4  |..8.9../........|
+00000020  be 59 88 d7 f3 73 62 af  a3 42 66 6e 74 38 64 9f  |.Y...sb..Bfnt8d.|
+00000030  16 79 09 d7 14 7e 91 8a  70 73 63 28 30 58 fe cc  |.y...~..psc(0X..|
+00000040  42 45 d6 37 fb 9e 8c c1  01 af 34                 |BE.7......4|
 >>> Flow 5 (client to server)
 00000000  17 03 02 00 30 00 00 00  00 00 00 00 00 00 00 00  |....0...........|
-00000010  00 00 00 00 00 85 20 30  5e 6e e6 ab a0 2c 66 7a  |...... 0^n...,fz|
-00000020  f9 88 3b ee c9 5b 5c 0b  a3 7a 94 f0 8a ab 61 e0  |..;..[\..z....a.|
-00000030  7f 93 78 79 61 15 03 02  00 30 00 00 00 00 00 00  |..xya....0......|
-00000040  00 00 00 00 00 00 00 00  00 00 e0 de 16 23 01 0a  |.............#..|
-00000050  cf 90 1b 96 6a 14 f2 c9  af e2 20 49 ce b4 82 bf  |....j..... I....|
-00000060  bf ed 7a 28 e1 ed 8e e7  18 c6                    |..z(......|
+00000010  00 00 00 00 00 31 0b e3  9d 2a 05 83 19 7d 10 36  |.....1...*...}.6|
+00000020  23 dc da fe 00 ab d3 aa  8f ce 28 5f 08 fd b7 59  |#.........(_...Y|
+00000030  1e 00 2e 25 5a 15 03 02  00 30 00 00 00 00 00 00  |...%Z....0......|
+00000040  00 00 00 00 00 00 00 00  00 00 10 91 fd fa 59 07  |..............Y.|
+00000050  df 2c 92 25 15 7b 7c 83  44 89 0d 4f 65 43 99 2e  |.,.%.{|.D..OeC..|
+00000060  41 5d 51 c9 09 89 ed 02  08 bc                    |A]Q.......|
index 8483870fe8312786a7093ca8bb0bc0d9f3c688c6..f7be3f7e93ac838ddd0a4227b63d03279d0da8f6 100644 (file)
@@ -8,11 +8,11 @@
 00000060  19 00 0b 00 02 01 00 00  0d 00 0a 00 08 04 01 04  |................|
 00000070  03 02 01 02 03 ff 01 00  01 00                    |..........|
 >>> Flow 2 (server to client)
-00000000  16 03 02 00 51 02 00 00  4d 03 02 52 cc 57 58 d2  |....Q...M..R.WX.|
-00000010  3e 7a 93 49 2a f2 3f f8  fe 58 6b 88 d2 87 66 71  |>z.I*.?..Xk...fq|
-00000020  69 bd d2 98 03 4b 17 b1  5d 5b a5 20 c6 5d a6 c7  |i....K..][. .]..|
-00000030  40 53 b8 4f 68 74 12 70  64 58 4f 78 fa d1 a9 5c  |@S.Oht.pdXOx...\|
-00000040  f0 ce 5e 78 fd 66 30 98  8e ac 94 37 00 05 00 00  |..^x.f0....7....|
+00000000  16 03 02 00 51 02 00 00  4d 03 02 53 04 f1 02 d4  |....Q...M..S....|
+00000010  69 65 aa 96 3d 42 96 eb  9e 7d 8a 18 af 4c 7c 5d  |ie..=B...}...L|]|
+00000020  fb 97 5f da 94 62 13 69  1f 66 06 20 aa 52 e3 08  |.._..b.i.f. .R..|
+00000030  35 0a 87 d5 ef 93 49 ab  1a 74 dd 90 bd 69 70 d1  |5.....I..t...ip.|
+00000040  e9 f1 44 17 3a dc 33 98  f5 e5 ab 93 00 05 00 00  |..D.:.3.........|
 00000050  05 ff 01 00 01 00 16 03  02 02 be 0b 00 02 ba 00  |................|
 00000060  02 b7 00 02 b4 30 82 02  b0 30 82 02 19 a0 03 02  |.....0...0......|
 00000070  01 02 02 09 00 85 b0 bb  a4 8a 7f b8 ca 30 0d 06  |.............0..|
 00000060  e6 bd 77 82 6f 23 b6 e0  bd a2 92 b7 3a ac e8 56  |..w.o#......:..V|
 00000070  f1 af 54 5e 46 87 e9 3b  33 e7 b8 28 b7 d6 c8 90  |..T^F..;3..(....|
 00000080  35 d4 1c 43 d1 30 6f 55  4e 0a 70 14 03 02 00 01  |5..C.0oUN.p.....|
-00000090  01 16 03 02 00 24 6c 69  1d 96 27 2b f5 0b 6e d5  |.....$li..'+..n.|
-000000a0  f7 97 96 c6 9f 5e 59 92  9f 3a 0e e5 d0 36 e4 af  |.....^Y..:...6..|
-000000b0  bc 17 bf 95 ab f7 0c 19  a6 86                    |..........|
+00000090  01 16 03 02 00 24 07 9f  dc df 2d c3 a6 88 06 28  |.....$....-....(|
+000000a0  21 e0 e0 d3 31 99 fc 89  b8 82 6e 95 f4 4b 9e e2  |!...1.....n..K..|
+000000b0  d9 36 5c 14 ce d7 db e2  78 4e                    |.6\.....xN|
 >>> Flow 4 (server to client)
-00000000  14 03 02 00 01 01 16 03  02 00 24 dd 9f e5 d3 53  |..........$....S|
-00000010  ae af f2 41 bf eb 53 33  71 d0 f3 84 ac b0 88 72  |...A..S3q......r|
-00000020  2d de 14 5b 59 9d f3 4c  9f ab a1 aa f4 f3 af     |-..[Y..L.......|
+00000000  14 03 02 00 01 01 16 03  02 00 24 81 72 75 80 d4  |..........$.ru..|
+00000010  1b 1a 32 00 89 bf 9e 79  30 b9 6b 67 e0 8e c7 eb  |..2....y0.kg....|
+00000020  73 f2 e4 93 51 65 9b 5f  91 b1 b4 b1 f7 44 76     |s...Qe._.....Dv|
 >>> Flow 5 (client to server)
-00000000  17 03 02 00 1a 24 4d 6b  23 8a 36 07 80 49 1a e5  |.....$Mk#.6..I..|
-00000010  da 85 7c a0 cb f3 82 e5  23 d7 b9 46 82 cb bc 15  |..|.....#..F....|
-00000020  03 02 00 16 f3 f1 5b f2  40 0f 3f 88 ea f2 4e 28  |......[.@.?...N(|
-00000030  52 aa c6 3a 6c 88 e3 30  21 0f                    |R..:l..0!.|
+00000000  17 03 02 00 1a b2 91 39  63 c0 38 3c 4d 25 fd 14  |.......9c.8<M%..|
+00000010  b9 b6 e1 23 21 b4 8d 17  9e 1f d8 33 92 69 c2 15  |...#!......3.i..|
+00000020  03 02 00 16 4b 10 25 4d  9d 09 c2 11 96 be f7 5b  |....K.%M.......[|
+00000030  c2 9b 99 fd 1f 8e af 0f  2c 51                    |........,Q|
index 0731ee2c35b31b3a4677ab319a18e56cff5b11bf..20732703647551c4f5aca8d99d663ba0a24220dc 100644 (file)
@@ -8,11 +8,11 @@
 00000060  19 00 0b 00 02 01 00 00  0d 00 0a 00 08 04 01 04  |................|
 00000070  03 02 01 02 03 ff 01 00  01 00                    |..........|
 >>> Flow 2 (server to client)
-00000000  16 03 03 00 59 02 00 00  55 03 03 52 cc 57 59 76  |....Y...U..R.WYv|
-00000010  bc 07 88 2c 89 e3 aa 41  69 76 d1 0b 5f c0 7a b1  |...,...Aiv.._.z.|
-00000020  c3 c4 f8 12 6e 73 7f 5b  25 41 8e 20 b4 2d 8d a9  |....ns.[%A. .-..|
-00000030  80 95 44 d4 d7 47 8c a1  f1 de 36 57 cf 54 dd ee  |..D..G....6W.T..|
-00000040  a7 d8 ba cf ca b7 68 0e  9e b4 a3 7b c0 09 00 00  |......h....{....|
+00000000  16 03 03 00 59 02 00 00  55 03 03 53 04 f1 03 6f  |....Y...U..S...o|
+00000010  c6 4b 55 27 fe e8 fe 4d  7c 0e d4 20 98 b8 7c 81  |.KU'...M|.. ..|.|
+00000020  3d 31 f8 35 66 2f 0a 0b  f1 2c e3 20 86 4d 12 32  |=1.5f/...,. .M.2|
+00000030  73 e3 ba be 25 50 a4 a2  a1 7b f1 9a 76 7a 75 fb  |s...%P...{..vzu.|
+00000040  e2 64 a2 12 ec f3 e7 9d  9a 24 6e 94 c0 09 00 00  |.d.......$n.....|
 00000050  0d ff 01 00 01 00 00 0b  00 04 03 00 01 02 16 03  |................|
 00000060  03 02 0e 0b 00 02 0a 00  02 07 00 02 04 30 82 02  |.............0..|
 00000070  00 30 82 01 62 02 09 00  b8 bf 2d 47 a0 d2 eb f4  |.0..b.....-G....|
 00000240  13 83 0d 94 06 bb d4 37  7a f6 ec 7a c9 86 2e dd  |.......7z..z....|
 00000250  d7 11 69 7f 85 7c 56 de  fb 31 78 2b e4 c7 78 0d  |..i..|V..1x+..x.|
 00000260  ae cb be 9e 4e 36 24 31  7b 6a 0f 39 95 12 07 8f  |....N6$1{j.9....|
-00000270  2a 16 03 03 00 d8 0c 00  00 d4 03 00 17 41 04 3a  |*............A.:|
-00000280  16 95 9a af 75 ff 51 27  76 6e 82 c7 db a2 3d 62  |....u.Q'vn....=b|
-00000290  b4 65 d1 97 26 38 55 68  29 68 2c fc 4b 69 2c f9  |.e..&8Uh)h,.Ki,.|
-000002a0  01 00 4b 1e ee cf 88 54  a7 f3 49 a5 04 98 bd 12  |..K....T..I.....|
-000002b0  74 b1 cb 95 4c 49 c2 1a  31 e6 95 9f f5 0a f5 04  |t...LI..1.......|
-000002c0  03 00 8b 30 81 88 02 42  01 e7 72 2b 54 9e e0 97  |...0...B..r+T...|
-000002d0  79 e6 23 52 f7 2e 08 36  d5 dc 31 21 9d f2 13 ae  |y.#R...6..1!....|
-000002e0  30 86 1c df d2 be 52 e0  e7 8b f5 dd fa 09 f2 f3  |0.....R.........|
-000002f0  97 5d f7 48 37 83 9b aa  6c ef 87 95 de f4 50 19  |.].H7...l.....P.|
-00000300  b2 9b d6 27 4c 4d 67 6a  27 be 02 42 01 d8 86 63  |...'LMgj'..B...c|
-00000310  dc f1 9f 32 a3 25 6f 55  b3 67 7b 64 7e 24 38 25  |...2.%oU.g{d~$8%|
-00000320  96 bb 7b b1 9b ef 73 c3  6c d1 69 83 7d bc c5 30  |..{...s.l.i.}..0|
-00000330  fe 9a 21 0d 29 c4 d6 1a  51 a5 dd 6e a0 80 c8 9c  |..!.)...Q..n....|
-00000340  54 49 34 22 f7 5e cf 28  ae 2f cd 46 b1 71 16 03  |TI4".^.(./.F.q..|
-00000350  03 00 30 0d 00 00 28 03  01 02 40 00 20 06 01 06  |..0...(...@. ...|
-00000360  02 06 03 05 01 05 02 05  03 04 01 04 02 04 03 03  |................|
-00000370  01 03 02 03 03 02 01 02  02 02 03 01 01 00 00 0e  |................|
-00000380  00 00 00                                          |...|
+00000270  2a 16 03 03 00 d7 0c 00  00 d3 03 00 17 41 04 a3  |*............A..|
+00000280  03 8c de d2 b0 68 c8 25  0e 85 ea d7 ae 13 0d 79  |.....h.%.......y|
+00000290  ec 59 0d b5 4d 51 96 d9  7f 64 36 fb 4c d5 6a 26  |.Y..MQ...d6.L.j&|
+000002a0  ae 0e 48 61 df 5c 2b d4  ff 09 41 15 c4 14 8e 1b  |..Ha.\+...A.....|
+000002b0  84 a8 c8 cd ef 10 97 95  66 67 85 dd fd dc 2a 04  |........fg....*.|
+000002c0  03 00 8a 30 81 87 02 41  11 75 5d bc bd 08 28 d4  |...0...A.u]...(.|
+000002d0  5b 1b 45 7f 9c d3 8d 0b  91 fa f6 82 ba 59 bd 3e  |[.E..........Y.>|
+000002e0  96 01 c6 1d 38 db fe 08  e7 56 89 fc 10 b0 37 6a  |....8....V....7j|
+000002f0  3d d6 c9 50 16 53 f7 c2  a2 60 67 82 1f 74 b8 d5  |=..P.S...`g..t..|
+00000300  bc 02 ec 96 db 82 18 8c  87 02 42 01 0d df f7 b7  |..........B.....|
+00000310  05 3c 8c 56 f0 1d 33 18  cf c5 4c 80 7e 0b d9 f9  |.<.V..3...L.~...|
+00000320  f0 51 69 fe 5d b8 0b 64  c0 c7 0d f4 75 65 ae 07  |.Qi.]..d....ue..|
+00000330  9d cf f4 4b ad 52 f6 b8  10 26 18 bd d6 e2 0d a8  |...K.R...&......|
+00000340  80 10 50 34 15 cd 72 0b  7d a9 94 de 4c 16 03 03  |..P4..r.}...L...|
+00000350  00 30 0d 00 00 28 03 01  02 40 00 20 06 01 06 02  |.0...(...@. ....|
+00000360  06 03 05 01 05 02 05 03  04 01 04 02 04 03 03 01  |................|
+00000370  03 02 03 03 02 01 02 02  02 03 01 01 00 00 0e 00  |................|
+00000380  00 00                                             |..|
 >>> Flow 3 (client to server)
 00000000  16 03 03 02 0a 0b 00 02  06 00 02 03 00 02 00 30  |...............0|
 00000010  82 01 fc 30 82 01 5e 02  09 00 9a 30 84 6c 26 35  |...0..^....0.l&5|
 000002a0  b3 c1 85 6a 42 9b f9 7e  7e 31 c2 e5 bd 66 02 41  |...jB..~~1...f.A|
 000002b0  4b 49 c6 cd 02 e3 83 f7  03 50 18 6d b4 c9 51 02  |KI.......P.m..Q.|
 000002c0  c0 ab 87 bc e0 3e 4b 89  53 3a e2 65 89 97 02 c1  |.....>K.S:.e....|
-000002d0  88 a6 56 bf d0 53 0b a9  7e 82 7d 4d fc 66 78 8f  |..V..S..~.}M.fx.|
-000002e0  57 dc 5e 62 54 70 cc 32  0c 1c b5 62 fc 6a 76 7e  |W.^bTp.2...b.jv~|
-000002f0  3f 14 03 03 00 01 01 16  03 03 00 40 00 00 00 00  |?..........@....|
-00000300  00 00 00 00 00 00 00 00  00 00 00 00 62 a7 74 42  |............b.tB|
-00000310  ab 04 f8 f9 5a 86 3e 35  94 ce 4b 79 77 a1 a0 6a  |....Z.>5..Kyw..j|
-00000320  cf 2b 3a 62 fc 3b 50 2b  51 5e d7 6a d1 d0 65 b4  |.+:b.;P+Q^.j..e.|
-00000330  5d 15 99 11 10 d3 6b a3  97 d2 30 08              |].....k...0.|
+000002d0  88 0d 64 db 8e 4f 73 4e  ea 29 0b ed a0 f5 ce 3d  |..d..OsN.).....=|
+000002e0  5f cc 20 ef 0a 22 02 82  f2 14 2a b7 42 68 bd c7  |_. .."....*.Bh..|
+000002f0  4d 14 03 03 00 01 01 16  03 03 00 40 00 00 00 00  |M..........@....|
+00000300  00 00 00 00 00 00 00 00  00 00 00 00 f0 cc 4f c7  |..............O.|
+00000310  b6 0f c9 38 4d 4b 97 2c  4f be 53 08 4c d6 5b 4e  |...8MK.,O.S.L.[N|
+00000320  24 70 30 81 82 3a 7f 62  95 03 4d fc 54 78 ec 13  |$p0..:.b..M.Tx..|
+00000330  b2 a1 00 85 2b 04 e4 1d  7b 6e 87 60              |....+...{n.`|
 >>> Flow 4 (server to client)
-00000000  14 03 03 00 01 01 16 03  03 00 40 fa 8c c0 6f 59  |..........@...oY|
-00000010  8f af a8 21 c8 aa 4c 00  db 6a a5 98 09 52 1a 27  |...!..L..j...R.'|
-00000020  77 af 6d 13 c2 54 f8 90  31 37 5c 86 3b 66 de d8  |w.m..T..17\.;f..|
-00000030  41 96 8b 84 d9 75 c0 00  b5 5f 99 3e a9 19 fa 6b  |A....u..._.>...k|
-00000040  be e2 78 43 30 41 94 39  ce 87 67                 |..xC0A.9..g|
+00000000  14 03 03 00 01 01 16 03  03 00 40 d5 2a 76 79 1c  |..........@.*vy.|
+00000010  e7 d5 b1 5c 65 6b d1 45  73 53 4c 05 3a 6c 5d 81  |...\ek.EsSL.:l].|
+00000020  dd 2f f0 74 62 e4 8e f8  ed 21 99 c7 4f d6 28 40  |./.tb....!..O.(@|
+00000030  63 d9 6d e5 b0 04 73 27  7a 1d 08 19 31 10 da ef  |c.m...s'z...1...|
+00000040  79 26 33 fb 45 23 be a4  7c 03 66                 |y&3.E#..|.f|
 >>> Flow 5 (client to server)
 00000000  17 03 03 00 30 00 00 00  00 00 00 00 00 00 00 00  |....0...........|
-00000010  00 00 00 00 00 76 8b 21  f2 c9 fe b7 c4 60 e6 91  |.....v.!.....`..|
-00000020  00 ca 3e 54 25 5e f4 20  f8 df 58 41 03 b0 d0 fb  |..>T%^. ..XA....|
-00000030  e7 59 4d 6a 34 15 03 03  00 30 00 00 00 00 00 00  |.YMj4....0......|
-00000040  00 00 00 00 00 00 00 00  00 00 0f 0f e5 7a a2 86  |.............z..|
-00000050  8c 31 53 05 be 22 fb 53  51 e6 26 0a a5 c5 09 ca  |.1S..".SQ.&.....|
-00000060  f3 46 0f 67 50 d8 83 35  7b f3                    |.F.gP..5{.|
+00000010  00 00 00 00 00 e2 53 bd  c0 ef 9e e6 44 94 ea 5d  |......S.....D..]|
+00000020  f5 c5 a9 4b ed eb 1c 49  9f 79 44 f9 cd d7 de 02  |...K...I.yD.....|
+00000030  51 10 ae 87 7d 15 03 03  00 30 00 00 00 00 00 00  |Q...}....0......|
+00000040  00 00 00 00 00 00 00 00  00 00 d3 95 13 7f 5f 58  |.............._X|
+00000050  ab d6 17 ea 01 2c 2a ea  5d 7c 44 61 4a 27 97 52  |.....,*.]|DaJ'.R|
+00000060  cc 9b 86 f6 37 42 2b 94  01 49                    |....7B+..I|
index 9edab394f70eb3fe1c5d5b93c3cd8d4806acacad..c3b753a7b44dd8028631c20b30a1bd9c005e3583 100644 (file)
@@ -8,11 +8,11 @@
 00000060  19 00 0b 00 02 01 00 00  0d 00 0a 00 08 04 01 04  |................|
 00000070  03 02 01 02 03 ff 01 00  01 00                    |..........|
 >>> Flow 2 (server to client)
-00000000  16 03 03 00 51 02 00 00  4d 03 03 52 cc 57 59 85  |....Q...M..R.WY.|
-00000010  0f 00 df b8 0d ef c4 98  ba e3 bd 71 4c 98 e5 80  |...........qL...|
-00000020  af 77 c7 d8 5f c1 36 62  1c ef 89 20 b6 01 c3 3b  |.w.._.6b... ...;|
-00000030  9e 93 27 04 05 7b 73 b3  02 7b c3 89 f5 ac 51 24  |..'..{s..{....Q$|
-00000040  7c e5 6d f3 34 d0 99 71  5e ae 30 ea 00 05 00 00  ||.m.4..q^.0.....|
+00000000  16 03 03 00 51 02 00 00  4d 03 03 53 04 f1 03 b0  |....Q...M..S....|
+00000010  43 00 97 24 a7 a8 ea b2  24 fe 96 24 a1 49 64 fd  |C..$....$..$.Id.|
+00000020  1c a3 30 35 2d 85 a7 40  42 86 6b 20 af 27 7f ac  |..05-..@B.k .'..|
+00000030  8b 16 89 6c 78 b7 f5 29  02 58 a6 8b 61 43 c2 b0  |...lx..).X..aC..|
+00000040  e0 a8 96 c8 fa 2b 26 ad  9a 5f 2d d6 00 05 00 00  |.....+&.._-.....|
 00000050  05 ff 01 00 01 00 16 03  03 02 be 0b 00 02 ba 00  |................|
 00000060  02 b7 00 02 b4 30 82 02  b0 30 82 02 19 a0 03 02  |.....0...0......|
 00000070  01 02 02 09 00 85 b0 bb  a4 8a 7f b8 ca 30 0d 06  |.............0..|
 000002e0  b3 c1 85 6a 42 9b f9 7e  7e 31 c2 e5 bd 66 02 41  |...jB..~~1...f.A|
 000002f0  4b 49 c6 cd 02 e3 83 f7  03 50 18 6d b4 c9 51 02  |KI.......P.m..Q.|
 00000300  c0 ab 87 bc e0 3e 4b 89  53 3a e2 65 89 97 02 c1  |.....>K.S:.e....|
-00000310  88 d6 42 09 8f e0 57 a7  e6 9e 65 3d fd 82 45 9d  |..B...W...e=..E.|
-00000320  3e 69 cc ad 17 72 c7 98  8d 3a ca f0 05 63 43 40  |>i...r...:...cC@|
-00000330  7a 14 03 03 00 01 01 16  03 03 00 24 a8 45 b8 6e  |z..........$.E.n|
-00000340  21 28 b3 6d 94 0d 7b c4  af 24 0e ca 51 ec 85 3c  |!(.m..{..$..Q..<|
-00000350  87 43 fd 36 e4 3d 1a f4  28 df 90 19 b6 05 d0 c9  |.C.6.=..(.......|
+00000310  88 5a 97 82 3e 55 6b 7c  d8 db b8 cc 1b 30 84 0a  |.Z..>Uk|.....0..|
+00000320  7a 97 71 e4 10 bb a4 39  8c 2a cf f5 88 c7 d1 95  |z.q....9.*......|
+00000330  73 14 03 03 00 01 01 16  03 03 00 24 9f 1e f0 72  |s..........$...r|
+00000340  92 ea dc f7 56 96 37 e4  69 db db 66 1d f6 94 c4  |....V.7.i..f....|
+00000350  18 31 4f d0 5d c5 f4 53  21 aa 98 b1 dc 08 94 94  |.1O.]..S!.......|
 >>> Flow 4 (server to client)
-00000000  14 03 03 00 01 01 16 03  03 00 24 ae 0d d7 72 4f  |..........$...rO|
-00000010  70 d9 16 20 da 70 dd 04  d5 5a 96 32 80 8c 14 db  |p.. .p...Z.2....|
-00000020  4d 10 31 47 1e 26 78 f1  95 25 0c fe 24 2f 6f     |M.1G.&x..%..$/o|
+00000000  14 03 03 00 01 01 16 03  03 00 24 ee 68 c1 87 9f  |..........$.h...|
+00000010  d7 90 94 f1 3b 6d 26 0b  3d 89 7a 45 3b 52 5d 3c  |....;m&.=.zE;R]<|
+00000020  dd 7c c1 4e 57 3e a9 ee  91 be cf 2b a3 98 9d     |.|.NW>.....+...|
 >>> Flow 5 (client to server)
-00000000  17 03 03 00 1a d4 7d c7  83 f7 98 3b 77 91 4b e6  |......}....;w.K.|
-00000010  68 fa e2 22 37 24 93 20  fa e2 67 18 03 41 90 15  |h.."7$. ..g..A..|
-00000020  03 03 00 16 0b ff 73 1c  e5 81 5f 28 52 72 49 39  |......s..._(RrI9|
-00000030  ca 42 fb 57 8a 3a 80 75  2b ac                    |.B.W.:.u+.|
+00000000  17 03 03 00 1a 88 33 3e  2b 22 6b 92 d0 bb 8a 1e  |......3>+"k.....|
+00000010  9b f4 9e aa 91 8b 2b 95  ea 53 c8 03 0a 93 58 15  |......+..S....X.|
+00000020  03 03 00 16 c4 67 79 ba  ec cf 90 b1 f9 ac ec 64  |.....gy........d|
+00000030  72 01 08 8f 3a 98 aa 66  25 00                    |r...:..f%.|
index 2b380a8119f6ef8632f4e3058d68b0faa1f1a6d7..0037af61a03a9af5717cf6229db657120b6e1225 100644 (file)
@@ -8,11 +8,11 @@
 00000060  19 00 0b 00 02 01 00 00  0d 00 0a 00 08 04 01 04  |................|
 00000070  03 02 01 02 03 ff 01 00  01 00                    |..........|
 >>> Flow 2 (server to client)
-00000000  16 03 03 00 59 02 00 00  55 03 03 52 cc 57 58 6a  |....Y...U..R.WXj|
-00000010  f6 51 de a7 6b b0 94 bb  98 24 ab e2 c0 02 e4 8c  |.Q..k....$......|
-00000020  ac a8 48 5d 41 82 eb c0  de 5a 1e 20 70 29 82 6c  |..H]A....Z. p).l|
-00000030  01 cf 4e 21 52 18 98 8c  0a 31 14 26 6c a4 44 11  |..N!R....1.&l.D.|
-00000040  6c 5b d6 5c cb a4 b1 91  52 13 b5 77 c0 09 00 00  |l[.\....R..w....|
+00000000  16 03 03 00 59 02 00 00  55 03 03 53 04 f1 02 fd  |....Y...U..S....|
+00000010  41 bd ef ee f3 da fc 1a  31 8c 77 f2 e9 66 54 a0  |A.......1.w..fT.|
+00000020  f4 15 b1 1c 84 0d 6d 74  87 ac 7d 20 78 17 8b 08  |......mt..} x...|
+00000030  10 20 c9 44 e4 8a 43 af  4a c7 b8 3d 99 f2 f7 af  |. .D..C.J..=....|
+00000040  bb a3 21 2f 40 cc ed b6  da a8 a1 d5 c0 09 00 00  |..!/@...........|
 00000050  0d ff 01 00 01 00 00 0b  00 04 03 00 01 02 16 03  |................|
 00000060  03 02 0e 0b 00 02 0a 00  02 07 00 02 04 30 82 02  |.............0..|
 00000070  00 30 82 01 62 02 09 00  b8 bf 2d 47 a0 d2 eb f4  |.0..b.....-G....|
 00000240  13 83 0d 94 06 bb d4 37  7a f6 ec 7a c9 86 2e dd  |.......7z..z....|
 00000250  d7 11 69 7f 85 7c 56 de  fb 31 78 2b e4 c7 78 0d  |..i..|V..1x+..x.|
 00000260  ae cb be 9e 4e 36 24 31  7b 6a 0f 39 95 12 07 8f  |....N6$1{j.9....|
-00000270  2a 16 03 03 00 d8 0c 00  00 d4 03 00 17 41 04 de  |*............A..|
-00000280  c7 75 51 96 25 63 8e c0  da 9c c0 8e d1 76 ca 08  |.uQ.%c.......v..|
-00000290  4c f5 a0 13 75 01 ab cf  12 09 ae cd 13 53 84 1e  |L...u........S..|
-000002a0  a4 f8 46 0f 15 08 18 48  65 d9 75 85 80 38 79 b7  |..F....He.u..8y.|
-000002b0  fc 51 a2 7c 7a ac ab b9  67 8a d7 16 af 75 df 04  |.Q.|z...g....u..|
-000002c0  03 00 8b 30 81 88 02 42  00 fe 19 74 a9 a1 27 05  |...0...B...t..'.|
-000002d0  92 78 a4 c8 9d c8 a5 de  67 cd 1f a3 1b c7 ba 7e  |.x......g......~|
-000002e0  75 68 15 29 16 e7 91 f9  b9 7b d7 e4 e9 c0 2a 7f  |uh.).....{....*.|
-000002f0  1a ce 71 5a b9 c1 64 dd  26 59 24 e8 34 21 24 8a  |..qZ..d.&Y$.4!$.|
-00000300  b0 19 06 d4 46 0c 05 46  dc 5f 02 42 00 a8 05 71  |....F..F._.B...q|
-00000310  59 ef 60 fa cf 06 ec 18  5f be 53 b7 95 07 05 5b  |Y.`....._.S....[|
-00000320  70 87 ac da f4 61 94 41  60 c8 ab 7b 26 5a 32 4d  |p....a.A`..{&Z2M|
-00000330  0d 53 00 0e 8e b3 b2 16  c7 4e 3a 15 14 76 bc dc  |.S.......N:..v..|
-00000340  ff b6 60 eb 62 b6 15 34  53 4f 2a 42 48 2c 16 03  |..`.b..4SO*BH,..|
+00000270  2a 16 03 03 00 d8 0c 00  00 d4 03 00 17 41 04 a9  |*............A..|
+00000280  19 8b d9 9b 5c 7c 6a 7d  85 d2 70 4e 89 7e 0b 5b  |....\|j}..pN.~.[|
+00000290  dd 5e a1 63 8d 15 bc 0b  0c 47 3d 4d e8 a7 56 88  |.^.c.....G=M..V.|
+000002a0  2e f6 7f e2 4d fc ed cc  03 ed a1 2d ac ae 81 a5  |....M......-....|
+000002b0  e2 6d 7f 9f a3 93 e9 10  c1 0e 48 1b f3 f4 38 04  |.m........H...8.|
+000002c0  03 00 8b 30 81 88 02 42  00 87 fe 7e 63 82 14 57  |...0...B...~c..W|
+000002d0  dc 7d e2 0f cc 97 2d ba  3c a7 56 4a 17 a8 09 6a  |.}....-.<.VJ...j|
+000002e0  28 2e f2 66 1a 3f 2d 48  2b 6f 79 a1 60 cd 5e 10  |(..f.?-H+oy.`.^.|
+000002f0  0b 0a 28 f2 5f e4 3f 4f  f9 c9 91 34 d9 dc bc fc  |..(._.?O...4....|
+00000300  98 ea 77 0b 99 f8 a2 11  c4 bd 02 42 01 a0 b0 dc  |..w........B....|
+00000310  db 5b c2 09 99 bd ee a0  b9 aa 31 b9 10 84 22 be  |.[........1...".|
+00000320  5a 63 12 5a 43 00 8e c1  33 cc 91 bb c2 70 7a 63  |Zc.ZC...3....pzc|
+00000330  19 82 c0 74 48 a1 c7 3d  1f f1 6f 4a 6f 6a 8c 3f  |...tH..=..oJoj.?|
+00000340  28 31 a8 0c 65 19 26 62  4b 7a 7c 4b ea 1a 16 03  |(1..e.&bKz|K....|
 00000350  03 00 30 0d 00 00 28 03  01 02 40 00 20 06 01 06  |..0...(...@. ...|
 00000360  02 06 03 05 01 05 02 05  03 04 01 04 02 04 03 03  |................|
 00000370  01 03 02 03 03 02 01 02  02 02 03 01 01 00 00 0e  |................|
 00000220  a7 24 20 3e b2 56 1c ce  97 28 5e f8 2b 2d 4f 9e  |.$ >.V...(^.+-O.|
 00000230  f1 07 9f 6c 4b 5b 83 56  e2 32 42 e9 58 b6 d7 49  |...lK[.V.2B.X..I|
 00000240  a6 b5 68 1a 41 03 56 6b  dc 5a 89 16 03 03 00 88  |..h.A.Vk.Z......|
-00000250  0f 00 00 84 04 01 00 80  42 bd a6 a2 1f 49 ea 57  |........B....I.W|
-00000260  43 59 a0 b6 9f 1d b3 3c  35 a7 fa c7 57 02 fa f0  |CY.....<5...W...|
-00000270  9f 7d a2 77 1a a4 e1 13  c0 78 a3 9e 59 1a f2 c6  |.}.w.....x..Y...|
-00000280  2b dd 8d bc 5d f1 99 e8  db ac a2 9c 6e d8 b5 6a  |+...].......n..j|
-00000290  a5 04 aa 6c 48 b8 7a 06  55 81 1a e0 41 69 28 42  |...lH.z.U...Ai(B|
-000002a0  53 fa f0 63 a9 b5 c1 2f  69 60 cf 02 da d2 eb c8  |S..c.../i`......|
-000002b0  e8 a1 5c 7b b1 05 c3 b8  66 34 fe 21 8a 7a 84 10  |..\{....f4.!.z..|
-000002c0  f1 4d bd b1 68 97 85 11  10 bf be 96 cf 37 e9 68  |.M..h........7.h|
-000002d0  20 ab d2 f8 d2 20 65 90  14 03 03 00 01 01 16 03  | .... e.........|
+00000250  0f 00 00 84 04 01 00 80  38 f2 16 e5 b5 86 16 62  |........8......b|
+00000260  86 e1 7d 01 f1 a8 e1 f7  e7 85 b1 a0 17 ee 84 25  |..}............%|
+00000270  cb 3c 46 61 1a 78 7b 1e  ee 32 bc d9 6c fa 6b 76  |.<Fa.x{..2..l.kv|
+00000280  67 a7 9e c8 7a 4c e8 79  0d 22 27 ad e7 98 6a 98  |g...zL.y."'...j.|
+00000290  89 88 8b a9 69 5b 6f c6  00 48 9a 21 77 a9 7c 15  |....i[o..H.!w.|.|
+000002a0  ba 47 16 74 8d 6c 67 dc  6d f1 98 b6 61 e8 bc 08  |.G.t.lg.m...a...|
+000002b0  18 53 a6 93 bf fc 27 5e  b7 4d d2 eb 68 e9 23 ee  |.S....'^.M..h.#.|
+000002c0  d2 70 d2 55 2c c7 99 7d  c0 66 b5 1c ea 38 71 5c  |.p.U,..}.f...8q\|
+000002d0  a6 57 1f 52 e4 8e e8 51  14 03 03 00 01 01 16 03  |.W.R...Q........|
 000002e0  03 00 40 00 00 00 00 00  00 00 00 00 00 00 00 00  |..@.............|
-000002f0  00 00 00 a1 73 35 05 15  f3 87 00 58 06 54 f7 44  |....s5.....X.T.D|
-00000300  25 71 3f a6 16 3c e9 95  0a de 14 9d d0 78 73 35  |%q?..<.......xs5|
-00000310  a4 84 dc 9e bf 94 4e fe  07 89 33 d0 81 ef 9f 49  |......N...3....I|
-00000320  17 0a 64                                          |..d|
+000002f0  00 00 00 5e e7 6e 1c a2  02 24 34 f0 a6 b6 27 ea  |...^.n...$4...'.|
+00000300  69 d5 0e 2e a8 ad 5c ad  6c 06 78 68 39 92 27 f1  |i.....\.l.xh9.'.|
+00000310  e8 35 49 67 4d fb 5d 8a  31 2e 4e 3f 19 ed ea 30  |.5IgM.].1.N?...0|
+00000320  20 60 e1                                          | `.|
 >>> Flow 4 (server to client)
-00000000  14 03 03 00 01 01 16 03  03 00 40 19 b8 9d de 48  |..........@....H|
-00000010  86 1e be b4 62 54 57 12  fc f0 ad fd 70 b7 69 48  |....bTW.....p.iH|
-00000020  58 66 8e 68 1c fd 73 62  da bd 6d 8f 83 8c 09 91  |Xf.h..sb..m.....|
-00000030  de 53 83 20 2b 81 f1 30  3f 0b 6b 7d c1 59 47 dc  |.S. +..0?.k}.YG.|
-00000040  e1 f4 95 85 d5 0b 09 45  4e a7 2f                 |.......EN./|
+00000000  14 03 03 00 01 01 16 03  03 00 40 ee a8 82 bc 3f  |..........@....?|
+00000010  bf ab a6 e4 30 e0 3d f1  2f 19 a2 ac 7a 81 57 f1  |....0.=./...z.W.|
+00000020  ee 67 3f 55 2b 30 fa 72  b5 10 03 ec 8d 0a 8f bb  |.g?U+0.r........|
+00000030  24 f5 45 f5 4e 53 4b 93  a5 0d 42 6c 46 69 98 fb  |$.E.NSK...BlFi..|
+00000040  63 c5 9f 95 65 d1 b6 f0  a4 15 bd                 |c...e......|
 >>> Flow 5 (client to server)
 00000000  17 03 03 00 30 00 00 00  00 00 00 00 00 00 00 00  |....0...........|
-00000010  00 00 00 00 00 69 73 64  71 d8 dc 9b a5 ce 9a 1e  |.....isdq.......|
-00000020  65 82 01 94 45 12 e7 13  4e 3e 59 7b 19 2b 41 a7  |e...E...N>Y{.+A.|
-00000030  49 aa 70 1d ce 15 03 03  00 30 00 00 00 00 00 00  |I.p......0......|
-00000040  00 00 00 00 00 00 00 00  00 00 5f 67 8d e3 77 5e  |.........._g..w^|
-00000050  32 c4 83 91 2b e6 37 76  18 11 8d 9f e5 c2 20 43  |2...+.7v...... C|
-00000060  c1 68 83 f9 e6 0b 07 95  ef b6                    |.h........|
+00000010  00 00 00 00 00 cb 4e bc  d1 a9 58 ef c8 39 a9 36  |......N...X..9.6|
+00000020  f4 35 05 96 8e a4 50 bc  f4 15 06 f9 fd 41 6d 1e  |.5....P......Am.|
+00000030  5e 7c 82 63 94 15 03 03  00 30 00 00 00 00 00 00  |^|.c.....0......|
+00000040  00 00 00 00 00 00 00 00  00 00 bd 77 87 a5 5a d4  |...........w..Z.|
+00000050  b8 59 e6 6b 0f dd ea f9  ed 18 b2 9f a9 61 b4 3a  |.Y.k.........a.:|
+00000060  47 15 15 3b 83 ef e1 6d  db a8                    |G..;...m..|
index dc56555cf1dc51308dcd3dbccfdbba6c94d9c79d..df3eaa4406e1e2ad8cc8e220b5935d7e601354f3 100644 (file)
@@ -8,11 +8,11 @@
 00000060  19 00 0b 00 02 01 00 00  0d 00 0a 00 08 04 01 04  |................|
 00000070  03 02 01 02 03 ff 01 00  01 00                    |..........|
 >>> Flow 2 (server to client)
-00000000  16 03 03 00 51 02 00 00  4d 03 03 52 cc 57 58 8f  |....Q...M..R.WX.|
-00000010  4b 2e 8c 90 dd 88 33 43  44 02 90 c0 b5 ae b3 b2  |K.....3CD.......|
-00000020  c9 2c cd c4 53 8e 7e 5c  de 08 fe 20 ee 4e 7b b1  |.,..S.~\... .N{.|
-00000030  7e a2 eb b0 21 e3 b2 1e  b5 18 ff b3 43 c8 a7 a3  |~...!.......C...|
-00000040  2f ef 82 11 ae 66 be c5  64 5d 15 59 00 05 00 00  |/....f..d].Y....|
+00000000  16 03 03 00 51 02 00 00  4d 03 03 53 04 f1 02 1d  |....Q...M..S....|
+00000010  0e dc 86 e5 a9 07 71 46  15 34 af 47 15 3f 03 9c  |......qF.4.G.?..|
+00000020  fc d6 fd 44 7c f4 f1 c7  8d 6f f8 20 28 ea 3c dc  |...D|....o. (.<.|
+00000030  b2 4c b7 ba 20 88 c4 db  a5 73 ea 93 ab 3a 85 a6  |.L.. ....s...:..|
+00000040  8f 59 49 d9 a9 31 14 d5  a6 2b 4f d1 00 05 00 00  |.YI..1...+O.....|
 00000050  05 ff 01 00 01 00 16 03  03 02 be 0b 00 02 ba 00  |................|
 00000060  02 b7 00 02 b4 30 82 02  b0 30 82 02 19 a0 03 02  |.....0...0......|
 00000070  01 02 02 09 00 85 b0 bb  a4 8a 7f b8 ca 30 0d 06  |.............0..|
 00000260  e6 bd 77 82 6f 23 b6 e0  bd a2 92 b7 3a ac e8 56  |..w.o#......:..V|
 00000270  f1 af 54 5e 46 87 e9 3b  33 e7 b8 28 b7 d6 c8 90  |..T^F..;3..(....|
 00000280  35 d4 1c 43 d1 30 6f 55  4e 0a 70 16 03 03 00 88  |5..C.0oUN.p.....|
-00000290  0f 00 00 84 04 01 00 80  0a eb 78 3e 77 c5 9c 8e  |..........x>w...|
-000002a0  05 67 4e 06 bd ec c8 7c  b2 16 13 7d c5 5d e1 1a  |.gN....|...}.]..|
-000002b0  4d 2e f4 b1 78 2c 94 1b  47 e4 3e 2f 7e 74 cc 03  |M...x,..G.>/~t..|
-000002c0  1d eb e2 56 99 0e 92 27  79 e4 7e a7 12 86 88 91  |...V...'y.~.....|
-000002d0  a4 99 db 1d 50 62 88 41  cc a6 b7 90 d0 51 66 47  |....Pb.A.....QfG|
-000002e0  92 39 a0 2b 4c 8d 50 a6  af f4 72 2d 16 3b 6b b8  |.9.+L.P...r-.;k.|
-000002f0  82 d2 9a d8 cd 7b b9 aa  82 a5 31 12 9e bb 19 23  |.....{....1....#|
-00000300  15 5d ad a5 b1 65 c1 de  01 7d e3 d3 16 73 28 d6  |.]...e...}...s(.|
-00000310  75 02 32 68 5a e0 b1 bb  14 03 03 00 01 01 16 03  |u.2hZ...........|
-00000320  03 00 24 cd c6 25 df 1c  0b b3 a7 2a 46 99 b8 10  |..$..%.....*F...|
-00000330  37 78 40 2a aa 66 7a 3a  34 8e 87 bf 85 4c e2 de  |7x@*.fz:4....L..|
-00000340  36 62 4a 9e 7f e8 e8                              |6bJ....|
+00000290  0f 00 00 84 04 01 00 80  2a 1f ae 48 9f 86 16 dc  |........*..H....|
+000002a0  c2 55 1f 5f 95 81 ed 56  00 5d 35 46 e5 b6 57 d5  |.U._...V.]5F..W.|
+000002b0  a6 3e 32 38 8b e2 c6 1c  b9 b1 38 b2 da 66 45 ed  |.>28......8..fE.|
+000002c0  58 6a 7f 43 41 93 a5 09  da b9 04 ce 3f 13 8a 19  |Xj.CA.......?...|
+000002d0  13 e9 2c 1f c5 e7 35 b4  2d ea 7c 81 90 33 c0 66  |..,...5.-.|..3.f|
+000002e0  dc 41 8b 23 08 8f 69 d4  d6 a2 5f c1 bd 26 e6 2e  |.A.#..i..._..&..|
+000002f0  7f c8 7c a8 2d d4 08 95  ce 6e 58 54 04 a2 a6 63  |..|.-....nXT...c|
+00000300  54 72 67 f2 7f 61 0a 6b  58 46 d4 88 95 38 37 f2  |Trg..a.kXF...87.|
+00000310  93 95 48 56 14 a7 b9 7c  14 03 03 00 01 01 16 03  |..HV...|........|
+00000320  03 00 24 64 bb 41 3a cb  a2 2f 95 53 5c 2f f7 83  |..$d.A:../.S\/..|
+00000330  a2 35 18 f6 d0 8d 6f e2  54 ed 2f 07 10 f4 36 e2  |.5....o.T./...6.|
+00000340  3d e5 30 1d e3 63 01                              |=.0..c.|
 >>> Flow 4 (server to client)
-00000000  14 03 03 00 01 01 16 03  03 00 24 20 68 ec 6b 11  |..........$ h.k.|
-00000010  e5 d6 eb c7 4f 7f e0 d0  c4 36 d4 c2 46 06 1d 92  |....O....6..F...|
-00000020  83 b4 77 e2 58 b6 cb 06  74 0a 5c f8 c6 06 83     |..w.X...t.\....|
+00000000  14 03 03 00 01 01 16 03  03 00 24 0a 22 b6 bc da  |..........$."...|
+00000010  34 38 53 8e 80 e2 25 7b  31 2f 70 8e 3a db e8 a3  |48S...%{1/p.:...|
+00000020  70 0e 88 22 b4 a8 be d4  a3 e3 cc 13 94 ef 47     |p.."..........G|
 >>> Flow 5 (client to server)
-00000000  17 03 03 00 1a bb d6 71  f3 e0 2f c3 d6 0d 70 85  |.......q../...p.|
-00000010  25 df 6f ba b1 37 dd 49  42 79 5c b3 c1 85 7e 15  |%.o..7.IBy\...~.|
-00000020  03 03 00 16 8c 93 50 30  45 38 da e2 bf c6 df d8  |......P0E8......|
-00000030  41 e1 63 ca 28 82 d1 ae  30 76                    |A.c.(...0v|
+00000000  17 03 03 00 1a b4 9c b1  57 ea 01 03 fe 01 e7 1e  |........W.......|
+00000010  c4 a7 0f 25 14 99 00 4f  88 51 c1 98 6e 99 01 15  |...%...O.Q..n...|
+00000020  03 03 00 16 2e c4 11 8b  1a fc 37 81 18 33 e4 9f  |..........7..3..|
+00000030  48 a3 29 e3 ad 9b 9b ec  9f 99                    |H.).......|
index 47bbebfc45799a7db74a13a04fe399067c05c8b1..76445903bac0ba846ae084e9b10236c710ba5616 100644 (file)
@@ -8,11 +8,11 @@
 00000060  19 00 0b 00 02 01 00 00  0d 00 0a 00 08 04 01 04  |................|
 00000070  03 02 01 02 03 ff 01 00  01 00                    |..........|
 >>> Flow 2 (server to client)
-00000000  16 03 03 00 59 02 00 00  55 03 03 52 cc 57 58 4e  |....Y...U..R.WXN|
-00000010  9d 88 aa 42 ea 19 04 55  fe e8 13 0c db 2d a9 70  |...B...U.....-.p|
-00000020  93 85 12 08 d2 15 f6 61  25 84 b7 20 55 a3 15 3b  |.......a%.. U..;|
-00000030  27 48 55 53 d1 23 c5 e8  f4 83 ce 44 db 2c 47 b8  |'HUS.#.....D.,G.|
-00000040  2b 4a 3e be 29 3d 73 76  dc 79 6b 75 c0 09 00 00  |+J>.)=sv.yku....|
+00000000  16 03 03 00 59 02 00 00  55 03 03 53 04 f1 02 a0  |....Y...U..S....|
+00000010  5f bd a4 8d 98 93 b8 da  08 86 9f b2 be 9a a4 91  |_...............|
+00000020  2b 3c 1f 18 f0 75 7c a9  a8 a0 f7 20 4a 89 9a d2  |+<...u|.... J...|
+00000030  34 3b d9 b1 c2 fd 61 bd  97 19 22 ce b9 d1 5b a7  |4;....a..."...[.|
+00000040  83 80 9c 19 d0 f5 a0 aa  4c ac 06 20 c0 09 00 00  |........L.. ....|
 00000050  0d ff 01 00 01 00 00 0b  00 04 03 00 01 02 16 03  |................|
 00000060  03 02 0e 0b 00 02 0a 00  02 07 00 02 04 30 82 02  |.............0..|
 00000070  00 30 82 01 62 02 09 00  b8 bf 2d 47 a0 d2 eb f4  |.0..b.....-G....|
 00000240  13 83 0d 94 06 bb d4 37  7a f6 ec 7a c9 86 2e dd  |.......7z..z....|
 00000250  d7 11 69 7f 85 7c 56 de  fb 31 78 2b e4 c7 78 0d  |..i..|V..1x+..x.|
 00000260  ae cb be 9e 4e 36 24 31  7b 6a 0f 39 95 12 07 8f  |....N6$1{j.9....|
-00000270  2a 16 03 03 00 d8 0c 00  00 d4 03 00 17 41 04 67  |*............A.g|
-00000280  9e b0 72 cf 69 da 56 8c  d2 8f fb 80 64 bb d2 ec  |..r.i.V.....d...|
-00000290  57 2d 8c 77 b7 5a b5 bc  ae bb 1f de db e7 64 5c  |W-.w.Z........d\|
-000002a0  36 2a 9b cb 92 a7 f1 db  93 76 8c 10 b5 74 ad b5  |6*.......v...t..|
-000002b0  8f c5 79 18 18 e7 92 6f  22 3b 92 47 dc 60 cf 04  |..y....o";.G.`..|
-000002c0  03 00 8b 30 81 88 02 42  01 c9 0b bf 07 18 e3 7d  |...0...B.......}|
-000002d0  26 b0 17 94 70 1d dc e8  6c 9e 45 4f d5 e0 79 00  |&...p...l.EO..y.|
-000002e0  2b 03 90 bb 7a aa 23 01  43 53 a1 d8 9f 81 1f 18  |+...z.#.CS......|
-000002f0  a2 5f 54 fa 7c c3 3a 15  d4 18 38 80 7e de f0 70  |._T.|.:...8.~..p|
-00000300  42 5d 2a 22 74 d1 6c 75  f5 18 02 42 00 a8 0a 3c  |B]*"t.lu...B...<|
-00000310  44 38 fa 3f b7 fc 00 1e  80 30 0d e5 55 87 99 53  |D8.?.....0..U..S|
-00000320  0a ab cd ad dd 1b 72 7f  49 bf 2f 89 74 72 11 3c  |......r.I./.tr.<|
-00000330  6e 49 5e 31 3c 6c f1 bc  90 fc e9 75 95 78 7b 95  |nI^1<l.....u.x{.|
-00000340  6a 8d 4d 51 0f e1 2f d1  9e 7b b7 c1 aa 62 16 03  |j.MQ../..{...b..|
-00000350  03 00 04 0e 00 00 00                              |.......|
+00000270  2a 16 03 03 00 d7 0c 00  00 d3 03 00 17 41 04 3c  |*............A.<|
+00000280  8f 35 1e 47 5d 7b ad 13  0c e9 5c c0 97 c7 83 06  |.5.G]{....\.....|
+00000290  49 0f 6c cf e5 4d 3b ed  f7 1b c6 96 8d ba 54 35  |I.l..M;.......T5|
+000002a0  7f df 35 e3 6e 28 e9 71  f2 24 b5 ab 17 2b 4b 2b  |..5.n(.q.$...+K+|
+000002b0  0c 8f 9f 48 89 73 8f 09  69 84 af 7f ec 43 7a 04  |...H.s..i....Cz.|
+000002c0  03 00 8a 30 81 87 02 41  79 84 43 0c 78 fa 7e e2  |...0...Ay.C.x.~.|
+000002d0  c5 51 c1 60 88 c4 4a 59  7d 02 fa dc 19 68 33 ed  |.Q.`..JY}....h3.|
+000002e0  19 ef a1 df ef 6b 21 a6  98 aa ba a9 13 70 91 0f  |.....k!......p..|
+000002f0  cc 6c 5c 1e 99 53 1b 42  51 6c 06 a7 3c c4 04 22  |.l\..S.BQl..<.."|
+00000300  5d 0d c1 30 ab e3 ec b4  54 02 42 01 15 15 1a 6e  |]..0....T.B....n|
+00000310  6f f1 c6 b1 10 84 2c c8  04 de 2b 52 d5 b4 f7 c9  |o.....,...+R....|
+00000320  4f 6d 0e 0e 26 45 1d 7a  28 59 2b 8b f6 92 3a 23  |Om..&E.z(Y+...:#|
+00000330  7a 39 9c d5 4e cc 5d c5  45 92 9c d0 5f 33 12 e3  |z9..N.].E..._3..|
+00000340  2b 29 39 52 bb 16 aa e1  72 9e b5 fe 99 16 03 03  |+)9R....r.......|
+00000350  00 04 0e 00 00 00                                 |......|
 >>> Flow 3 (client to server)
 00000000  16 03 03 00 46 10 00 00  42 41 04 1e 18 37 ef 0d  |....F...BA...7..|
 00000010  19 51 88 35 75 71 b5 e5  54 5b 12 2e 8f 09 67 fd  |.Q.5uq..T[....g.|
 00000030  f1 07 9f 6c 4b 5b 83 56  e2 32 42 e9 58 b6 d7 49  |...lK[.V.2B.X..I|
 00000040  a6 b5 68 1a 41 03 56 6b  dc 5a 89 14 03 03 00 01  |..h.A.Vk.Z......|
 00000050  01 16 03 03 00 40 00 00  00 00 00 00 00 00 00 00  |.....@..........|
-00000060  00 00 00 00 00 00 ea 9c  f3 e5 4d 7c 48 a5 48 9b  |..........M|H.H.|
-00000070  87 43 2a 6b 74 e4 8e ca  e4 1c c9 87 46 c2 d7 ef  |.C*kt.......F...|
-00000080  5e 8f c8 60 a9 1c 5f 68  c6 a9 7d 39 a5 8d 13 7c  |^..`.._h..}9...||
-00000090  bd 31 6e 19 8d 95                                 |.1n...|
+00000060  00 00 00 00 00 00 20 a3  f8 5a e2 ea f3 09 19 3e  |...... ..Z.....>|
+00000070  4a 54 69 70 06 5b 17 35  0f ed e7 30 3b 6f eb a1  |JTip.[.5...0;o..|
+00000080  cb 9c 35 81 10 2e 34 f7  12 a5 e4 63 20 b2 65 31  |..5...4....c .e1|
+00000090  19 da 30 43 39 59                                 |..0C9Y|
 >>> Flow 4 (server to client)
-00000000  14 03 03 00 01 01 16 03  03 00 40 b1 d2 81 e7 2c  |..........@....,|
-00000010  ae 4a 72 98 91 5a 36 ae  8b 62 4b 58 55 f0 65 30  |.Jr..Z6..bKXU.e0|
-00000020  9a 8d ca 73 13 9f 90 c0  18 fb 10 07 c9 fa 9c aa  |...s............|
-00000030  67 2a 42 90 11 e5 38 7f  2f 35 ac d7 c7 75 cc 84  |g*B...8./5...u..|
-00000040  a4 73 dd 9c f5 0d 3e 5d  25 76 c6                 |.s....>]%v.|
+00000000  14 03 03 00 01 01 16 03  03 00 40 8d 4d 31 07 df  |..........@.M1..|
+00000010  ab 41 f5 19 9c 1a 57 fc  33 ab 5f e6 bd 45 b9 fa  |.A....W.3._..E..|
+00000020  7f db c0 df 72 f2 3b ef  aa d4 5e 34 e6 3d 44 7c  |....r.;...^4.=D||
+00000030  12 05 c7 57 da 54 b1 e3  66 f0 0a ab cd 15 a5 bf  |...W.T..f.......|
+00000040  c5 c2 07 a9 d9 a7 2e 5e  29 da da                 |.......^)..|
 >>> Flow 5 (client to server)
 00000000  17 03 03 00 30 00 00 00  00 00 00 00 00 00 00 00  |....0...........|
-00000010  00 00 00 00 00 5c f6 d4  35 91 79 5d 47 e6 7d eb  |.....\..5.y]G.}.|
-00000020  a4 ba d8 94 ee 89 71 60  ba 67 e6 58 79 d7 52 38  |......q`.g.Xy.R8|
-00000030  35 07 0a 90 e4 15 03 03  00 30 00 00 00 00 00 00  |5........0......|
-00000040  00 00 00 00 00 00 00 00  00 00 73 92 bf 53 22 d7  |..........s..S".|
-00000050  80 54 7a 1c 77 d9 a5 16  05 68 c7 c7 5b ce 05 41  |.Tz.w....h..[..A|
-00000060  51 d2 a6 2b b9 ba 34 ef  93 5e                    |Q..+..4..^|
+00000010  00 00 00 00 00 dc 03 7b  29 2c 49 64 58 2d dc f7  |.......{),IdX-..|
+00000020  26 a1 3b ec 2d e8 30 c4  6c a3 ff e2 bc b5 a4 a6  |&.;.-.0.l.......|
+00000030  93 ce 14 bd da 15 03 03  00 30 00 00 00 00 00 00  |.........0......|
+00000040  00 00 00 00 00 00 00 00  00 00 a6 77 10 30 15 eb  |...........w.0..|
+00000050  ed cf 73 5b 74 5d 09 52  4a 5b e2 f0 e4 67 f8 7a  |..s[t].RJ[...g.z|
+00000060  5e 5e fc ba 7f 80 0a d2  f4 fb                    |^^........|
index 155fab6b2ae30eac2cada2a4f6e7fbcafb84af7f..fb5af17f0c4392762de38a5633dc8ff98d29b6c2 100644 (file)
@@ -8,11 +8,11 @@
 00000060  19 00 0b 00 02 01 00 00  0d 00 0a 00 08 04 01 04  |................|
 00000070  03 02 01 02 03 ff 01 00  01 00                    |..........|
 >>> Flow 2 (server to client)
-00000000  16 03 03 00 59 02 00 00  55 03 03 52 cc 57 58 95  |....Y...U..R.WX.|
-00000010  a0 c0 f9 1d 34 16 31 f5  a5 08 dd 3d 6a 4c c9 96  |....4.1....=jL..|
-00000020  aa 8b 7f f3 1e 0c 59 4c  06 c1 2b 20 22 f5 fb 09  |......YL..+ "...|
-00000030  f2 7a c3 22 85 2f 16 b6  81 2d 2b d6 12 c2 4d 84  |.z."./...-+...M.|
-00000040  7e a9 3f 18 f2 1c f7 44  6c 66 3f 7f c0 2b 00 00  |~.?....Dlf?..+..|
+00000000  16 03 03 00 59 02 00 00  55 03 03 53 04 f1 02 48  |....Y...U..S...H|
+00000010  03 36 01 05 56 6f f0 54  d2 c3 d3 41 c2 e2 69 7b  |.6..Vo.T...A..i{|
+00000020  50 f8 03 ef 3f 5d 7c e6  9c cb fe 20 82 a0 81 fd  |P...?]|.... ....|
+00000030  72 4b b8 e6 29 76 3b 0f  1d 0a b7 82 9d 0b cf a0  |rK..)v;.........|
+00000040  65 b1 56 53 c9 d5 58 7b  f0 b6 2d cf c0 2b 00 00  |e.VS..X{..-..+..|
 00000050  0d ff 01 00 01 00 00 0b  00 04 03 00 01 02 16 03  |................|
 00000060  03 02 0e 0b 00 02 0a 00  02 07 00 02 04 30 82 02  |.............0..|
 00000070  00 30 82 01 62 02 09 00  b8 bf 2d 47 a0 d2 eb f4  |.0..b.....-G....|
 00000240  13 83 0d 94 06 bb d4 37  7a f6 ec 7a c9 86 2e dd  |.......7z..z....|
 00000250  d7 11 69 7f 85 7c 56 de  fb 31 78 2b e4 c7 78 0d  |..i..|V..1x+..x.|
 00000260  ae cb be 9e 4e 36 24 31  7b 6a 0f 39 95 12 07 8f  |....N6$1{j.9....|
-00000270  2a 16 03 03 00 d8 0c 00  00 d4 03 00 17 41 04 dd  |*............A..|
-00000280  b5 88 3a be 12 a5 fe 0e  19 f5 76 4f 71 90 93 ca  |..:.......vOq...|
-00000290  0e b3 62 a2 b0 c0 f2 78  95 90 cb 10 f9 e8 c8 8e  |..b....x........|
-000002a0  a6 5e 48 ae 8e 96 3d 60  3f 0a b0 73 e8 ea 42 42  |.^H...=`?..s..BB|
-000002b0  6e 19 fe e3 ab 30 ff 01  cc ae c1 90 3a 10 85 04  |n....0......:...|
-000002c0  03 00 8b 30 81 88 02 42  01 6e b2 79 a1 c1 45 7d  |...0...B.n.y..E}|
-000002d0  a3 44 45 75 e0 05 b6 68  ee e3 bb 80 2d 88 23 0c  |.DEu...h....-.#.|
-000002e0  40 ad 68 95 59 0f 49 5b  e5 67 2a 5a 9b 29 32 38  |@.h.Y.I[.g*Z.)28|
-000002f0  13 a0 f1 6c 11 3f 23 b9  1b 4b 0c 1f 2d 61 5e b3  |...l.?#..K..-a^.|
-00000300  de 14 b7 b8 a6 fb 7b 23  1f f3 02 42 01 30 39 22  |......{#...B.09"|
-00000310  01 6f d7 a2 83 2a fd 8a  6f f0 c2 d6 1b 0f b4 17  |.o...*..o.......|
-00000320  d3 50 f7 de 59 20 6a 5c  a1 93 65 ed aa fa 8f 6f  |.P..Y j\..e....o|
-00000330  75 3a b6 ab 33 b6 68 26  0b 8f d5 b2 ca eb 86 27  |u:..3.h&.......'|
-00000340  27 72 68 ed ee 42 37 1a  ff 8a 68 c3 32 91 16 03  |'rh..B7...h.2...|
-00000350  03 00 04 0e 00 00 00                              |.......|
+00000270  2a 16 03 03 00 d7 0c 00  00 d3 03 00 17 41 04 86  |*............A..|
+00000280  36 b4 78 76 87 70 ed ae  0d 34 70 3d 16 e5 a4 db  |6.xv.p...4p=....|
+00000290  ae 28 58 4c 01 5a 56 73  a7 0d 34 59 a7 04 75 69  |.(XL.ZVs..4Y..ui|
+000002a0  f2 55 24 40 b0 33 c6 93  ff ae e0 14 f5 4b ce a8  |.U$@.3.......K..|
+000002b0  e2 e6 9a 67 1d 66 fb 8f  fd 56 59 e7 73 f2 2c 04  |...g.f...VY.s.,.|
+000002c0  03 00 8a 30 81 87 02 41  73 ab a8 3c 64 17 69 9f  |...0...As..<d.i.|
+000002d0  4d b2 9b 55 12 60 33 94  cf f3 83 40 2b 7b 1b af  |M..U.`3....@+{..|
+000002e0  5c f4 cd 02 66 fb 83 04  35 fd ab 74 98 1a 7d f6  |\...f...5..t..}.|
+000002f0  9e 50 98 c3 98 e8 56 9c  f2 2a b0 30 9d 05 14 58  |.P....V..*.0...X|
+00000300  68 6a 88 04 49 07 78 bf  3a 02 42 01 be b2 05 9e  |hj..I.x.:.B.....|
+00000310  67 da 1e e9 5a 36 98 52  21 9f 43 75 43 ba bb 9a  |g...Z6.R!.CuC...|
+00000320  e6 e2 65 f4 e0 44 45 08  5a 1e 54 06 dd 5f 60 2e  |..e..DE.Z.T.._`.|
+00000330  7d e7 55 08 d3 7b 4e 0a  c7 da d4 27 34 d4 bd b0  |}.U..{N....'4...|
+00000340  12 2f 41 7a ed 71 32 ef  ee 12 74 66 00 16 03 03  |./Az.q2...tf....|
+00000350  00 04 0e 00 00 00                                 |......|
 >>> Flow 3 (client to server)
 00000000  16 03 03 00 46 10 00 00  42 41 04 1e 18 37 ef 0d  |....F...BA...7..|
 00000010  19 51 88 35 75 71 b5 e5  54 5b 12 2e 8f 09 67 fd  |.Q.5uq..T[....g.|
 00000020  a7 24 20 3e b2 56 1c ce  97 28 5e f8 2b 2d 4f 9e  |.$ >.V...(^.+-O.|
 00000030  f1 07 9f 6c 4b 5b 83 56  e2 32 42 e9 58 b6 d7 49  |...lK[.V.2B.X..I|
 00000040  a6 b5 68 1a 41 03 56 6b  dc 5a 89 14 03 03 00 01  |..h.A.Vk.Z......|
-00000050  01 16 03 03 00 28 00 00  00 00 00 00 00 00 8c c6  |.....(..........|
-00000060  b4 4f c9 4b f0 81 05 aa  aa 88 79 b0 76 fb 56 8a  |.O.K......y.v.V.|
-00000070  d3 8f 14 ff e2 9b a3 f6  92 77 aa cf f3 4e        |.........w...N|
+00000050  01 16 03 03 00 28 00 00  00 00 00 00 00 00 87 7a  |.....(.........z|
+00000060  82 d7 46 25 1d a6 bb c2  a8 a8 4e a5 d1 f8 02 db  |..F%......N.....|
+00000070  33 33 ca 78 b6 d3 bd 77  8a 33 23 a7 95 fb        |33.x...w.3#...|
 >>> Flow 4 (server to client)
-00000000  14 03 03 00 01 01 16 03  03 00 28 ca a5 27 78 fb  |..........(..'x.|
-00000010  e9 66 83 4d 71 62 d4 37  2f 01 5c 8b 67 52 98 5d  |.f.Mqb.7/.\.gR.]|
-00000020  0d a0 94 53 c1 b5 25 00  e2 42 ab 37 67 2e 12 eb  |...S..%..B.7g...|
-00000030  35 a3 9a                                          |5..|
+00000000  14 03 03 00 01 01 16 03  03 00 28 ce a1 9d 01 c0  |..........(.....|
+00000010  31 e5 d5 57 16 e1 a6 b3  8b 25 58 0f fa 2a de 3e  |1..W.....%X..*.>|
+00000020  0c d9 06 11 a6 b0 d7 b0  33 ad 31 73 5b 26 b4 d2  |........3.1s[&..|
+00000030  12 56 c8                                          |.V.|
 >>> Flow 5 (client to server)
-00000000  17 03 03 00 1e 00 00 00  00 00 00 00 01 e0 be 1e  |................|
-00000010  a7 0f 73 a6 25 ca 9e d3  0a ad 6b e7 e9 db 21 a1  |..s.%.....k...!.|
-00000020  70 5f c1 15 03 03 00 1a  00 00 00 00 00 00 00 02  |p_..............|
-00000030  c3 af f9 8c 63 94 cb e4  99 6d b7 32 80 22 f7 1f  |....c....m.2."..|
-00000040  02 87                                             |..|
+00000000  17 03 03 00 1e 00 00 00  00 00 00 00 01 d5 04 4c  |...............L|
+00000010  7b 35 b4 d7 90 ae fe 00  d2 f2 4b 76 f1 36 5e 24  |{5........Kv.6^$|
+00000020  4a aa 94 15 03 03 00 1a  00 00 00 00 00 00 00 02  |J...............|
+00000030  d3 1c 41 37 ab f6 17 79  f0 01 a4 19 a5 75 7a 8e  |..A7...y.....uz.|
+00000040  a3 b2                                             |..|
index ae137d878451ffdc296474d68b76dff95ede95ad..5336bbbad8f862751dbe474994181c3aef82a2ec 100644 (file)
@@ -8,11 +8,11 @@
 00000060  19 00 0b 00 02 01 00 00  0d 00 0a 00 08 04 01 04  |................|
 00000070  03 02 01 02 03 ff 01 00  01 00                    |..........|
 >>> Flow 2 (server to client)
-00000000  16 03 03 00 59 02 00 00  55 03 03 52 cc 57 58 64  |....Y...U..R.WXd|
-00000010  28 c0 0d 0a be 3a aa 96  4e 92 8d 9d f2 19 ab de  |(....:..N.......|
-00000020  b2 05 3a 61 13 78 60 7e  96 24 b2 20 a3 06 80 14  |..:a.x`~.$. ....|
-00000030  15 52 89 1b d4 84 94 8b  ed 66 8d 75 63 8f dc 5a  |.R.......f.uc..Z|
-00000040  a8 20 14 65 5d ce 7e 2f  4b 3e 1e 09 c0 13 00 00  |. .e].~/K>......|
+00000000  16 03 03 00 59 02 00 00  55 03 03 53 04 f1 02 41  |....Y...U..S...A|
+00000010  95 cc 56 30 65 46 24 75  d5 9e 3c a7 5b 6c 99 fe  |..V0eF$u..<.[l..|
+00000020  86 35 23 42 3a 8f 4d 4c  b9 98 7d 20 a7 46 43 72  |.5#B:.ML..} .FCr|
+00000030  66 bb b6 ad ff ad cf 63  37 fe 6b b4 78 94 08 49  |f......c7.k.x..I|
+00000040  54 06 ed f4 85 73 38 4a  c6 fe b6 98 c0 13 00 00  |T....s8J........|
 00000050  0d ff 01 00 01 00 00 0b  00 04 03 00 01 02 16 03  |................|
 00000060  03 02 be 0b 00 02 ba 00  02 b7 00 02 b4 30 82 02  |.............0..|
 00000070  b0 30 82 02 19 a0 03 02  01 02 02 09 00 85 b0 bb  |.0..............|
 000002f0  5f 33 c4 b6 d8 c9 75 90  96 8c 0f 52 98 b5 cd 98  |_3....u....R....|
 00000300  1f 89 20 5f f2 a0 1c a3  1b 96 94 dd a9 fd 57 e9  |.. _..........W.|
 00000310  70 e8 26 6d 71 99 9b 26  6e 38 50 29 6c 90 a7 bd  |p.&mq..&n8P)l...|
-00000320  d9 16 03 03 00 cd 0c 00  00 c9 03 00 17 41 04 33  |.............A.3|
-00000330  96 55 c1 5e 9d c4 a1 23  86 5b e8 df f9 7d d4 d0  |.U.^...#.[...}..|
-00000340  5f 61 9a c6 24 be a6 4b  ce 08 6e 9f 8f ef 08 66  |_a..$..K..n....f|
-00000350  9f a5 2e e7 04 c9 f2 d9  ab ef fa 62 28 a0 01 7a  |...........b(..z|
-00000360  d9 d6 44 9b c3 25 dc 5e  a9 75 ea 8d 2f e8 63 04  |..D..%.^.u../.c.|
-00000370  01 00 80 ba b3 36 74 0f  2c 3a c2 a3 2c ae 74 dc  |.....6t.,:..,.t.|
-00000380  f8 90 ba 91 10 a3 c1 8e  2b bf 2c b4 05 78 12 ff  |........+.,..x..|
-00000390  ec 62 b3 db f2 27 3d d0  0f bb 7d 1e f6 8f fd ee  |.b...'=...}.....|
-000003a0  53 37 be 6a 9e d9 21 42  ea 20 1e d8 fc eb 3c 79  |S7.j..!B. ....<y|
-000003b0  98 85 ab fe 9b 2d 63 77  cb 13 32 32 81 94 3e 53  |.....-cw..22..>S|
-000003c0  4f a7 63 c4 78 2d a2 48  08 ea f6 2a 50 24 33 f4  |O.c.x-.H...*P$3.|
-000003d0  9f 10 63 13 80 4e ec 5e  68 e3 18 f0 7b a4 2f 16  |..c..N.^h...{./.|
-000003e0  db c0 aa 8c a0 ee 47 65  a9 57 f3 a5 ef 6a 45 f5  |......Ge.W...jE.|
-000003f0  e2 54 cd 16 03 03 00 04  0e 00 00 00              |.T..........|
+00000320  d9 16 03 03 00 cd 0c 00  00 c9 03 00 17 41 04 48  |.............A.H|
+00000330  68 d8 8a 10 b4 bf eb 8d  d1 98 b0 a6 f4 47 5d 91  |h............G].|
+00000340  61 da 50 d9 85 7b 5d 90  02 2c 38 c9 af 81 d3 55  |a.P..{]..,8....U|
+00000350  07 62 b1 62 58 7f 39 94  d7 91 96 a8 1f 47 60 a5  |.b.bX.9......G`.|
+00000360  c0 04 f2 fb cb 15 75 a6  16 3f 94 53 7c ff dd 04  |......u..?.S|...|
+00000370  01 00 80 b9 82 fa 0b f8  8c 94 2c 6e 05 81 7d 80  |..........,n..}.|
+00000380  5d 9a 77 78 af c8 33 5d  89 7e 2e 3c e5 72 66 a8  |].wx..3].~.<.rf.|
+00000390  f1 5c 02 04 02 70 76 7b  45 ff 0d 29 a0 cb 0d db  |.\...pv{E..)....|
+000003a0  7a 4c c4 13 19 cd 47 b2  f1 c9 43 4f 95 d2 f1 c6  |zL....G...CO....|
+000003b0  bc ae 31 4a 9d de 80 b2  a4 b7 b6 dd 8c 03 3e 2a  |..1J..........>*|
+000003c0  46 5e d1 e7 5b c5 9e 06  58 f3 55 b2 77 09 f3 98  |F^..[...X.U.w...|
+000003d0  d5 7f 5a 74 64 7e 48 22  8f 7d a8 68 b6 1d 90 df  |..Ztd~H".}.h....|
+000003e0  2c 91 d7 c5 07 3d d1 6f  e9 c1 91 03 3c 23 5a 56  |,....=.o....<#ZV|
+000003f0  3b b2 c2 16 03 03 00 04  0e 00 00 00              |;...........|
 >>> Flow 3 (client to server)
 00000000  16 03 03 00 46 10 00 00  42 41 04 1e 18 37 ef 0d  |....F...BA...7..|
 00000010  19 51 88 35 75 71 b5 e5  54 5b 12 2e 8f 09 67 fd  |.Q.5uq..T[....g.|
 00000030  f1 07 9f 6c 4b 5b 83 56  e2 32 42 e9 58 b6 d7 49  |...lK[.V.2B.X..I|
 00000040  a6 b5 68 1a 41 03 56 6b  dc 5a 89 14 03 03 00 01  |..h.A.Vk.Z......|
 00000050  01 16 03 03 00 40 00 00  00 00 00 00 00 00 00 00  |.....@..........|
-00000060  00 00 00 00 00 00 73 06  31 ab 8e cc e0 db 1a fe  |......s.1.......|
-00000070  7e d1 5b 2b 96 93 ee 2d  76 54 17 f9 c2 73 e8 62  |~.[+...-vT...s.b|
-00000080  f0 39 31 02 72 e9 ae 08  75 2d f1 f3 82 06 17 57  |.91.r...u-.....W|
-00000090  b7 aa c2 79 9f 8e                                 |...y..|
+00000060  00 00 00 00 00 00 59 e6  92 05 27 ec 09 2c b0 a5  |......Y...'..,..|
+00000070  2a fb 7e f1 03 53 16 63  68 a1 86 13 bb da 98 27  |*.~..S.ch......'|
+00000080  6d 42 08 35 6a ec 58 61  2a 4d 44 ec ae c5 b9 d2  |mB.5j.Xa*MD.....|
+00000090  76 57 1f 75 9f 8d                                 |vW.u..|
 >>> Flow 4 (server to client)
-00000000  14 03 03 00 01 01 16 03  03 00 40 dc c3 03 17 76  |..........@....v|
-00000010  d0 a5 61 9e 47 6a 1a 01  d0 21 92 c5 d5 f9 69 ba  |..a.Gj...!....i.|
-00000020  5e 82 2e d3 fc 4b a4 af  78 9d 47 6e b8 33 dc 8b  |^....K..x.Gn.3..|
-00000030  a0 94 b5 72 ea 4a 7d fc  ea f5 6e b6 c9 00 73 d1  |...r.J}...n...s.|
-00000040  3e cb 44 ef 0c fc fc ff  1e 87 8e                 |>.D........|
+00000000  14 03 03 00 01 01 16 03  03 00 40 6e 03 d0 e6 98  |..........@n....|
+00000010  1f f5 39 7b 06 9f 95 f0  7a 88 35 7c 55 db c3 2f  |..9{....z.5|U../|
+00000020  00 ef 5b d3 62 87 a2 94  da 2f f6 4a 89 c9 a8 3d  |..[.b..../.J...=|
+00000030  3a 92 db 77 35 92 01 4b  f5 c5 6b 95 09 9f cd 79  |:..w5..K..k....y|
+00000040  3c af 37 5b 27 bf 93 3e  04 55 71                 |<.7['..>.Uq|
 >>> Flow 5 (client to server)
 00000000  17 03 03 00 30 00 00 00  00 00 00 00 00 00 00 00  |....0...........|
-00000010  00 00 00 00 00 de 2e e3  8e d3 a5 e5 64 8e 22 f7  |............d.".|
-00000020  09 00 15 06 cb e9 d3 c5  3c 17 41 2f 5b a8 ce 09  |........<.A/[...|
-00000030  70 59 47 24 22 15 03 03  00 30 00 00 00 00 00 00  |pYG$"....0......|
-00000040  00 00 00 00 00 00 00 00  00 00 6a af 3f c1 54 6b  |..........j.?.Tk|
-00000050  14 64 23 c4 94 79 82 fd  78 84 ef 04 6f 64 fd 3e  |.d#..y..x...od.>|
-00000060  20 ff a0 11 ed fb d1 fb  ff 07                    | .........|
+00000010  00 00 00 00 00 bc c9 d0  8e 80 14 de 32 18 49 e8  |............2.I.|
+00000020  20 dc 5e 6c e4 6d 14 00  df 51 71 fb 86 95 16 4c  | .^l.m...Qq....L|
+00000030  04 8e 71 e1 48 15 03 03  00 30 00 00 00 00 00 00  |..q.H....0......|
+00000040  00 00 00 00 00 00 00 00  00 00 b7 6d 30 72 61 53  |...........m0raS|
+00000050  d8 0a d4 1d ae e5 d4 22  46 c9 d5 4e 4a 86 f5 ac  |......."F..NJ...|
+00000060  72 98 c6 db 38 29 97 2c  84 0b                    |r...8).,..|
index 2c6e776848eab1118db9755bc638d9f125011332..0377f052ae60c0de239dd0b82c32a635e2bb4381 100644 (file)
@@ -8,11 +8,11 @@
 00000060  19 00 0b 00 02 01 00 00  0d 00 0a 00 08 04 01 04  |................|
 00000070  03 02 01 02 03 ff 01 00  01 00                    |..........|
 >>> Flow 2 (server to client)
-00000000  16 03 03 00 51 02 00 00  4d 03 03 52 cc 57 58 70  |....Q...M..R.WXp|
-00000010  03 50 58 32 ec de bc ae  18 e7 24 15 2b 4d cc d5  |.PX2......$.+M..|
-00000020  1f 40 db 80 5b 19 d9 3f  48 4b 06 20 87 fe d3 d4  |.@..[..?HK. ....|
-00000030  51 42 74 9c cf 9d fd 31  c3 53 28 f1 a4 21 16 d6  |QBt....1.S(..!..|
-00000040  4f 5a 22 09 9d 99 89 7c  f9 e7 5b 49 00 05 00 00  |OZ"....|..[I....|
+00000000  16 03 03 00 51 02 00 00  4d 03 03 53 04 f1 02 9d  |....Q...M..S....|
+00000010  2e 4e d9 17 4a 35 fa 9d  94 f6 45 0a f6 6b 5d 1c  |.N..J5....E..k].|
+00000020  1e 15 19 8d 6d 94 cc 90  d9 39 94 20 8b 4b de 76  |....m....9. .K.v|
+00000030  d5 64 5d b7 19 df e7 eb  7e a0 22 c4 09 38 a0 12  |.d].....~."..8..|
+00000040  d5 59 10 c8 31 06 dc fc  e4 9d d1 80 00 05 00 00  |.Y..1...........|
 00000050  05 ff 01 00 01 00 16 03  03 02 be 0b 00 02 ba 00  |................|
 00000060  02 b7 00 02 b4 30 82 02  b0 30 82 02 19 a0 03 02  |.....0...0......|
 00000070  01 02 02 09 00 85 b0 bb  a4 8a 7f b8 ca 30 0d 06  |.............0..|
 00000060  e6 bd 77 82 6f 23 b6 e0  bd a2 92 b7 3a ac e8 56  |..w.o#......:..V|
 00000070  f1 af 54 5e 46 87 e9 3b  33 e7 b8 28 b7 d6 c8 90  |..T^F..;3..(....|
 00000080  35 d4 1c 43 d1 30 6f 55  4e 0a 70 14 03 03 00 01  |5..C.0oUN.p.....|
-00000090  01 16 03 03 00 24 fb 2b  2f c5 19 90 a6 bc 81 c0  |.....$.+/.......|
-000000a0  ac 6b e6 1f e2 af be 10  b4 a0 c7 31 aa d8 cc a2  |.k.........1....|
-000000b0  ce 51 1e 8e d6 00 76 27  72 53                    |.Q....v'rS|
+00000090  01 16 03 03 00 24 37 14  b2 97 7b b5 f0 9a 38 05  |.....$7...{...8.|
+000000a0  22 35 69 9c 95 2f 86 4b  37 98 22 db 4e 9a 46 9c  |"5i../.K7.".N.F.|
+000000b0  b9 81 74 72 58 18 53 0c  5c 3c                    |..trX.S.\<|
 >>> Flow 4 (server to client)
-00000000  14 03 03 00 01 01 16 03  03 00 24 d4 c8 b3 8a 87  |..........$.....|
-00000010  b5 0d 01 80 fc 2c df 18  ca 17 fd 06 ad 31 29 13  |.....,.......1).|
-00000020  5f f9 b4 75 ad 49 c8 de  ec 1a ee 57 6b 1a 81     |_..u.I.....Wk..|
+00000000  14 03 03 00 01 01 16 03  03 00 24 3c b3 e7 77 5a  |..........$<..wZ|
+00000010  7c 36 5a 74 74 26 8d 5b  5a 09 96 60 e8 24 45 2f  ||6Ztt&.[Z..`.$E/|
+00000020  c2 39 14 5e db 58 12 49  ad a8 b6 ea ef 58 16     |.9.^.X.I.....X.|
 >>> Flow 5 (client to server)
-00000000  17 03 03 00 1a 8b ce be  7d c7 06 42 d0 9b 65 6c  |........}..B..el|
-00000010  23 0e 84 ef e0 a3 ec 42  4e 70 14 65 78 ad 52 15  |#......BNp.ex.R.|
-00000020  03 03 00 16 49 5f f2 e2  e0 8f d3 54 68 2a d6 ab  |....I_.....Th*..|
-00000030  28 be 50 3d 62 8b 1a b3  5b e5                    |(.P=b...[.|
+00000000  17 03 03 00 1a 6d 29 d7  ba 2f 85 02 b6 f0 82 64  |.....m)../.....d|
+00000010  6c 55 ae ab f6 fd 14 ff  b8 38 f0 f8 a6 ea cc 15  |lU.......8......|
+00000020  03 03 00 16 10 c5 d9 41  7b e2 89 67 dc 29 8e f8  |.......A{..g.)..|
+00000030  b5 ab 32 91 44 2c 27 84  49 f7                    |..2.D,'.I.|
index d90f02bd3556017ea03d33566dc6adac1b9d4039..9b8cb4d9b62912047640a95afa52dade7405e9c6 100644 (file)
@@ -1,7 +1,7 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 00 76 01 00 00  72 03 01 52 cc 57 59 19  |....v...r..R.WY.|
-00000010  3d b7 7c 4b 54 8d ca 3a  b1 4c 4e a9 78 86 d9 74  |=.|KT..:.LN.x..t|
-00000020  87 2f f3 86 bf ac cc f4  11 75 b7 00 00 04 c0 0a  |./.......u......|
+00000000  16 03 01 00 76 01 00 00  72 03 01 53 04 f0 f9 4b  |....v...r..S...K|
+00000010  30 a8 68 d0 79 13 14 69  ee 3b 5d 05 cb 71 63 43  |0.h.y..i.;]..qcC|
+00000020  4a 55 6b 05 25 53 19 ba  e0 2f b1 00 00 04 c0 0a  |JUk.%S.../......|
 00000030  00 ff 01 00 00 45 00 0b  00 04 03 00 01 02 00 0a  |.....E..........|
 00000040  00 34 00 32 00 0e 00 0d  00 19 00 0b 00 0c 00 18  |.4.2............|
 00000050  00 09 00 0a 00 16 00 17  00 08 00 06 00 07 00 14  |................|
 00000210  0e bd 3f a3 8c 25 c1 33  13 83 0d 94 06 bb d4 37  |..?..%.3.......7|
 00000220  7a f6 ec 7a c9 86 2e dd  d7 11 69 7f 85 7c 56 de  |z..z......i..|V.|
 00000230  fb 31 78 2b e4 c7 78 0d  ae cb be 9e 4e 36 24 31  |.1x+..x.....N6$1|
-00000240  7b 6a 0f 39 95 12 07 8f  2a 16 03 01 01 1a 0c 00  |{j.9....*.......|
-00000250  01 16 03 00 19 85 04 01  39 dc ee 44 17 5e db d7  |........9..D.^..|
-00000260  27 af b6 56 d9 b4 43 5a  99 cf aa 31 37 0c 6f 3a  |'..V..CZ...17.o:|
-00000270  a0 f8 53 c4 74 d1 91 0a  46 f5 38 3b 5c 09 d8 97  |..S.t...F.8;\...|
-00000280  dc 4b aa 70 26 48 f2 d6  0b 31 c9 f8 d4 98 43 e1  |.K.p&H...1....C.|
-00000290  6c d5 c7 b2 8e 0b 01 e6  b6 00 28 80 7b fc 96 8f  |l.........(.{...|
-000002a0  0d a2 4f b0 79 af dc 61  28 63 33 78 f6 31 39 fd  |..O.y..a(c3x.19.|
-000002b0  8a f4 15 18 11 fe db d5  07 da 2c ed 49 a0 23 bf  |..........,.I.#.|
-000002c0  d0 3a 38 1d 54 ae 1c 7b  ea 29 ee d0 38 c1 76 a7  |.:8.T..{.)..8.v.|
-000002d0  7f 2a f4 ce 1e ac cc 94  79 90 33 00 8b 30 81 88  |.*......y.3..0..|
-000002e0  02 42 00 c6 85 8e 06 b7  04 04 e9 cd 9e 3e cb 66  |.B...........>.f|
-000002f0  23 95 b4 42 9c 64 81 39  05 3f b5 21 f8 28 af 60  |#..B.d.9.?.!.(.`|
-00000300  6b 4d 3d ba a1 4b 5e 77  ef e7 59 28 fe 1d c1 27  |kM=..K^w..Y(...'|
-00000310  a2 ff a8 de 33 48 b3 c1  85 6a 42 9b f9 7e 7e 31  |....3H...jB..~~1|
-00000320  c2 e5 bd 66 02 42 00 ad  7d 06 35 ab ec 8d ac d4  |...f.B..}.5.....|
-00000330  ba 1b 49 5e 05 5f f0 97  93 82 b8 2b 8d 91 98 63  |..I^._.....+...c|
-00000340  8e b4 14 62 db 1e c9 2b  30 f8 41 9b a6 e6 bc de  |...b...+0.A.....|
-00000350  0e 68 30 21 e4 33 62 b4  3c 00 12 33 3c d2 bb 9e  |.h0!.3b.<..3<...|
-00000360  a9 db ef 22 41 ed 2b 1a  16 03 01 00 04 0e 00 00  |..."A.+.........|
-00000370  00                                                |.|
+00000240  7b 6a 0f 39 95 12 07 8f  2a 16 03 01 00 d6 0c 00  |{j.9....*.......|
+00000250  00 d2 03 00 17 41 04 1e  18 37 ef 0d 19 51 88 35  |.....A...7...Q.5|
+00000260  75 71 b5 e5 54 5b 12 2e  8f 09 67 fd a7 24 20 3e  |uq..T[....g..$ >|
+00000270  b2 56 1c ce 97 28 5e f8  2b 2d 4f 9e f1 07 9f 6c  |.V...(^.+-O....l|
+00000280  4b 5b 83 56 e2 32 42 e9  58 b6 d7 49 a6 b5 68 1a  |K[.V.2B.X..I..h.|
+00000290  41 03 56 6b dc 5a 89 00  8b 30 81 88 02 42 00 c6  |A.Vk.Z...0...B..|
+000002a0  85 8e 06 b7 04 04 e9 cd  9e 3e cb 66 23 95 b4 42  |.........>.f#..B|
+000002b0  9c 64 81 39 05 3f b5 21  f8 28 af 60 6b 4d 3d ba  |.d.9.?.!.(.`kM=.|
+000002c0  a1 4b 5e 77 ef e7 59 28  fe 1d c1 27 a2 ff a8 de  |.K^w..Y(...'....|
+000002d0  33 48 b3 c1 85 6a 42 9b  f9 7e 7e 31 c2 e5 bd 66  |3H...jB..~~1...f|
+000002e0  02 42 00 ad 7d 06 35 ab  ec 8d ac d4 ba 1b 49 5e  |.B..}.5.......I^|
+000002f0  05 5f f0 97 93 82 b8 2b  8d 91 98 63 8e b4 14 62  |._.....+...c...b|
+00000300  db 1e c9 2b 30 f8 41 9b  a6 e6 bc de 0e 68 30 21  |...+0.A......h0!|
+00000310  d8 ef 2f 05 42 da f2 e0  2c 06 33 1d 0d 9a 1a 75  |../.B...,.3....u|
+00000320  59 a7 3a bc 16 03 01 00  04 0e 00 00 00           |Y.:..........|
 >>> Flow 3 (client to server)
-00000000  16 03 01 00 8a 10 00 00  86 85 04 00 be 44 15 9d  |.............D..|
-00000010  89 bc 13 e7 55 cf 7a f9  b1 dc 2b 22 f2 7b a6 33  |....U.z...+".{.3|
-00000020  b6 46 de 7d c2 08 a6 67  15 7d 3e c2 02 d4 30 dc  |.F.}...g.}>...0.|
-00000030  35 37 7e ee fb 12 8c 74  c0 bf ea db 72 06 cd 52  |57~....t....r..R|
-00000040  1a e9 36 2e c5 5d 22 9e  56 e5 8d 9f a5 01 e2 e6  |..6..]".V.......|
-00000050  f5 77 64 05 80 c0 e3 54  9a 89 76 f9 73 64 7d 0e  |.wd....T..v.sd}.|
-00000060  bf d7 b3 57 a2 6c 4a 34  80 53 96 90 61 56 34 ad  |...W.lJ4.S..aV4.|
-00000070  75 44 54 79 dd 71 16 fe  4f 27 4f d0 0c d0 1e b6  |uDTy.q..O'O.....|
-00000080  82 c4 b2 e9 a1 87 fd a7  2a 6a 5d 79 b8 f1 4e 14  |........*j]y..N.|
-00000090  03 01 00 01 01 16 03 01  00 30 db c5 5f 9b 03 77  |.........0.._..w|
-000000a0  c9 aa 5e 74 fd 0c f7 28  fe 40 8b 0e 2d 85 1e 25  |..^t...(.@..-..%|
-000000b0  96 23 79 48 2c 3f 5d 9b  57 f0 34 8d 87 81 13 d9  |.#yH,?].W.4.....|
-000000c0  ed 72 f6 51 bf d7 6e 8b  61 3d                    |.r.Q..n.a=|
+00000000  16 03 01 00 46 10 00 00  42 41 04 08 28 cf bd 3c  |....F...BA..(..<|
+00000010  3c cc 98 9e 73 3f 92 a7  cb 22 83 3b c7 61 46 0e  |<...s?...".;.aF.|
+00000020  4d 7c 30 b5 06 85 2f 01  be b5 40 e2 64 1e 45 c1  |M|0.../...@.d.E.|
+00000030  9d 73 95 d5 65 92 0b 9b  e7 6f c6 91 ab b6 fa be  |.s..e....o......|
+00000040  61 83 a7 f2 eb f5 65 31  fe 24 7b 14 03 01 00 01  |a.....e1.${.....|
+00000050  01 16 03 01 00 30 15 d1  c4 ca 0b 01 84 13 5a ba  |.....0........Z.|
+00000060  89 04 87 73 7c bb d8 89  7e 10 27 ba 6f 5d dc d3  |...s|...~.'.o]..|
+00000070  b5 ef 32 86 58 cc fb eb  5c 32 9e 95 ef 01 1c ac  |..2.X...\2......|
+00000080  dc 8e df 7f fe 0a                                 |......|
 >>> Flow 4 (server to client)
-00000000  14 03 01 00 01 01 16 03  01 00 30 95 b6 20 60 88  |..........0.. `.|
-00000010  5f 23 11 06 83 f5 20 2d  42 77 36 c2 84 1b 70 90  |_#.... -Bw6...p.|
-00000020  12 af d3 5e fa b0 59 6b  1e 91 0a c3 e0 37 40 94  |...^..Yk.....7@.|
-00000030  07 c0 2b 84 74 41 fa fd  7c 41 59 17 03 01 00 20  |..+.tA..|AY.... |
-00000040  de a7 ea 4e f8 88 22 6d  cb 3d d5 ed 60 7c b9 a0  |...N.."m.=..`|..|
-00000050  ba c5 19 14 86 3a 98 ea  d3 73 68 1e d4 f8 0e 12  |.....:...sh.....|
-00000060  17 03 01 00 30 30 50 48  84 2e b0 15 0a 5f 64 3c  |....00PH....._d<|
-00000070  fc 19 aa 89 7d 6e ba 84  56 56 66 15 6e d4 b9 35  |....}n..VVf.n..5|
-00000080  20 ac 98 0d 8d 09 e1 80  8d 32 c8 99 d2 70 41 3a  | ........2...pA:|
-00000090  9b 62 d6 48 b1 15 03 01  00 20 9a 16 01 aa d0 6f  |.b.H..... .....o|
-000000a0  d4 d3 bb 5d 57 c0 7c d1  a8 d4 67 5d 5e 1d be 7d  |...]W.|...g]^..}|
-000000b0  d2 78 4a 33 93 ae 53 cc  fb a0                    |.xJ3..S...|
+00000000  14 03 01 00 01 01 16 03  01 00 30 e8 48 86 81 3c  |..........0.H..<|
+00000010  f5 25 5c 94 a9 06 c4 5c  71 62 b1 43 76 ec 2c 44  |.%\....\qb.Cv.,D|
+00000020  95 b5 8c 95 d2 ff 82 92  b6 fc 52 75 03 c6 a1 f0  |..........Ru....|
+00000030  99 6d b1 ed ec 68 6c d7  9f 18 50 17 03 01 00 20  |.m...hl...P.... |
+00000040  32 d9 26 8a 81 b8 9d a5  7b fd d5 4e 7a db 2e 29  |2.&.....{..Nz..)|
+00000050  58 9a 4f 6a 27 18 bc dc  c2 49 b8 65 cb 8e 16 5a  |X.Oj'....I.e...Z|
+00000060  17 03 01 00 30 c4 56 0a  ad 9a 82 cb 3e 32 f1 7c  |....0.V.....>2.||
+00000070  95 6e dd cd e9 4d f0 e5  2d c9 a3 f7 de bb d7 fd  |.n...M..-.......|
+00000080  84 bb df 34 8c 64 1f 03  58 64 19 4a 5b 7a a8 81  |...4.d..Xd.J[z..|
+00000090  52 bb 51 0a 43 15 03 01  00 20 89 18 7a 40 ec 49  |R.Q.C.... ..z@.I|
+000000a0  52 d5 d3 20 ac 07 eb e9  4a 78 23 cf e7 21 32 74  |R.. ....Jx#..!2t|
+000000b0  ec 40 8d a8 f4 33 1c ae  93 cf                    |.@...3....|
index c170408e48099b5d9eea6faa16eea0d9bfd7d954..0ab8b8d74c56d40fdabfac023cc11a29b84b9145 100644 (file)
@@ -1,7 +1,7 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 00 ca 01 00 00  c6 03 03 52 cc 5e 7f 49  |...........R.^.I|
-00000010  8a 7a 88 c0 85 24 6b 3d  95 ff 0f 9e 91 32 c2 a4  |.z...$k=.....2..|
-00000020  6b 4c 53 e4 b4 4c 40 72  e4 54 27 00 00 32 c0 30  |kLS..L@r.T'..2.0|
+00000000  16 03 01 00 ca 01 00 00  c6 03 03 53 04 f1 3f 5f  |...........S..?_|
+00000010  f4 ef 1f b3 41 0b 54 e4  4d 56 0a 31 22 b8 5c 73  |....A.T.MV.1".\s|
+00000020  a3 cb b5 b2 9d 43 f1 83  bc d3 bd 00 00 32 c0 30  |.....C.......2.0|
 00000030  c0 2c c0 28 c0 24 c0 14  c0 0a c0 22 c0 21 00 a3  |.,.(.$.....".!..|
 00000040  00 9f 00 6b 00 6a 00 39  00 38 00 88 00 87 c0 32  |...k.j.9.8.....2|
 00000050  c0 2e c0 2a c0 26 c0 0f  c0 05 00 9d 00 3d 00 35  |...*.&.......=.5|
 00000210  33 13 83 0d 94 06 bb d4  37 7a f6 ec 7a c9 86 2e  |3.......7z..z...|
 00000220  dd d7 11 69 7f 85 7c 56  de fb 31 78 2b e4 c7 78  |...i..|V..1x+..x|
 00000230  0d ae cb be 9e 4e 36 24  31 7b 6a 0f 39 95 12 07  |.....N6$1{j.9...|
-00000240  8f 2a 16 03 03 01 1c 0c  00 01 18 03 00 19 85 04  |.*..............|
-00000250  01 39 dc ee 44 17 5e db  d7 27 af b6 56 d9 b4 43  |.9..D.^..'..V..C|
-00000260  5a 99 cf aa 31 37 0c 6f  3a a0 f8 53 c4 74 d1 91  |Z...17.o:..S.t..|
-00000270  0a 46 f5 38 3b 5c 09 d8  97 dc 4b aa 70 26 48 f2  |.F.8;\....K.p&H.|
-00000280  d6 0b 31 c9 f8 d4 98 43  e1 6c d5 c7 b2 8e 0b 01  |..1....C.l......|
-00000290  e6 b6 00 28 80 7b fc 96  8f 0d a2 4f b0 79 af dc  |...(.{.....O.y..|
-000002a0  61 28 63 33 78 f6 31 39  fd 8a f4 15 18 11 fe db  |a(c3x.19........|
-000002b0  d5 07 da 2c ed 49 a0 23  bf d0 3a 38 1d 54 ae 1c  |...,.I.#..:8.T..|
-000002c0  7b ea 29 ee d0 38 c1 76  a7 7f 2a f4 ce 1e ac cc  |{.)..8.v..*.....|
-000002d0  94 79 90 33 04 03 00 8b  30 81 88 02 42 00 c6 85  |.y.3....0...B...|
-000002e0  8e 06 b7 04 04 e9 cd 9e  3e cb 66 23 95 b4 42 9c  |........>.f#..B.|
-000002f0  64 81 39 05 3f b5 21 f8  28 af 60 6b 4d 3d ba a1  |d.9.?.!.(.`kM=..|
-00000300  4b 5e 77 ef e7 59 28 fe  1d c1 27 a2 ff a8 de 33  |K^w..Y(...'....3|
-00000310  48 b3 c1 85 6a 42 9b f9  7e 7e 31 c2 e5 bd 66 02  |H...jB..~~1...f.|
-00000320  42 00 ad 7d 06 35 ab ec  8d ac d4 ba 1b 49 5e 05  |B..}.5.......I^.|
-00000330  5f f0 97 93 82 b8 2b 8d  91 98 63 8e b4 14 62 db  |_.....+...c...b.|
-00000340  1e c9 2b ca fe c9 88 b7  3d 46 d2 5b 55 de bc 9a  |..+.....=F.[U...|
-00000350  66 c9 cf b7 3d e8 c8 62  24 93 d8 db 12 77 2a 6c  |f...=..b$....w*l|
-00000360  08 66 48 16 03 03 00 04  0e 00 00 00              |.fH.........|
+00000240  8f 2a 16 03 03 00 d8 0c  00 00 d4 03 00 17 41 04  |.*............A.|
+00000250  1e 18 37 ef 0d 19 51 88  35 75 71 b5 e5 54 5b 12  |..7...Q.5uq..T[.|
+00000260  2e 8f 09 67 fd a7 24 20  3e b2 56 1c ce 97 28 5e  |...g..$ >.V...(^|
+00000270  f8 2b 2d 4f 9e f1 07 9f  6c 4b 5b 83 56 e2 32 42  |.+-O....lK[.V.2B|
+00000280  e9 58 b6 d7 49 a6 b5 68  1a 41 03 56 6b dc 5a 89  |.X..I..h.A.Vk.Z.|
+00000290  04 03 00 8b 30 81 88 02  42 00 c6 85 8e 06 b7 04  |....0...B.......|
+000002a0  04 e9 cd 9e 3e cb 66 23  95 b4 42 9c 64 81 39 05  |....>.f#..B.d.9.|
+000002b0  3f b5 21 f8 28 af 60 6b  4d 3d ba a1 4b 5e 77 ef  |?.!.(.`kM=..K^w.|
+000002c0  e7 59 28 fe 1d c1 27 a2  ff a8 de 33 48 b3 c1 85  |.Y(...'....3H...|
+000002d0  6a 42 9b f9 7e 7e 31 c2  e5 bd 66 02 42 00 ad 7d  |jB..~~1...f.B..}|
+000002e0  06 35 ab ec 8d ac d4 ba  1b 49 5e 05 5f f0 97 93  |.5.......I^._...|
+000002f0  82 b8 2b 8d 91 98 63 8e  b4 14 62 db 1e c9 2b 64  |..+...c...b...+d|
+00000300  e9 e6 bf 15 5b 67 c2 40  90 c6 1f b7 92 db 4b f6  |....[g.@......K.|
+00000310  f4 db ae 82 f1 4f 02 75  52 40 38 10 ff 35 f0 16  |.....O.uR@8..5..|
+00000320  03 03 00 04 0e 00 00 00                           |........|
 >>> Flow 3 (client to server)
-00000000  16 03 03 00 8a 10 00 00  86 85 04 01 fd 02 a1 b1  |................|
-00000010  56 3c 37 37 da 78 37 d9  07 ee 09 35 4f ff 3e db  |V<77.x7....5O.>.|
-00000020  da da 23 12 2c 40 12 dd  73 e7 2c c5 2e fb 37 24  |..#.,@..s.,...7$|
-00000030  2f 97 95 b4 6c 1e 56 6c  4e 49 d5 89 21 8b ca 74  |/...l.VlNI..!..t|
-00000040  85 1b 24 96 fb 28 cc 64  70 59 fc be 18 00 00 98  |..$..(.dpY......|
-00000050  9a f6 c9 26 26 6d ce 48  7b 3b 62 ea dd da 73 8b  |...&&m.H{;b...s.|
-00000060  71 48 18 71 52 2d 22 1d  7c 67 55 1b 6b fa 44 40  |qH.qR-".|gU.k.D@|
-00000070  be 87 0f 52 21 4b 86 b4  f0 6d 1b dd e7 0f f8 ef  |...R!K...m......|
-00000080  1a 09 8b 66 b9 60 38 da  6f 9d 9d 74 58 d9 35 14  |...f.`8.o..tX.5.|
-00000090  03 03 00 01 01 16 03 03  00 40 5b 98 11 9d d4 83  |.........@[.....|
-000000a0  13 b6 28 4b 85 61 0b e1  bf 36 3f 43 c0 95 3d 7e  |..(K.a...6?C..=~|
-000000b0  95 ea 84 14 e6 6d 1a e0  20 50 b4 02 d0 b2 e9 5f  |.....m.. P....._|
-000000c0  07 82 a8 6a 1e 7c 1e f7  6c b5 be 1b 20 2e 98 4e  |...j.|..l... ..N|
-000000d0  ab 8d 1e f2 56 88 ed ef  aa 39                    |....V....9|
+00000000  16 03 03 00 46 10 00 00  42 41 04 d8 94 c4 05 26  |....F...BA.....&|
+00000010  76 29 2d 0e ec 47 b6 50  d5 a3 da 2a ba 02 11 37  |v)-..G.P...*...7|
+00000020  3d ef e6 2a db d0 47 47  a7 9a 5f 43 2d 98 78 26  |=..*..GG.._C-.x&|
+00000030  81 e2 f1 ba fe f7 66 c6  61 cb c1 b7 60 62 34 a5  |......f.a...`b4.|
+00000040  78 67 50 3d 9a 0e 4a 8c  8f d7 10 14 03 03 00 01  |xgP=..J.........|
+00000050  01 16 03 03 00 40 5e 46  b0 5d 30 f6 da 8f 9e 67  |.....@^F.]0....g|
+00000060  f5 3e bd fe c9 b8 53 b2  10 d5 7c 0e 34 e3 93 6d  |.>....S...|.4..m|
+00000070  0e 8e 8a 2b df fb 9a 0f  a5 23 55 e7 0a 4b e2 d3  |...+.....#U..K..|
+00000080  db 15 e8 52 74 26 78 b3  b0 56 65 63 ac ae 1e c0  |...Rt&x..Vec....|
+00000090  0b f4 92 56 a9 04                                 |...V..|
 >>> Flow 4 (server to client)
 00000000  14 03 03 00 01 01 16 03  03 00 40 00 00 00 00 00  |..........@.....|
-00000010  00 00 00 00 00 00 00 00  00 00 00 7e f1 fc 1d 0c  |...........~....|
-00000020  f5 a2 c6 35 de 78 97 62  72 3f 05 6c a3 a8 0e cb  |...5.x.br?.l....|
-00000030  10 7e c0 3d 28 c7 d9 4e  71 f4 18 d7 14 42 09 5c  |.~.=(..Nq....B.\|
-00000040  22 26 04 1f 04 12 9f 88  3d 4a 4a 17 03 03 00 40  |"&......=JJ....@|
+00000010  00 00 00 00 00 00 00 00  00 00 00 16 a9 63 0a 99  |.............c..|
+00000020  21 8a fc 5c b3 ee 05 71  4e 75 c0 d9 40 54 0d 3e  |!..\...qNu..@T.>|
+00000030  4e 5d 44 b7 4b 5d a9 e7  5a 30 ed b6 d5 08 50 b1  |N]D.K]..Z0....P.|
+00000040  e8 8c 54 eb 1b 39 7a f9  3b ac 2e 17 03 03 00 40  |..T..9z.;......@|
 00000050  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
-00000060  0f 35 50 38 be 3a c7 4e  c4 de 36 63 85 c1 7a 78  |.5P8.:.N..6c..zx|
-00000070  c6 7f 65 8c d1 44 c5 7e  45 32 60 88 93 bf 10 82  |..e..D.~E2`.....|
-00000080  4b 1a 46 9a 60 54 c5 ee  2a c1 86 02 a7 b6 d5 ea  |K.F.`T..*.......|
+00000060  96 03 20 2b 20 c4 c1 9a  76 7b f3 96 bd 33 ed e6  |.. + ...v{...3..|
+00000070  38 48 ea 53 d5 e0 62 b5  7e 1a 36 a8 dd 9f 2d 4b  |8H.S..b.~.6...-K|
+00000080  06 0d ae f6 bc 99 14 b3  93 14 27 63 e2 a0 c8 76  |..........'c...v|
 00000090  15 03 03 00 30 00 00 00  00 00 00 00 00 00 00 00  |....0...........|
-000000a0  00 00 00 00 00 78 6c 41  05 2f 6f c2 d7 70 54 24  |.....xlA./o..pT$|
-000000b0  66 01 2c 1e 71 43 05 3a  1b 9e 86 ff b4 c5 65 b2  |f.,.qC.:......e.|
-000000c0  f0 f8 ef 6b 25                                    |...k%|
+000000a0  00 00 00 00 00 48 af e1  e4 11 e1 b7 03 19 b0 e3  |.....H..........|
+000000b0  e6 a9 66 d8 ac af aa 03  f6 0d 51 df 9a 27 78 3a  |..f.......Q..'x:|
+000000c0  56 5a 03 1a 4c                                    |VZ..L|
index de2f0aba8b31c64f6eac846722e42b6badaefae4..88abb15a7e6c83c67c14f6f553e72fefc1116eb1 100644 (file)
@@ -1,7 +1,7 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 00 ca 01 00 00  c6 03 03 52 cc 5e 7f ec  |...........R.^..|
-00000010  d7 b4 0c ac 92 e8 d1 6e  df c1 e6 ee f5 84 5e 1a  |.......n......^.|
-00000020  1d 05 bf 2d 3f 71 91 d1  cc b7 f8 00 00 32 c0 30  |...-?q.......2.0|
+00000000  16 03 01 00 ca 01 00 00  c6 03 03 53 04 f1 3f cc  |...........S..?.|
+00000010  41 74 00 07 cb ae 3b 30  79 48 51 60 41 a3 8c ab  |At....;0yHQ`A...|
+00000020  dc 76 f9 74 52 1e c5 fb  a9 69 c2 00 00 32 c0 30  |.v.tR....i...2.0|
 00000030  c0 2c c0 28 c0 24 c0 14  c0 0a c0 22 c0 21 00 a3  |.,.(.$.....".!..|
 00000040  00 9f 00 6b 00 6a 00 39  00 38 00 88 00 87 c0 32  |...k.j.9.8.....2|
 00000050  c0 2e c0 2a c0 26 c0 0f  c0 05 00 9d 00 3d 00 35  |...*.&.......=.5|
 000002c0  5a 5f 33 c4 b6 d8 c9 75  90 96 8c 0f 52 98 b5 cd  |Z_3....u....R...|
 000002d0  98 1f 89 20 5f f2 a0 1c  a3 1b 96 94 dd a9 fd 57  |... _..........W|
 000002e0  e9 70 e8 26 6d 71 99 9b  26 6e 38 50 29 6c 90 a7  |.p.&mq..&n8P)l..|
-000002f0  bd d9 16 03 03 01 11 0c  00 01 0d 03 00 19 85 04  |................|
-00000300  01 39 dc ee 44 17 5e db  d7 27 af b6 56 d9 b4 43  |.9..D.^..'..V..C|
-00000310  5a 99 cf aa 31 37 0c 6f  3a a0 f8 53 c4 74 d1 91  |Z...17.o:..S.t..|
-00000320  0a 46 f5 38 3b 5c 09 d8  97 dc 4b aa 70 26 48 f2  |.F.8;\....K.p&H.|
-00000330  d6 0b 31 c9 f8 d4 98 43  e1 6c d5 c7 b2 8e 0b 01  |..1....C.l......|
-00000340  e6 b6 00 28 80 7b fc 96  8f 0d a2 4f b0 79 af dc  |...(.{.....O.y..|
-00000350  61 28 63 33 78 f6 31 39  fd 8a f4 15 18 11 fe db  |a(c3x.19........|
-00000360  d5 07 da 2c ed 49 a0 23  bf d0 3a 38 1d 54 ae 1c  |...,.I.#..:8.T..|
-00000370  7b ea 29 ee d0 38 c1 76  a7 7f 2a f4 ce 1e ac cc  |{.)..8.v..*.....|
-00000380  94 79 90 33 04 01 00 80  ad 89 a5 bf 16 74 a1 14  |.y.3.........t..|
-00000390  c4 a1 09 31 95 69 e4 b4  e3 8d df 99 73 cd e6 94  |...1.i......s...|
-000003a0  eb ca 07 7f f4 36 ca 31  1c 29 f0 f0 d8 40 6b 19  |.....6.1.)...@k.|
-000003b0  f2 15 be f1 76 22 b3 82  f7 bf 2b 09 0f cd 31 c8  |....v"....+...1.|
-000003c0  69 7b 7b 1a ed a1 f7 85  6e 04 5c fa a5 20 c0 ef  |i{{.....n.\.. ..|
-000003d0  c6 45 6d 05 25 37 ec f6  94 91 32 f3 c8 d1 f0 13  |.Em.%7....2.....|
-000003e0  81 1e 26 bb 4c 47 91 79  ad cf 7e 61 85 54 eb 13  |..&.LG.y..~a.T..|
-000003f0  6b b1 15 36 72 bf d1 ad  07 3e 6d bd 44 1a 30 ac  |k..6r....>m.D.0.|
-00000400  41 39 ad 75 14 bb 11 dc  16 03 03 00 04 0e 00 00  |A9.u............|
-00000410  00                                                |.|
+000002f0  bd d9 16 03 03 00 cd 0c  00 00 c9 03 00 17 41 04  |..............A.|
+00000300  1e 18 37 ef 0d 19 51 88  35 75 71 b5 e5 54 5b 12  |..7...Q.5uq..T[.|
+00000310  2e 8f 09 67 fd a7 24 20  3e b2 56 1c ce 97 28 5e  |...g..$ >.V...(^|
+00000320  f8 2b 2d 4f 9e f1 07 9f  6c 4b 5b 83 56 e2 32 42  |.+-O....lK[.V.2B|
+00000330  e9 58 b6 d7 49 a6 b5 68  1a 41 03 56 6b dc 5a 89  |.X..I..h.A.Vk.Z.|
+00000340  04 01 00 80 9d 84 09 35  73 fb f6 ea 94 7b 49 fb  |.......5s....{I.|
+00000350  c2 70 b1 11 64 5b 93 9f  d9 8c f5 56 98 f6 d3 66  |.p..d[.....V...f|
+00000360  a6 1d 18 56 88 87 71 3f  b0 38 9d 44 1f ad 2c 0d  |...V..q?.8.D..,.|
+00000370  3a a7 e8 d4 3e 33 3c 41  20 f3 3f 5c e5 fb e3 23  |:...>3<A .?\...#|
+00000380  12 48 ff d2 c4 30 7c 8a  51 3f 9f 19 6e 34 d7 60  |.H...0|.Q?..n4.`|
+00000390  7d 12 8a aa 90 0f 50 d9  0b 9a b2 d7 66 b1 c6 84  |}.....P.....f...|
+000003a0  af 5c e2 5e 16 3e 36 61  73 84 64 89 b3 c1 6d 50  |.\.^.>6as.d...mP|
+000003b0  33 55 c7 e1 c5 a5 4c 32  5c 95 dc 07 43 60 49 11  |3U....L2\...C`I.|
+000003c0  e9 98 cc ba 16 03 03 00  04 0e 00 00 00           |.............|
 >>> Flow 3 (client to server)
-00000000  16 03 03 00 8a 10 00 00  86 85 04 01 fb 77 96 9a  |.............w..|
-00000010  82 26 4f 44 b5 2f 32 28  0a dd 51 f5 a4 84 46 a1  |.&OD./2(..Q...F.|
-00000020  ba 58 e6 9a 96 1b 85 9f  ae 3a 8b db a8 93 81 00  |.X.......:......|
-00000030  17 be 24 26 17 fd b8 7c  fe 93 7f af 5f 4d c6 47  |..$&...|...._M.G|
-00000040  8b 72 5b 23 89 03 d5 a6  fb 6f de 59 15 00 bb 36  |.r[#.....o.Y...6|
-00000050  6d 72 03 47 61 b7 7e d4  46 43 b3 e9 9d 2f 61 6a  |mr.Ga.~.FC.../aj|
-00000060  08 1b 04 70 ac 95 ad bf  18 e5 09 b6 b3 0d 6a bb  |...p..........j.|
-00000070  e8 77 09 fa 81 2e 8a e1  61 7e 9f 38 d0 67 f5 11  |.w......a~.8.g..|
-00000080  f1 62 7f a4 69 4a 42 7a  f8 9e 05 26 66 34 6e 14  |.b..iJBz...&f4n.|
-00000090  03 03 00 01 01 16 03 03  00 40 2c a1 a8 3a 34 18  |.........@,..:4.|
-000000a0  ea a1 d4 28 0b 1a ac ab  51 b1 c5 48 f2 56 8d c7  |...(....Q..H.V..|
-000000b0  83 7b 70 44 40 7d 15 1c  00 19 ed 53 21 fe 9d c1  |.{pD@}.....S!...|
-000000c0  a2 13 8f a0 0c 51 f5 13  67 1f bf 07 da bc 2d ca  |.....Q..g.....-.|
-000000d0  7c 0f 53 4b 4a 02 bb 0f  72 c6                    ||.SKJ...r.|
+00000000  16 03 03 00 46 10 00 00  42 41 04 28 02 84 d5 b4  |....F...BA.(....|
+00000010  58 07 47 d5 a0 d6 0b 1d  37 91 e6 34 a4 ad 0b ad  |X.G.....7..4....|
+00000020  22 01 82 77 a7 32 86 78  83 3a da 75 2f e5 68 7a  |"..w.2.x.:.u/.hz|
+00000030  de e4 05 e0 02 47 40 4e  38 d2 2c c3 7b da 53 73  |.....G@N8.,.{.Ss|
+00000040  19 cb 8b 73 34 72 4d 33  71 39 c8 14 03 03 00 01  |...s4rM3q9......|
+00000050  01 16 03 03 00 40 10 63  43 76 83 bd 36 e4 1e 4d  |.....@.cCv..6..M|
+00000060  7e 13 b0 ac aa c8 ec 90  31 df 84 46 49 68 39 5a  |~.......1..FIh9Z|
+00000070  05 8b 73 32 86 15 3a 18  57 d8 e2 2c 2d 05 89 93  |..s2..:.W..,-...|
+00000080  37 b8 dd 73 33 92 ff a7  b2 53 27 94 b7 25 56 64  |7..s3....S'..%Vd|
+00000090  a1 d3 2c f7 6b 71                                 |..,.kq|
 >>> Flow 4 (server to client)
 00000000  14 03 03 00 01 01 16 03  03 00 40 00 00 00 00 00  |..........@.....|
-00000010  00 00 00 00 00 00 00 00  00 00 00 82 f6 03 51 7f  |..............Q.|
-00000020  37 19 ec 26 20 db e2 5b  8e 5e 22 29 1a 88 ca f1  |7..& ..[.^")....|
-00000030  ad 55 1c 3c 07 1d 05 b6  c4 88 58 84 a0 5d 33 41  |.U.<......X..]3A|
-00000040  7a 65 bc ba a1 71 a4 71  df 6c 9d 17 03 03 00 40  |ze...q.q.l.....@|
+00000010  00 00 00 00 00 00 00 00  00 00 00 21 5c 31 b1 4b  |...........!\1.K|
+00000020  96 96 30 8f 79 35 3a 3a  2d 26 67 d0 70 48 be 30  |..0.y5::-&g.pH.0|
+00000030  f8 3e e8 c1 cb 1d d5 89  f6 9c 72 bb 1c f9 4d 90  |.>........r...M.|
+00000040  9c d7 c6 fa 40 76 a5 61  46 61 24 17 03 03 00 40  |....@v.aFa$....@|
 00000050  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
-00000060  8d ca 51 a1 4a b1 23 dc  e3 ef 63 5f b0 e8 7a c6  |..Q.J.#...c_..z.|
-00000070  97 d7 18 6a 4b 80 3e 5c  7b 79 86 93 60 2c 8b f1  |...jK.>\{y..`,..|
-00000080  4e 46 c5 5e 64 0c 98 81  10 6d c5 08 22 f1 02 1d  |NF.^d....m.."...|
+00000060  94 8a 14 04 06 b9 30 a0  67 fd b2 4c 84 f4 10 93  |......0.g..L....|
+00000070  7d d4 2b 23 f0 e9 62 93  c2 20 a2 f2 7c 07 21 4b  |}.+#..b.. ..|.!K|
+00000080  94 ba 7b 7d cb 77 da 85  93 bd 53 ee ca db 9b 3e  |..{}.w....S....>|
 00000090  15 03 03 00 30 00 00 00  00 00 00 00 00 00 00 00  |....0...........|
-000000a0  00 00 00 00 00 51 19 c4  67 b7 14 6b 5c 49 ac 1d  |.....Q..g..k\I..|
-000000b0  b3 97 88 42 29 cb f5 06  54 f4 c6 38 9a 47 41 78  |...B)...T..8.GAx|
-000000c0  0f 33 21 ac c5                                    |.3!..|
+000000a0  00 00 00 00 00 17 3f 53  8d b3 35 b4 84 ed bb 12  |......?S..5.....|
+000000b0  cf 73 25 25 7c c3 d3 bb  1f 5a 6b 73 9a 8a b1 a2  |.s%%|....Zks....|
+000000c0  ba 99 f8 0e 43                                    |....C|
index fa6cb080c90544c8a8035ff9275851926b3b40bc..aacbb86705a07a1cbd5c6dddc62bff957d3c1cd0 100644 (file)
@@ -1,7 +1,7 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 00 9c 01 00 00  98 03 03 52 cc 57 59 54  |...........R.WYT|
-00000010  0b ba f3 ec 2c 33 3e 3e  ac b8 c1 d5 d5 ff e3 d3  |....,3>>........|
-00000020  63 d0 29 16 aa 5d 96 37  89 90 c4 00 00 04 c0 0a  |c.)..].7........|
+00000000  16 03 01 00 9c 01 00 00  98 03 03 53 04 f0 f9 09  |...........S....|
+00000010  13 56 01 37 84 b1 32 59  4c 73 b1 8e bb 02 1a 32  |.V.7..2YLs.....2|
+00000020  db ab 8c e6 ed ad 7f 52  9a 59 39 00 00 04 c0 0a  |.......R.Y9.....|
 00000030  00 ff 01 00 00 6b 00 0b  00 04 03 00 01 02 00 0a  |.....k..........|
 00000040  00 34 00 32 00 0e 00 0d  00 19 00 0b 00 0c 00 18  |.4.2............|
 00000050  00 09 00 0a 00 16 00 17  00 08 00 06 00 07 00 14  |................|
 00000210  0e bd 3f a3 8c 25 c1 33  13 83 0d 94 06 bb d4 37  |..?..%.3.......7|
 00000220  7a f6 ec 7a c9 86 2e dd  d7 11 69 7f 85 7c 56 de  |z..z......i..|V.|
 00000230  fb 31 78 2b e4 c7 78 0d  ae cb be 9e 4e 36 24 31  |.1x+..x.....N6$1|
-00000240  7b 6a 0f 39 95 12 07 8f  2a 16 03 03 01 1c 0c 00  |{j.9....*.......|
-00000250  01 18 03 00 19 85 04 01  39 dc ee 44 17 5e db d7  |........9..D.^..|
-00000260  27 af b6 56 d9 b4 43 5a  99 cf aa 31 37 0c 6f 3a  |'..V..CZ...17.o:|
-00000270  a0 f8 53 c4 74 d1 91 0a  46 f5 38 3b 5c 09 d8 97  |..S.t...F.8;\...|
-00000280  dc 4b aa 70 26 48 f2 d6  0b 31 c9 f8 d4 98 43 e1  |.K.p&H...1....C.|
-00000290  6c d5 c7 b2 8e 0b 01 e6  b6 00 28 80 7b fc 96 8f  |l.........(.{...|
-000002a0  0d a2 4f b0 79 af dc 61  28 63 33 78 f6 31 39 fd  |..O.y..a(c3x.19.|
-000002b0  8a f4 15 18 11 fe db d5  07 da 2c ed 49 a0 23 bf  |..........,.I.#.|
-000002c0  d0 3a 38 1d 54 ae 1c 7b  ea 29 ee d0 38 c1 76 a7  |.:8.T..{.)..8.v.|
-000002d0  7f 2a f4 ce 1e ac cc 94  79 90 33 04 03 00 8b 30  |.*......y.3....0|
-000002e0  81 88 02 42 00 c6 85 8e  06 b7 04 04 e9 cd 9e 3e  |...B...........>|
-000002f0  cb 66 23 95 b4 42 9c 64  81 39 05 3f b5 21 f8 28  |.f#..B.d.9.?.!.(|
-00000300  af 60 6b 4d 3d ba a1 4b  5e 77 ef e7 59 28 fe 1d  |.`kM=..K^w..Y(..|
-00000310  c1 27 a2 ff a8 de 33 48  b3 c1 85 6a 42 9b f9 7e  |.'....3H...jB..~|
-00000320  7e 31 c2 e5 bd 66 02 42  00 ad 7d 06 35 ab ec 8d  |~1...f.B..}.5...|
-00000330  ac d4 ba 1b 49 5e 05 5f  f0 97 93 82 b8 2b 8d 91  |....I^._.....+..|
-00000340  98 63 8e b4 14 62 db 1e  c9 2b eb 95 36 9b 44 c6  |.c...b...+..6.D.|
-00000350  a6 89 58 50 f9 30 94 89  ef 0f 71 ed c0 42 59 11  |..XP.0....q..BY.|
-00000360  68 e7 ac 52 2c 1e ed 70  b1 f8 16 03 03 00 04 0e  |h..R,..p........|
-00000370  00 00 00                                          |...|
+00000240  7b 6a 0f 39 95 12 07 8f  2a 16 03 03 00 d8 0c 00  |{j.9....*.......|
+00000250  00 d4 03 00 17 41 04 1e  18 37 ef 0d 19 51 88 35  |.....A...7...Q.5|
+00000260  75 71 b5 e5 54 5b 12 2e  8f 09 67 fd a7 24 20 3e  |uq..T[....g..$ >|
+00000270  b2 56 1c ce 97 28 5e f8  2b 2d 4f 9e f1 07 9f 6c  |.V...(^.+-O....l|
+00000280  4b 5b 83 56 e2 32 42 e9  58 b6 d7 49 a6 b5 68 1a  |K[.V.2B.X..I..h.|
+00000290  41 03 56 6b dc 5a 89 04  03 00 8b 30 81 88 02 42  |A.Vk.Z.....0...B|
+000002a0  00 c6 85 8e 06 b7 04 04  e9 cd 9e 3e cb 66 23 95  |...........>.f#.|
+000002b0  b4 42 9c 64 81 39 05 3f  b5 21 f8 28 af 60 6b 4d  |.B.d.9.?.!.(.`kM|
+000002c0  3d ba a1 4b 5e 77 ef e7  59 28 fe 1d c1 27 a2 ff  |=..K^w..Y(...'..|
+000002d0  a8 de 33 48 b3 c1 85 6a  42 9b f9 7e 7e 31 c2 e5  |..3H...jB..~~1..|
+000002e0  bd 66 02 42 00 ad 7d 06  35 ab ec 8d ac d4 ba 1b  |.f.B..}.5.......|
+000002f0  49 5e 05 5f f0 97 93 82  b8 2b 8d 91 98 63 8e b4  |I^._.....+...c..|
+00000300  14 62 db 1e c9 2c 13 ae  b7 d3 17 38 23 2f f6 7f  |.b...,.....8#/..|
+00000310  0c 4d d3 33 d2 79 d1 77  ee cb b1 c2 fc 34 b8 69  |.M.3.y.w.....4.i|
+00000320  f9 10 8b 61 89 85 16 03  03 00 04 0e 00 00 00     |...a...........|
 >>> Flow 3 (client to server)
-00000000  16 03 03 00 8a 10 00 00  86 85 04 01 7d 90 e6 a1  |............}...|
-00000010  06 d6 a9 32 e9 ba 42 d5  05 11 e7 69 7c 5e 6e b6  |...2..B....i|^n.|
-00000020  ad 51 bc 25 12 89 93 e0  bf 3c 2f ce a8 83 29 0e  |.Q.%.....</...).|
-00000030  eb 06 89 10 f4 de ce d9  16 6c 95 dc 61 66 84 87  |.........l..af..|
-00000040  84 6b 47 77 01 55 79 a2  0d e0 e8 d0 bd 00 7e d7  |.kGw.Uy.......~.|
-00000050  34 a4 75 e4 c8 ac e4 a3  4b df 27 52 7a f7 1b 0f  |4.u.....K.'Rz...|
-00000060  73 af 26 66 2c c9 29 56  b9 e2 1b b8 02 21 80 74  |s.&f,.)V.....!.t|
-00000070  db c8 d4 99 31 8f 6c 41  a6 b2 ac 60 82 13 85 08  |....1.lA...`....|
-00000080  4d 34 f2 77 ba 22 ec 05  92 9c 9b ca 2d 8e dc 14  |M4.w."......-...|
-00000090  03 03 00 01 01 16 03 03  00 40 19 4c 60 51 51 ed  |.........@.L`QQ.|
-000000a0  03 16 fe 05 a4 88 17 91  f0 0a 50 0d e4 a8 82 9a  |..........P.....|
-000000b0  4d 4b ef 2a 3d 57 29 60  e7 36 70 9c 41 e2 93 89  |MK.*=W)`.6p.A...|
-000000c0  b7 b0 f6 76 fc 19 93 7b  ac 8e 39 d1 7c 90 73 62  |...v...{..9.|.sb|
-000000d0  88 bf 0b 20 f9 fd 49 b4  d9 3c                    |... ..I..<|
+00000000  16 03 03 00 46 10 00 00  42 41 04 dd 22 68 a1 4e  |....F...BA.."h.N|
+00000010  04 1b 47 f9 c5 7d 04 1d  d8 fe 84 fa be 31 2e a7  |..G..}.......1..|
+00000020  f8 e5 b8 14 92 44 99 11  0e 34 97 fc e5 b1 91 cf  |.....D...4......|
+00000030  a4 d1 3f b4 71 94 c6 06  16 f0 98 c0 3e 05 f9 2f  |..?.q.......>../|
+00000040  0a 97 78 3d ef dc fa a2  d7 ee 7d 14 03 03 00 01  |..x=......}.....|
+00000050  01 16 03 03 00 40 90 bf  7f e9 c9 6e d1 80 f5 12  |.....@.....n....|
+00000060  6d c5 b7 c5 15 4b 18 a5  d3 18 1e f8 8c 4d 7e 6d  |m....K.......M~m|
+00000070  03 60 29 7c 45 7c b2 ca  8c 07 71 70 aa 23 fa 6e  |.`)|E|....qp.#.n|
+00000080  d9 0b 0a 32 4c 9e e5 00  f9 19 9b b6 8d dc d3 67  |...2L..........g|
+00000090  3d 0f bb b8 4b 9e                                 |=...K.|
 >>> Flow 4 (server to client)
 00000000  14 03 03 00 01 01 16 03  03 00 40 00 00 00 00 00  |..........@.....|
-00000010  00 00 00 00 00 00 00 00  00 00 00 f1 df 07 dd 3f  |...............?|
-00000020  92 27 96 9a 5c 98 4a 3b  39 0b 5d 2e 5f 12 55 11  |.'..\.J;9.]._.U.|
-00000030  32 4d 14 39 31 02 1b 02  ee b0 ae 2c 19 8d c2 31  |2M.91......,...1|
-00000040  52 cc e9 c3 d6 34 ac 4d  7f e6 6e 17 03 03 00 40  |R....4.M..n....@|
+00000010  00 00 00 00 00 00 00 00  00 00 00 a1 6e e5 d1 ca  |............n...|
+00000020  03 f4 77 dc ec ee 5d f0  22 5e 7f 55 1a 8d ad 45  |..w...]."^.U...E|
+00000030  09 f1 3b b2 61 36 dc 3d  2a 1e 1f e5 a7 84 76 a9  |..;.a6.=*.....v.|
+00000040  41 5b 86 03 ac 22 18 20  9b a9 29 17 03 03 00 40  |A[...". ..)....@|
 00000050  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
-00000060  6b 1e 9a b5 a8 89 3c d0  05 08 cf 81 a7 02 b4 ed  |k.....<.........|
-00000070  fa 21 e2 15 87 d0 78 ac  4b 8e fc d0 c5 0e b2 5c  |.!....x.K......\|
-00000080  bb c6 49 2a 80 00 67 93  37 4c a6 38 a7 24 f3 05  |..I*..g.7L.8.$..|
+00000060  f5 cb 28 1e b5 bc 82 7f  82 38 54 14 e8 b9 6d 3b  |..(......8T...m;|
+00000070  bc 99 d6 0e f9 00 96 99  a8 92 2e 86 9d 62 4e 90  |.............bN.|
+00000080  27 52 58 45 20 93 90 a1  f3 a8 89 2b e7 21 24 16  |'RXE ......+.!$.|
 00000090  15 03 03 00 30 00 00 00  00 00 00 00 00 00 00 00  |....0...........|
-000000a0  00 00 00 00 00 50 ea de  de b1 ba e4 da b1 d2 3e  |.....P.........>|
-000000b0  b3 ed 03 2f 8e 30 d5 20  f8 2a 65 d4 4c 1c b9 7f  |.../.0. .*e.L...|
-000000c0  4c 0c 8f cf 5a                                    |L...Z|
+000000a0  00 00 00 00 00 a8 2a ab  8f b0 ce 49 8b fd a5 c9  |......*....I....|
+000000b0  11 b2 04 83 18 f3 1d 6c  82 34 1d df dd 2f 45 3b  |.......l.4.../E;|
+000000c0  27 8a 0f 16 69                                    |'...i|
index 6f7e0e40ab1c1b68c8789cc22bfb0da53abbb5f9..0ddfe022f22ea822dfdadca39d756235df52c74d 100644 (file)
@@ -1,7 +1,7 @@
 >>> Flow 1 (client to server)
-00000000  16 03 01 00 9c 01 00 00  98 03 03 52 cc 57 59 d3  |...........R.WY.|
-00000010  91 67 1b de 52 be 25 1d  61 3b 45 29 43 aa 8a e9  |.g..R.%.a;E)C...|
-00000020  fc 29 19 d5 59 aa 48 0d  21 8a eb 00 00 04 c0 2f  |.)..Y.H.!....../|
+00000000  16 03 01 00 9c 01 00 00  98 03 03 53 04 f1 30 73  |...........S..0s|
+00000010  a1 ea 8c d2 90 1c c6 d6  0d 3c af 58 21 65 90 25  |.........<.X!e.%|
+00000020  5e fa f4 27 22 65 c9 68  90 b9 04 00 00 04 c0 2f  |^..'"e.h......./|
 00000030  00 ff 01 00 00 6b 00 0b  00 04 03 00 01 02 00 0a  |.....k..........|
 00000040  00 34 00 32 00 0e 00 0d  00 19 00 0b 00 0c 00 18  |.4.2............|
 00000050  00 09 00 0a 00 16 00 17  00 08 00 06 00 07 00 14  |................|
 000002c0  50 56 5c d5 82 5a 2d 5a  5f 33 c4 b6 d8 c9 75 90  |PV\..Z-Z_3....u.|
 000002d0  96 8c 0f 52 98 b5 cd 98  1f 89 20 5f f2 a0 1c a3  |...R...... _....|
 000002e0  1b 96 94 dd a9 fd 57 e9  70 e8 26 6d 71 99 9b 26  |......W.p.&mq..&|
-000002f0  6e 38 50 29 6c 90 a7 bd  d9 16 03 03 01 11 0c 00  |n8P)l...........|
-00000300  01 0d 03 00 19 85 04 01  39 dc ee 44 17 5e db d7  |........9..D.^..|
-00000310  27 af b6 56 d9 b4 43 5a  99 cf aa 31 37 0c 6f 3a  |'..V..CZ...17.o:|
-00000320  a0 f8 53 c4 74 d1 91 0a  46 f5 38 3b 5c 09 d8 97  |..S.t...F.8;\...|
-00000330  dc 4b aa 70 26 48 f2 d6  0b 31 c9 f8 d4 98 43 e1  |.K.p&H...1....C.|
-00000340  6c d5 c7 b2 8e 0b 01 e6  b6 00 28 80 7b fc 96 8f  |l.........(.{...|
-00000350  0d a2 4f b0 79 af dc 61  28 63 33 78 f6 31 39 fd  |..O.y..a(c3x.19.|
-00000360  8a f4 15 18 11 fe db d5  07 da 2c ed 49 a0 23 bf  |..........,.I.#.|
-00000370  d0 3a 38 1d 54 ae 1c 7b  ea 29 ee d0 38 c1 76 a7  |.:8.T..{.)..8.v.|
-00000380  7f 2a f4 ce 1e ac cc 94  79 90 33 04 01 00 80 5f  |.*......y.3...._|
-00000390  c6 9e 6d 87 97 04 b5 c2  12 73 75 ff a5 40 d0 0f  |..m......su..@..|
-000003a0  39 74 98 e5 1f 62 4c e8  9a af a0 d4 08 61 d4 53  |9t...bL......a.S|
-000003b0  67 ee f6 27 45 1a ee 52  35 7a 5f 5b 54 4a de 9b  |g..'E..R5z_[TJ..|
-000003c0  fb 9a a2 61 e1 db be b0  08 5d 4f fc b5 5d d3 bc  |...a.....]O..]..|
-000003d0  c9 49 e2 b9 d8 52 52 79  d1 a3 8e da 7d 35 12 19  |.I...RRy....}5..|
-000003e0  45 30 fb fd e7 e2 f4 00  43 78 64 ca 2b 6c 65 28  |E0......Cxd.+le(|
-000003f0  8a 8d 83 4f cf 44 9a 19  89 4d 08 d8 85 b3 65 95  |...O.D...M....e.|
-00000400  e8 47 4d 86 25 48 09 5c  77 7d 73 0c 6b 22 22 16  |.GM.%H.\w}s.k"".|
-00000410  03 03 00 04 0e 00 00 00                           |........|
+000002f0  6e 38 50 29 6c 90 a7 bd  d9 16 03 03 00 cd 0c 00  |n8P)l...........|
+00000300  00 c9 03 00 17 41 04 1e  18 37 ef 0d 19 51 88 35  |.....A...7...Q.5|
+00000310  75 71 b5 e5 54 5b 12 2e  8f 09 67 fd a7 24 20 3e  |uq..T[....g..$ >|
+00000320  b2 56 1c ce 97 28 5e f8  2b 2d 4f 9e f1 07 9f 6c  |.V...(^.+-O....l|
+00000330  4b 5b 83 56 e2 32 42 e9  58 b6 d7 49 a6 b5 68 1a  |K[.V.2B.X..I..h.|
+00000340  41 03 56 6b dc 5a 89 04  01 00 80 a2 54 61 84 29  |A.Vk.Z......Ta.)|
+00000350  3e 97 4b 97 9a 9f 5c c0  49 6d 86 d2 79 8e 95 a1  |>.K...\.Im..y...|
+00000360  0a 5a 36 73 34 bb 05 73  35 47 e1 2b 5d f3 ef 36  |.Z6s4..s5G.+]..6|
+00000370  a8 32 e2 7e ef aa 3f 1f  b3 64 60 d4 06 2e 98 e3  |.2.~..?..d`.....|
+00000380  11 e2 60 3c d6 20 17 63  b2 6f a0 cd 21 01 2b 4e  |..`<. .c.o..!.+N|
+00000390  b2 a8 55 04 39 37 5c 6c  71 66 4d a3 eb 1b 83 67  |..U.97\lqfM....g|
+000003a0  6b 15 a0 56 9a f1 a2 79  92 29 ce 58 3c 10 4d 65  |k..V...y.).X<.Me|
+000003b0  1f 22 e3 ea d8 74 aa 01  7e ca f3 89 23 41 4d bd  |."...t..~...#AM.|
+000003c0  df 77 4e 59 54 97 74 ad  07 ea c0 16 03 03 00 04  |.wNYT.t.........|
+000003d0  0e 00 00 00                                       |....|
 >>> Flow 3 (client to server)
-00000000  16 03 03 00 8a 10 00 00  86 85 04 01 4e f2 1d 1f  |............N...|
-00000010  c0 23 cf 00 58 0b 25 ee  6b e1 5f 50 7b c9 15 9d  |.#..X.%.k._P{...|
-00000020  d9 2f 8a f1 b8 7b 62 d2  6d d0 46 b8 6f 3f 2d 6f  |./...{b.m.F.o?-o|
-00000030  ba 68 74 7e a3 b5 12 1c  93 d1 0a 13 e1 50 d7 82  |.ht~.........P..|
-00000040  1b 4c 54 b5 73 a9 9e 72  80 4e bc 75 17 00 e1 f3  |.LT.s..r.N.u....|
-00000050  70 03 80 1f d3 1f 2a 53  52 6a ee 4e 93 f4 10 1c  |p.....*SRj.N....|
-00000060  2d ff 5f 6c a4 3b fa a1  7f 87 93 5d 76 b3 35 62  |-._l.;.....]v.5b|
-00000070  0b 48 41 42 f9 57 65 4a  42 9e 53 7d 2c 09 37 02  |.HAB.WeJB.S},.7.|
-00000080  55 bd 6f 0e 4d 05 17 8d  c5 df ff 54 da 94 d6 14  |U.o.M......T....|
-00000090  03 03 00 01 01 16 03 03  00 28 38 e3 ad 08 8e e3  |.........(8.....|
-000000a0  b8 bc 6d a2 15 35 b1 b2  28 47 82 63 30 9e b6 5c  |..m..5..(G.c0..\|
-000000b0  26 47 38 20 a0 77 e3 b2  38 8f 8b c4 96 ac f4 5c  |&G8 .w..8......\|
-000000c0  10 9f                                             |..|
+00000000  16 03 03 00 46 10 00 00  42 41 04 45 65 ce f7 b9  |....F...BA.Ee...|
+00000010  52 e3 fb 13 db 91 f2 65  43 84 57 f5 1a 19 a0 e6  |R......eC.W.....|
+00000020  89 2d bb 2c 83 6b 62 f6  6f 1f 26 ae 59 67 bd dc  |.-.,.kb.o.&.Yg..|
+00000030  c4 9e 0b dc 7d 6e f8 6b  95 8c 61 47 3d cd d1 df  |....}n.k..aG=...|
+00000040  82 45 30 81 c3 a3 49 5d  85 59 70 14 03 03 00 01  |.E0...I].Yp.....|
+00000050  01 16 03 03 00 28 3f aa  85 33 f9 c6 95 a0 56 ff  |.....(?..3....V.|
+00000060  1c f1 5a ba 6e 41 50 0c  ab 92 e1 e2 8e 89 1c f1  |..Z.nAP.........|
+00000070  fa 54 1b f1 f5 00 01 12  6d c4 96 78 b6 87        |.T......m..x..|
 >>> Flow 4 (server to client)
 00000000  14 03 03 00 01 01 16 03  03 00 28 00 00 00 00 00  |..........(.....|
-00000010  00 00 00 30 ae 4d 36 49  d1 d6 4d 1a 71 87 eb ed  |...0.M6I..M.q...|
-00000020  d2 6f 66 3f 25 03 a5 69  04 5d ca e6 71 eb c2 06  |.of?%..i.]..q...|
-00000030  b4 15 b7 17 03 03 00 25  00 00 00 00 00 00 00 01  |.......%........|
-00000040  8d a6 27 08 34 77 a2 a7  f5 e6 c3 ca 49 25 db 9a  |..'.4w......I%..|
-00000050  19 44 42 d1 0b c1 3a d6  73 b2 11 df 52 15 03 03  |.DB...:.s...R...|
-00000060  00 1a 00 00 00 00 00 00  00 02 42 63 43 f7 98 69  |..........BcC..i|
-00000070  d9 2b 38 6b 88 9d bf a2  8a 31 5d 54              |.+8k.....1]T|
+00000010  00 00 00 94 5c be 46 05  d6 d0 b0 3a 56 dc 2c 10  |....\.F....:V.,.|
+00000020  0f 6f 5d 33 33 7f a5 4e  74 84 bf 63 87 c4 f4 49  |.o]33..Nt..c...I|
+00000030  bc 6b ab 17 03 03 00 25  00 00 00 00 00 00 00 01  |.k.....%........|
+00000040  7e 4f f9 ae ae fe 6b a0  4a f8 0f 0b b4 b6 65 b6  |~O....k.J.....e.|
+00000050  be 24 5f 94 6d d1 db 54  11 07 b9 ce 01 15 03 03  |.$_.m..T........|
+00000060  00 1a 00 00 00 00 00 00  00 02 a8 1c d6 62 ac fd  |.............b..|
+00000070  77 ba 23 92 5d 34 f1 17  c7 e1 1c 99              |w.#.]4......|
index 40156a0013bc9fb982d2cd143d57c2759232f6e7..d50e1202924a600ffeb2fd369661f319938d1127 100644 (file)
@@ -15,6 +15,7 @@ import (
        "io/ioutil"
        "net"
        "strings"
+       "time"
 )
 
 // Server returns a new TLS server side connection
@@ -27,7 +28,7 @@ func Server(conn net.Conn, config *Config) *Conn {
 
 // Client returns a new TLS client side connection
 // using conn as the underlying transport.
-// The config cannot be nil: users must set either ServerHostname or
+// The config cannot be nil: users must set either ServerName or
 // InsecureSkipVerify in the config.
 func Client(conn net.Conn, config *Config) *Conn {
        return &Conn{conn: conn, config: config, isClient: true}
@@ -76,24 +77,51 @@ func Listen(network, laddr string, config *Config) (net.Listener, error) {
        return NewListener(l, config), nil
 }
 
-// Dial connects to the given network address using net.Dial
-// and then initiates a TLS handshake, returning the resulting
-// TLS connection.
-// Dial interprets a nil configuration as equivalent to
-// the zero configuration; see the documentation of Config
-// for the defaults.
-func Dial(network, addr string, config *Config) (*Conn, error) {
-       raddr := addr
-       c, err := net.Dial(network, raddr)
+type timeoutError struct{}
+
+func (timeoutError) Error() string   { return "tls: DialWithDialer timed out" }
+func (timeoutError) Timeout() bool   { return true }
+func (timeoutError) Temporary() bool { return true }
+
+// DialWithDialer connects to the given network address using dialer.Dial and
+// then initiates a TLS handshake, returning the resulting TLS connection. Any
+// timeout or deadline given in the dialer apply to connection and TLS
+// handshake as a whole.
+//
+// DialWithDialer interprets a nil configuration as equivalent to the zero
+// configuration; see the documentation of Config for the defaults.
+func DialWithDialer(dialer *net.Dialer, network, addr string, config *Config) (*Conn, error) {
+       // We want the Timeout and Deadline values from dialer to cover the
+       // whole process: TCP connection and TLS handshake. This means that we
+       // also need to start our own timers now.
+       timeout := dialer.Timeout
+
+       if !dialer.Deadline.IsZero() {
+               deadlineTimeout := dialer.Deadline.Sub(time.Now())
+               if timeout == 0 || deadlineTimeout < timeout {
+                       timeout = deadlineTimeout
+               }
+       }
+
+       var errChannel chan error
+
+       if timeout != 0 {
+               errChannel = make(chan error, 2)
+               time.AfterFunc(timeout, func() {
+                       errChannel <- timeoutError{}
+               })
+       }
+
+       rawConn, err := dialer.Dial(network, addr)
        if err != nil {
                return nil, err
        }
 
-       colonPos := strings.LastIndex(raddr, ":")
+       colonPos := strings.LastIndex(addr, ":")
        if colonPos == -1 {
-               colonPos = len(raddr)
+               colonPos = len(addr)
        }
-       hostname := raddr[:colonPos]
+       hostname := addr[:colonPos]
 
        if config == nil {
                config = defaultConfig()
@@ -106,14 +134,37 @@ func Dial(network, addr string, config *Config) (*Conn, error) {
                c.ServerName = hostname
                config = &c
        }
-       conn := Client(c, config)
-       if err = conn.Handshake(); err != nil {
-               c.Close()
+
+       conn := Client(rawConn, config)
+
+       if timeout == 0 {
+               err = conn.Handshake()
+       } else {
+               go func() {
+                       errChannel <- conn.Handshake()
+               }()
+
+               err = <-errChannel
+       }
+
+       if err != nil {
+               rawConn.Close()
                return nil, err
        }
+
        return conn, nil
 }
 
+// Dial connects to the given network address using net.Dial
+// and then initiates a TLS handshake, returning the resulting
+// TLS connection.
+// Dial interprets a nil configuration as equivalent to
+// the zero configuration; see the documentation of Config
+// for the defaults.
+func Dial(network, addr string, config *Config) (*Conn, error) {
+       return DialWithDialer(new(net.Dialer), network, addr, config)
+}
+
 // LoadX509KeyPair reads and parses a public/private key pair from a pair of
 // files. The files must contain PEM encoded data.
 func LoadX509KeyPair(certFile, keyFile string) (cert Certificate, err error) {
index 38229014cd662bba804577ed638acd139274999b..f8c94ff35d4735553915ed64d50eb176cff2a04d 100644 (file)
@@ -5,7 +5,12 @@
 package tls
 
 import (
+       "fmt"
+       "io"
+       "net"
+       "strings"
        "testing"
+       "time"
 )
 
 var rsaCertPEM = `-----BEGIN CERTIFICATE-----
@@ -105,3 +110,128 @@ func TestX509MixedKeyPair(t *testing.T) {
                t.Error("Load of ECDSA certificate succeeded with RSA private key")
        }
 }
+
+func newLocalListener(t *testing.T) net.Listener {
+       ln, err := net.Listen("tcp", "127.0.0.1:0")
+       if err != nil {
+               ln, err = net.Listen("tcp6", "[::1]:0")
+       }
+       if err != nil {
+               t.Fatal(err)
+       }
+       return ln
+}
+
+func TestDialTimeout(t *testing.T) {
+       if testing.Short() {
+               t.Skip("skipping in short mode")
+       }
+       listener := newLocalListener(t)
+
+       addr := listener.Addr().String()
+       defer listener.Close()
+
+       complete := make(chan bool)
+       defer close(complete)
+
+       go func() {
+               conn, err := listener.Accept()
+               if err != nil {
+                       t.Error(err)
+                       return
+               }
+               <-complete
+               conn.Close()
+       }()
+
+       dialer := &net.Dialer{
+               Timeout: 10 * time.Millisecond,
+       }
+
+       var err error
+       if _, err = DialWithDialer(dialer, "tcp", addr, nil); err == nil {
+               t.Fatal("DialWithTimeout completed successfully")
+       }
+
+       if !strings.Contains(err.Error(), "timed out") {
+               t.Errorf("resulting error not a timeout: %s", err)
+       }
+}
+
+// tests that Conn.Read returns (non-zero, io.EOF) instead of
+// (non-zero, nil) when a Close (alertCloseNotify) is sitting right
+// behind the application data in the buffer.
+func TestConnReadNonzeroAndEOF(t *testing.T) {
+       // This test is racy: it assumes that after a write to a
+       // localhost TCP connection, the peer TCP connection can
+       // immediately read it.  Because it's racy, we skip this test
+       // in short mode, and then retry it several times with an
+       // increasing sleep in between our final write (via srv.Close
+       // below) and the following read.
+       if testing.Short() {
+               t.Skip("skipping in short mode")
+       }
+       var err error
+       for delay := time.Millisecond; delay <= 64*time.Millisecond; delay *= 2 {
+               if err = testConnReadNonzeroAndEOF(t, delay); err == nil {
+                       return
+               }
+       }
+       t.Error(err)
+}
+
+func testConnReadNonzeroAndEOF(t *testing.T, delay time.Duration) error {
+       ln := newLocalListener(t)
+       defer ln.Close()
+
+       srvCh := make(chan *Conn, 1)
+       var serr error
+       go func() {
+               sconn, err := ln.Accept()
+               if err != nil {
+                       serr = err
+                       srvCh <- nil
+                       return
+               }
+               serverConfig := *testConfig
+               srv := Server(sconn, &serverConfig)
+               if err := srv.Handshake(); err != nil {
+                       serr = fmt.Errorf("handshake: %v", err)
+                       srvCh <- nil
+                       return
+               }
+               srvCh <- srv
+       }()
+
+       clientConfig := *testConfig
+       conn, err := Dial("tcp", ln.Addr().String(), &clientConfig)
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer conn.Close()
+
+       srv := <-srvCh
+       if srv == nil {
+               return serr
+       }
+
+       buf := make([]byte, 6)
+
+       srv.Write([]byte("foobar"))
+       n, err := conn.Read(buf)
+       if n != 6 || err != nil || string(buf) != "foobar" {
+               return fmt.Errorf("Read = %d, %v, data %q; want 6, nil, foobar", n, err, buf)
+       }
+
+       srv.Write([]byte("abcdef"))
+       srv.Close()
+       time.Sleep(delay)
+       n, err = conn.Read(buf)
+       if n != 6 || string(buf) != "abcdef" {
+               return fmt.Errorf("Read = %d, buf= %q; want 6, abcdef", n, buf)
+       }
+       if err != io.EOF {
+               return fmt.Errorf("Second Read error = %v; want io.EOF", err)
+       }
+       return nil
+}
index a5bd19e821695d6352a00f2ecf2d428b6f6a129b..11ad3c440d20468f8f6a9cba43fdde9f839a7809 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.
 
-// +build dragonfly freebsd linux openbsd netbsd solaris
+// +build dragonfly freebsd linux nacl netbsd openbsd solaris
 
 package x509
 
index ba6c13d45102e666a3debb96a76bbb581b7a684f..96b9d9b420b09147b9f3ac80b85718c5cb733789 100644 (file)
@@ -31,8 +31,8 @@ type verifyTest struct {
 var verifyTests = []verifyTest{
        {
                leaf:                 googleLeaf,
-               intermediates:        []string{thawteIntermediate},
-               currentTime:          1302726541,
+               intermediates:        []string{giag2Intermediate},
+               currentTime:          1395785200,
                dnsName:              "www.google.com",
                testSystemRootsError: true,
 
@@ -42,39 +42,39 @@ var verifyTests = []verifyTest{
        },
        {
                leaf:          googleLeaf,
-               intermediates: []string{thawteIntermediate},
-               roots:         []string{verisignRoot},
-               currentTime:   1302726541,
+               intermediates: []string{giag2Intermediate},
+               roots:         []string{geoTrustRoot},
+               currentTime:   1395785200,
                dnsName:       "www.google.com",
 
                expectedChains: [][]string{
-                       {"Google", "Thawte", "VeriSign"},
+                       {"Google", "Google Internet Authority", "GeoTrust"},
                },
        },
        {
                leaf:          googleLeaf,
-               intermediates: []string{thawteIntermediate},
-               roots:         []string{verisignRoot},
-               currentTime:   1302726541,
+               intermediates: []string{giag2Intermediate},
+               roots:         []string{geoTrustRoot},
+               currentTime:   1395785200,
                dnsName:       "WwW.GooGLE.coM",
 
                expectedChains: [][]string{
-                       {"Google", "Thawte", "VeriSign"},
+                       {"Google", "Google Internet Authority", "GeoTrust"},
                },
        },
        {
                leaf:          googleLeaf,
-               intermediates: []string{thawteIntermediate},
-               roots:         []string{verisignRoot},
-               currentTime:   1302726541,
+               intermediates: []string{giag2Intermediate},
+               roots:         []string{geoTrustRoot},
+               currentTime:   1395785200,
                dnsName:       "www.example.com",
 
                errorCallback: expectHostnameError,
        },
        {
                leaf:          googleLeaf,
-               intermediates: []string{thawteIntermediate},
-               roots:         []string{verisignRoot},
+               intermediates: []string{giag2Intermediate},
+               roots:         []string{geoTrustRoot},
                currentTime:   1,
                dnsName:       "www.example.com",
 
@@ -82,8 +82,8 @@ var verifyTests = []verifyTest{
        },
        {
                leaf:        googleLeaf,
-               roots:       []string{verisignRoot},
-               currentTime: 1302726541,
+               roots:       []string{geoTrustRoot},
+               currentTime: 1395785200,
                dnsName:     "www.google.com",
 
                // Skip when using systemVerify, since Windows
@@ -93,14 +93,22 @@ var verifyTests = []verifyTest{
        },
        {
                leaf:          googleLeaf,
-               intermediates: []string{verisignRoot, thawteIntermediate},
-               roots:         []string{verisignRoot},
-               currentTime:   1302726541,
+               intermediates: []string{geoTrustRoot, giag2Intermediate},
+               roots:         []string{geoTrustRoot},
+               currentTime:   1395785200,
                dnsName:       "www.google.com",
 
                expectedChains: [][]string{
-                       {"Google", "Thawte", "VeriSign"},
+                       {"Google", "Google Internet Authority", "GeoTrust"},
+                       // TODO(agl): this is ok, but it would be nice if the
+                       //            chain building didn't visit the same SPKI
+                       //            twice.
+                       {"Google", "Google Internet Authority", "GeoTrust", "GeoTrust"},
                },
+               // CAPI doesn't build the chain with the duplicated GeoTrust
+               // entry so the results don't match. Thus we skip this test
+               // until that's fixed.
+               systemSkip: true,
        },
        {
                leaf:          dnssecExpLeaf,
@@ -128,9 +136,9 @@ var verifyTests = []verifyTest{
        },
        {
                leaf:          googleLeafWithInvalidHash,
-               intermediates: []string{thawteIntermediate},
-               roots:         []string{verisignRoot},
-               currentTime:   1302726541,
+               intermediates: []string{giag2Intermediate},
+               roots:         []string{geoTrustRoot},
+               currentTime:   1395785200,
                dnsName:       "www.google.com",
 
                // The specific error message may not occur when using system
@@ -201,6 +209,24 @@ var verifyTests = []verifyTest{
                        },
                },
        },
+       {
+               // Check that SHA-384 intermediates (which are popping up)
+               // work.
+               leaf:          moipLeafCert,
+               intermediates: []string{comodoIntermediateSHA384, comodoRSAAuthority},
+               roots:         []string{addTrustRoot},
+               currentTime:   1397502195,
+               dnsName:       "api.moip.com.br",
+
+               expectedChains: [][]string{
+                       {
+                               "api.moip.com.br",
+                               "COMODO RSA Extended Validation Secure Server CA",
+                               "COMODO RSA Certification Authority",
+                               "AddTrust External CA Root",
+                       },
+               },
+       },
 }
 
 func expectHostnameError(t *testing.T, i int, err error) (ok bool) {
@@ -385,84 +411,111 @@ func nameToKey(name *pkix.Name) string {
        return strings.Join(name.Country, ",") + "/" + strings.Join(name.Organization, ",") + "/" + strings.Join(name.OrganizationalUnit, ",") + "/" + name.CommonName
 }
 
-const verisignRoot = `-----BEGIN CERTIFICATE-----
-MIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzELMAkG
-A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz
-cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2
-MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV
-BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmlt
-YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN
-ADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhE
-BarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/is
-I19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0G
-CSqGSIb3DQEBAgUAA4GBALtMEivPLCYATxQT3ab7/AoRhIzzKBxnki98tsX63/Do
-lbwdj2wsqFHMc9ikwFPwTtYmwHYBV4GSXiHx0bH/59AhWM1pF+NEHJwZRDmJXNyc
-AA9WjQKZ7aKQRUzkuxCkPfAyAw7xzvjoyVGM5mKf5p/AfbdynMk2OmufTqj/ZA1k
+const geoTrustRoot = `-----BEGIN CERTIFICATE-----
+MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT
+MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i
+YWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQG
+EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSR2VvVHJ1c3Qg
+R2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2swYYzD9
+9BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjoBbdq
+fnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDv
+iS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU
+1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+
+bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoW
+MPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTA
+ephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1l
+uMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKIn
+Z57QzxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfS
+tQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF
+PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un
+hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV
+5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw==
 -----END CERTIFICATE-----
 `
 
-const thawteIntermediate = `-----BEGIN CERTIFICATE-----
-MIIDIzCCAoygAwIBAgIEMAAAAjANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJV
-UzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xNzA1BgNVBAsTLkNsYXNzIDMgUHVi
-bGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQwNTEzMDAw
-MDAwWhcNMTQwNTEyMjM1OTU5WjBMMQswCQYDVQQGEwJaQTElMCMGA1UEChMcVGhh
-d3RlIENvbnN1bHRpbmcgKFB0eSkgTHRkLjEWMBQGA1UEAxMNVGhhd3RlIFNHQyBD
-QTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1NNn0I0Vf67NMf59HZGhPwtx
-PKzMyGT7Y/wySweUvW+Aui/hBJPAM/wJMyPpC3QrccQDxtLN4i/1CWPN/0ilAL/g
-5/OIty0y3pg25gqtAHvEZEo7hHUD8nCSfQ5i9SGraTaEMXWQ+L/HbIgbBpV8yeWo
-3nWhLHpo39XKHIdYYBkCAwEAAaOB/jCB+zASBgNVHRMBAf8ECDAGAQH/AgEAMAsG
-A1UdDwQEAwIBBjARBglghkgBhvhCAQEEBAMCAQYwKAYDVR0RBCEwH6QdMBsxGTAX
-BgNVBAMTEFByaXZhdGVMYWJlbDMtMTUwMQYDVR0fBCowKDAmoCSgIoYgaHR0cDov
-L2NybC52ZXJpc2lnbi5jb20vcGNhMy5jcmwwMgYIKwYBBQUHAQEEJjAkMCIGCCsG
-AQUFBzABhhZodHRwOi8vb2NzcC50aGF3dGUuY29tMDQGA1UdJQQtMCsGCCsGAQUF
-BwMBBggrBgEFBQcDAgYJYIZIAYb4QgQBBgpghkgBhvhFAQgBMA0GCSqGSIb3DQEB
-BQUAA4GBAFWsY+reod3SkF+fC852vhNRj5PZBSvIG3dLrWlQoe7e3P3bB+noOZTc
-q3J5Lwa/q4FwxKjt6lM07e8eU9kGx1Yr0Vz00YqOtCuxN5BICEIlxT6Ky3/rbwTR
-bcV0oveifHtgPHfNDs5IAn8BL7abN+AqKjbc1YXWrOU/VG+WHgWv
+const giag2Intermediate = `-----BEGIN CERTIFICATE-----
+MIIEBDCCAuygAwIBAgIDAjppMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT
+MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i
+YWwgQ0EwHhcNMTMwNDA1MTUxNTU1WhcNMTUwNDA0MTUxNTU1WjBJMQswCQYDVQQG
+EwJVUzETMBEGA1UEChMKR29vZ2xlIEluYzElMCMGA1UEAxMcR29vZ2xlIEludGVy
+bmV0IEF1dGhvcml0eSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+AJwqBHdc2FCROgajguDYUEi8iT/xGXAaiEZ+4I/F8YnOIe5a/mENtzJEiaB0C1NP
+VaTOgmKV7utZX8bhBYASxF6UP7xbSDj0U/ck5vuR6RXEz/RTDfRK/J9U3n2+oGtv
+h8DQUB8oMANA2ghzUWx//zo8pzcGjr1LEQTrfSTe5vn8MXH7lNVg8y5Kr0LSy+rE
+ahqyzFPdFUuLH8gZYR/Nnag+YyuENWllhMgZxUYi+FOVvuOAShDGKuy6lyARxzmZ
+EASg8GF6lSWMTlJ14rbtCMoU/M4iarNOz0YDl5cDfsCx3nuvRTPPuj5xt970JSXC
+DTWJnZ37DhF5iR43xa+OcmkCAwEAAaOB+zCB+DAfBgNVHSMEGDAWgBTAephojYn7
+qwVkDBF9qn1luMrMTjAdBgNVHQ4EFgQUSt0GFhu89mi1dvWBtrtiGrpagS8wEgYD
+VR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwOgYDVR0fBDMwMTAvoC2g
+K4YpaHR0cDovL2NybC5nZW90cnVzdC5jb20vY3Jscy9ndGdsb2JhbC5jcmwwPQYI
+KwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwOi8vZ3RnbG9iYWwtb2NzcC5n
+ZW90cnVzdC5jb20wFwYDVR0gBBAwDjAMBgorBgEEAdZ5AgUBMA0GCSqGSIb3DQEB
+BQUAA4IBAQA21waAESetKhSbOHezI6B1WLuxfoNCunLaHtiONgaX4PCVOzf9G0JY
+/iLIa704XtE7JW4S615ndkZAkNoUyHgN7ZVm2o6Gb4ChulYylYbc3GrKBIxbf/a/
+zG+FA1jDaFETzf3I93k9mTXwVqO94FntT0QJo544evZG0R0SnU++0ED8Vf4GXjza
+HFa9llF7b1cq26KqltyMdMKVvvBulRP/F/A8rLIQjcxz++iPAsbw+zOzlTvjwsto
+WHPbqCRiOwY1nQ2pM714A5AuTHhdUDqB1O6gyHA43LL5Z/qHQF1hwFGPa4NrzQU6
+yuGnBXj8ytqU0CwIPX4WecigUCAkVDNx
 -----END CERTIFICATE-----
 `
 
 const googleLeaf = `-----BEGIN CERTIFICATE-----
-MIIDITCCAoqgAwIBAgIQL9+89q6RUm0PmqPfQDQ+mjANBgkqhkiG9w0BAQUFADBM
-MQswCQYDVQQGEwJaQTElMCMGA1UEChMcVGhhd3RlIENvbnN1bHRpbmcgKFB0eSkg
-THRkLjEWMBQGA1UEAxMNVGhhd3RlIFNHQyBDQTAeFw0wOTEyMTgwMDAwMDBaFw0x
-MTEyMTgyMzU5NTlaMGgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlh
-MRYwFAYDVQQHFA1Nb3VudGFpbiBWaWV3MRMwEQYDVQQKFApHb29nbGUgSW5jMRcw
-FQYDVQQDFA53d3cuZ29vZ2xlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkC
-gYEA6PmGD5D6htffvXImttdEAoN4c9kCKO+IRTn7EOh8rqk41XXGOOsKFQebg+jN
-gtXj9xVoRaELGYW84u+E593y17iYwqG7tcFR39SDAqc9BkJb4SLD3muFXxzW2k6L
-05vuuWciKh0R73mkszeK9P4Y/bz5RiNQl/Os/CRGK1w7t0UCAwEAAaOB5zCB5DAM
-BgNVHRMBAf8EAjAAMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwudGhhd3Rl
-LmNvbS9UaGF3dGVTR0NDQS5jcmwwKAYDVR0lBCEwHwYIKwYBBQUHAwEGCCsGAQUF
-BwMCBglghkgBhvhCBAEwcgYIKwYBBQUHAQEEZjBkMCIGCCsGAQUFBzABhhZodHRw
-Oi8vb2NzcC50aGF3dGUuY29tMD4GCCsGAQUFBzAChjJodHRwOi8vd3d3LnRoYXd0
-ZS5jb20vcmVwb3NpdG9yeS9UaGF3dGVfU0dDX0NBLmNydDANBgkqhkiG9w0BAQUF
-AAOBgQCfQ89bxFApsb/isJr/aiEdLRLDLE5a+RLizrmCUi3nHX4adpaQedEkUjh5
-u2ONgJd8IyAPkU0Wueru9G2Jysa9zCRo1kNbzipYvzwY4OA8Ys+WAi0oR1A04Se6
-z5nRUP8pJcA2NhUzUnC+MY+f6H/nEQyNv4SgQhqAibAxWEEHXw==
------END CERTIFICATE-----`
+MIIEdjCCA16gAwIBAgIIcR5k4dkoe04wDQYJKoZIhvcNAQEFBQAwSTELMAkGA1UE
+BhMCVVMxEzARBgNVBAoTCkdvb2dsZSBJbmMxJTAjBgNVBAMTHEdvb2dsZSBJbnRl
+cm5ldCBBdXRob3JpdHkgRzIwHhcNMTQwMzEyMDkzODMwWhcNMTQwNjEwMDAwMDAw
+WjBoMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwN
+TW91bnRhaW4gVmlldzETMBEGA1UECgwKR29vZ2xlIEluYzEXMBUGA1UEAwwOd3d3
+Lmdvb2dsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC4zYCe
+m0oUBhwE0EwBr65eBOcgcQO2PaSIAB2dEP/c1EMX2tOy0ov8rk83ePhJ+MWdT1z6
+jge9X4zQQI8ZyA9qIiwrKBZOi8DNUvrqNZC7fJAVRrb9aX/99uYOJCypIbpmWG1q
+fhbHjJewhwf8xYPj71eU4rLG80a+DapWmphtfq3h52lDQIBzLVf1yYbyrTaELaz4
+NXF7HXb5YkId/gxIsSzM0aFUVu2o8sJcLYAsJqwfFKBKOMxUcn545nlspf0mTcWZ
+0APlbwsKznNs4/xCDwIxxWjjqgHrYAFl6y07i1gzbAOqdNEyR24p+3JWI8WZBlBI
+dk2KGj0W1fIfsvyxAgMBAAGjggFBMIIBPTAdBgNVHSUEFjAUBggrBgEFBQcDAQYI
+KwYBBQUHAwIwGQYDVR0RBBIwEIIOd3d3Lmdvb2dsZS5jb20waAYIKwYBBQUHAQEE
+XDBaMCsGCCsGAQUFBzAChh9odHRwOi8vcGtpLmdvb2dsZS5jb20vR0lBRzIuY3J0
+MCsGCCsGAQUFBzABhh9odHRwOi8vY2xpZW50czEuZ29vZ2xlLmNvbS9vY3NwMB0G
+A1UdDgQWBBTXD5Bx6iqT+dmEhbFL4OUoHyZn8zAMBgNVHRMBAf8EAjAAMB8GA1Ud
+IwQYMBaAFErdBhYbvPZotXb1gba7Yhq6WoEvMBcGA1UdIAQQMA4wDAYKKwYBBAHW
+eQIFATAwBgNVHR8EKTAnMCWgI6Ahhh9odHRwOi8vcGtpLmdvb2dsZS5jb20vR0lB
+RzIuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQCR3RJtHzgDh33b/MI1ugiki+nl8Ikj
+5larbJRE/rcA5oite+QJyAr6SU1gJJ/rRrK3ItVEHr9L621BCM7GSdoNMjB9MMcf
+tJAW0kYGJ+wqKm53wG/JaOADTnnq2Mt/j6F2uvjgN/ouns1nRHufIvd370N0LeH+
+orKqTuAPzXK7imQk6+OycYABbqCtC/9qmwRd8wwn7sF97DtYfK8WuNHtFalCAwyi
+8LxJJYJCLWoMhZ+V8GZm+FOex5qkQAjnZrtNlbQJ8ro4r+rpKXtmMFFhfa+7L+PA
+Kom08eUK8skxAzfDDijZPh10VtJ66uBoiDPdT+uCBehcBIcmSTrKjFGX
+-----END CERTIFICATE-----
+`
 
 // googleLeafWithInvalidHash is the same as googleLeaf, but the signature
 // algorithm in the certificate contains a nonsense OID.
 const googleLeafWithInvalidHash = `-----BEGIN CERTIFICATE-----
-MIIDITCCAoqgAwIBAgIQL9+89q6RUm0PmqPfQDQ+mjANBgkqhkiG9w0BATIFADBM
-MQswCQYDVQQGEwJaQTElMCMGA1UEChMcVGhhd3RlIENvbnN1bHRpbmcgKFB0eSkg
-THRkLjEWMBQGA1UEAxMNVGhhd3RlIFNHQyBDQTAeFw0wOTEyMTgwMDAwMDBaFw0x
-MTEyMTgyMzU5NTlaMGgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlh
-MRYwFAYDVQQHFA1Nb3VudGFpbiBWaWV3MRMwEQYDVQQKFApHb29nbGUgSW5jMRcw
-FQYDVQQDFA53d3cuZ29vZ2xlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkC
-gYEA6PmGD5D6htffvXImttdEAoN4c9kCKO+IRTn7EOh8rqk41XXGOOsKFQebg+jN
-gtXj9xVoRaELGYW84u+E593y17iYwqG7tcFR39SDAqc9BkJb4SLD3muFXxzW2k6L
-05vuuWciKh0R73mkszeK9P4Y/bz5RiNQl/Os/CRGK1w7t0UCAwEAAaOB5zCB5DAM
-BgNVHRMBAf8EAjAAMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwudGhhd3Rl
-LmNvbS9UaGF3dGVTR0NDQS5jcmwwKAYDVR0lBCEwHwYIKwYBBQUHAwEGCCsGAQUF
-BwMCBglghkgBhvhCBAEwcgYIKwYBBQUHAQEEZjBkMCIGCCsGAQUFBzABhhZodHRw
-Oi8vb2NzcC50aGF3dGUuY29tMD4GCCsGAQUFBzAChjJodHRwOi8vd3d3LnRoYXd0
-ZS5jb20vcmVwb3NpdG9yeS9UaGF3dGVfU0dDX0NBLmNydDANBgkqhkiG9w0BAVAF
-AAOBgQCfQ89bxFApsb/isJr/aiEdLRLDLE5a+RLizrmCUi3nHX4adpaQedEkUjh5
-u2ONgJd8IyAPkU0Wueru9G2Jysa9zCRo1kNbzipYvzwY4OA8Ys+WAi0oR1A04Se6
-z5nRUP8pJcA2NhUzUnC+MY+f6H/nEQyNv4SgQhqAibAxWEEHXw==
------END CERTIFICATE-----`
+MIIEdjCCA16gAwIBAgIIcR5k4dkoe04wDQYJKoZIhvcNAWAFBQAwSTELMAkGA1UE
+BhMCVVMxEzARBgNVBAoTCkdvb2dsZSBJbmMxJTAjBgNVBAMTHEdvb2dsZSBJbnRl
+cm5ldCBBdXRob3JpdHkgRzIwHhcNMTQwMzEyMDkzODMwWhcNMTQwNjEwMDAwMDAw
+WjBoMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwN
+TW91bnRhaW4gVmlldzETMBEGA1UECgwKR29vZ2xlIEluYzEXMBUGA1UEAwwOd3d3
+Lmdvb2dsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC4zYCe
+m0oUBhwE0EwBr65eBOcgcQO2PaSIAB2dEP/c1EMX2tOy0ov8rk83ePhJ+MWdT1z6
+jge9X4zQQI8ZyA9qIiwrKBZOi8DNUvrqNZC7fJAVRrb9aX/99uYOJCypIbpmWG1q
+fhbHjJewhwf8xYPj71eU4rLG80a+DapWmphtfq3h52lDQIBzLVf1yYbyrTaELaz4
+NXF7HXb5YkId/gxIsSzM0aFUVu2o8sJcLYAsJqwfFKBKOMxUcn545nlspf0mTcWZ
+0APlbwsKznNs4/xCDwIxxWjjqgHrYAFl6y07i1gzbAOqdNEyR24p+3JWI8WZBlBI
+dk2KGj0W1fIfsvyxAgMBAAGjggFBMIIBPTAdBgNVHSUEFjAUBggrBgEFBQcDAQYI
+KwYBBQUHAwIwGQYDVR0RBBIwEIIOd3d3Lmdvb2dsZS5jb20waAYIKwYBBQUHAQEE
+XDBaMCsGCCsGAQUFBzAChh9odHRwOi8vcGtpLmdvb2dsZS5jb20vR0lBRzIuY3J0
+MCsGCCsGAQUFBzABhh9odHRwOi8vY2xpZW50czEuZ29vZ2xlLmNvbS9vY3NwMB0G
+A1UdDgQWBBTXD5Bx6iqT+dmEhbFL4OUoHyZn8zAMBgNVHRMBAf8EAjAAMB8GA1Ud
+IwQYMBaAFErdBhYbvPZotXb1gba7Yhq6WoEvMBcGA1UdIAQQMA4wDAYKKwYBBAHW
+eQIFATAwBgNVHR8EKTAnMCWgI6Ahhh9odHRwOi8vcGtpLmdvb2dsZS5jb20vR0lB
+RzIuY3JsMA0GCSqGSIb3DQFgBQUAA4IBAQCR3RJtHzgDh33b/MI1ugiki+nl8Ikj
+5larbJRE/rcA5oite+QJyAr6SU1gJJ/rRrK3ItVEHr9L621BCM7GSdoNMjB9MMcf
+tJAW0kYGJ+wqKm53wG/JaOADTnnq2Mt/j6F2uvjgN/ouns1nRHufIvd370N0LeH+
+orKqTuAPzXK7imQk6+OycYABbqCtC/9qmwRd8wwn7sF97DtYfK8WuNHtFalCAwyi
+8LxJJYJCLWoMhZ+V8GZm+FOex5qkQAjnZrtNlbQJ8ro4r+rpKXtmMFFhfa+7L+PA
+Kom08eUK8skxAzfDDijZPh10VtJ66uBoiDPdT+uCBehcBIcmSTrKjFGX
+-----END CERTIFICATE-----
+`
 
 const dnssecExpLeaf = `-----BEGIN CERTIFICATE-----
 MIIGzTCCBbWgAwIBAgIDAdD6MA0GCSqGSIb3DQEBBQUAMIGMMQswCQYDVQQGEwJJ
@@ -936,3 +989,135 @@ AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad
 DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME
 HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==
 -----END CERTIFICATE-----`
+
+var moipLeafCert = `-----BEGIN CERTIFICATE-----
+MIIGQDCCBSigAwIBAgIRAPe/cwh7CUWizo8mYSDavLIwDQYJKoZIhvcNAQELBQAw
+gZIxCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO
+BgNVBAcTB1NhbGZvcmQxGjAYBgNVBAoTEUNPTU9ETyBDQSBMaW1pdGVkMTgwNgYD
+VQQDEy9DT01PRE8gUlNBIEV4dGVuZGVkIFZhbGlkYXRpb24gU2VjdXJlIFNlcnZl
+ciBDQTAeFw0xMzA4MTUwMDAwMDBaFw0xNDA4MTUyMzU5NTlaMIIBQjEXMBUGA1UE
+BRMOMDg3MTg0MzEwMDAxMDgxEzARBgsrBgEEAYI3PAIBAxMCQlIxGjAYBgsrBgEE
+AYI3PAIBAhMJU2FvIFBhdWxvMR0wGwYDVQQPExRQcml2YXRlIE9yZ2FuaXphdGlv
+bjELMAkGA1UEBhMCQlIxETAPBgNVBBETCDAxNDUyMDAwMRIwEAYDVQQIEwlTYW8g
+UGF1bG8xEjAQBgNVBAcTCVNhbyBQYXVsbzEtMCsGA1UECRMkQXZlbmlkYSBCcmln
+YWRlaXJvIEZhcmlhIExpbWEgLCAyOTI3MR0wGwYDVQQKExRNb2lwIFBhZ2FtZW50
+b3MgUy5BLjENMAsGA1UECxMETU9JUDEYMBYGA1UECxMPU1NMIEJsaW5kYWRvIEVW
+MRgwFgYDVQQDEw9hcGkubW9pcC5jb20uYnIwggEiMA0GCSqGSIb3DQEBAQUAA4IB
+DwAwggEKAoIBAQDN0b9x6TrXXA9hPCF8/NjqGJ++2D4LO4ZiMFTjs0VwpXy2Y1Oe
+s74/HuiLGnAHxTmAtV7IpZMibiOcTxcnDYp9oEWkf+gR+hZvwFZwyOBC7wyb3SR3
+UvV0N1ZbEVRYpN9kuX/3vjDghjDmzzBwu8a/T+y5JTym5uiJlngVAWyh/RjtIvYi
++NVkQMbyVlPGkoCe6c30pH8DKYuUCZU6DHjUsPTX3jAskqbhDSAnclX9iX0p2bmw
+KVBc+5Vh/2geyzDuquF0w+mNIYdU5h7uXvlmJnf3d2Cext5dxdL8/jezD3U0dAqI
+pYSKERbyxSkJWxdvRlhdpM9YXMJcpc88xNp1AgMBAAGjggHcMIIB2DAfBgNVHSME
+GDAWgBQ52v/KKBSKqHQTCLnkDqnS+n6daTAdBgNVHQ4EFgQU/lXuOa7DMExzZjRj
+LQWcMWGZY7swDgYDVR0PAQH/BAQDAgWgMAwGA1UdEwEB/wQCMAAwHQYDVR0lBBYw
+FAYIKwYBBQUHAwEGCCsGAQUFBwMCMEYGA1UdIAQ/MD0wOwYMKwYBBAGyMQECAQUB
+MCswKQYIKwYBBQUHAgEWHWh0dHBzOi8vc2VjdXJlLmNvbW9kby5jb20vQ1BTMFYG
+A1UdHwRPME0wS6BJoEeGRWh0dHA6Ly9jcmwuY29tb2RvY2EuY29tL0NPTU9ET1JT
+QUV4dGVuZGVkVmFsaWRhdGlvblNlY3VyZVNlcnZlckNBLmNybDCBhwYIKwYBBQUH
+AQEEezB5MFEGCCsGAQUFBzAChkVodHRwOi8vY3J0LmNvbW9kb2NhLmNvbS9DT01P
+RE9SU0FFeHRlbmRlZFZhbGlkYXRpb25TZWN1cmVTZXJ2ZXJDQS5jcnQwJAYIKwYB
+BQUHMAGGGGh0dHA6Ly9vY3NwLmNvbW9kb2NhLmNvbTAvBgNVHREEKDAmgg9hcGku
+bW9pcC5jb20uYnKCE3d3dy5hcGkubW9pcC5jb20uYnIwDQYJKoZIhvcNAQELBQAD
+ggEBAFoTmPlaDcf+nudhjXHwud8g7/LRyA8ucb+3/vfmgbn7FUc1eprF5sJS1mA+
+pbiTyXw4IxcJq2KUj0Nw3IPOe9k84mzh+XMmdCKH+QK3NWkE9Udz+VpBOBc0dlqC
+1RH5umStYDmuZg/8/r652eeQ5kUDcJyADfpKWBgDPYaGtwzKVT4h3Aok9SLXRHx6
+z/gOaMjEDMarMCMw4VUIG1pvNraZrG5oTaALPaIXXpd8VqbQYPudYJ6fR5eY3FeW
+H/ofbYFdRcuD26MfBFWE9VGGral9Fgo8sEHffho+UWhgApuQV4/l5fMzxB5YBXyQ
+jhuy8PqqZS9OuLilTeLu4a8z2JI=
+-----END CERTIFICATE-----`
+
+var comodoIntermediateSHA384 = `-----BEGIN CERTIFICATE-----
+MIIGDjCCA/agAwIBAgIQBqdDgNTr/tQ1taP34Wq92DANBgkqhkiG9w0BAQwFADCB
+hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
+A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV
+BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTIwMjEy
+MDAwMDAwWhcNMjcwMjExMjM1OTU5WjCBkjELMAkGA1UEBhMCR0IxGzAZBgNVBAgT
+EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR
+Q09NT0RPIENBIExpbWl0ZWQxODA2BgNVBAMTL0NPTU9ETyBSU0EgRXh0ZW5kZWQg
+VmFsaWRhdGlvbiBTZWN1cmUgU2VydmVyIENBMIIBIjANBgkqhkiG9w0BAQEFAAOC
+AQ8AMIIBCgKCAQEAlVbeVLTf1QJJe9FbXKKyHo+cK2JMK40SKPMalaPGEP0p3uGf
+CzhAk9HvbpUQ/OGQF3cs7nU+e2PsYZJuTzurgElr3wDqAwB/L3XVKC/sVmePgIOj
+vdwDmZOLlJFWW6G4ajo/Br0OksxgnP214J9mMF/b5pTwlWqvyIqvgNnmiDkBfBzA
+xSr3e5Wg8narbZtyOTDr0VdVAZ1YEZ18bYSPSeidCfw8/QpKdhQhXBZzQCMZdMO6
+WAqmli7eNuWf0MLw4eDBYuPCGEUZUaoXHugjddTI0JYT/8ck0YwLJ66eetw6YWNg
+iJctXQUL5Tvrrs46R3N2qPos3cCHF+msMJn4HwIDAQABo4IBaTCCAWUwHwYDVR0j
+BBgwFoAUu69+Aj36pvE8hI6t7jiY7NkyMtQwHQYDVR0OBBYEFDna/8ooFIqodBMI
+ueQOqdL6fp1pMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMBAf8ECDAGAQH/AgEAMD4G
+A1UdIAQ3MDUwMwYEVR0gADArMCkGCCsGAQUFBwIBFh1odHRwczovL3NlY3VyZS5j
+b21vZG8uY29tL0NQUzBMBgNVHR8ERTBDMEGgP6A9hjtodHRwOi8vY3JsLmNvbW9k
+b2NhLmNvbS9DT01PRE9SU0FDZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDBxBggr
+BgEFBQcBAQRlMGMwOwYIKwYBBQUHMAKGL2h0dHA6Ly9jcnQuY29tb2RvY2EuY29t
+L0NPTU9ET1JTQUFkZFRydXN0Q0EuY3J0MCQGCCsGAQUFBzABhhhodHRwOi8vb2Nz
+cC5jb21vZG9jYS5jb20wDQYJKoZIhvcNAQEMBQADggIBAERCnUFRK0iIXZebeV4R
+AUpSGXtBLMeJPNBy3IX6WK/VJeQT+FhlZ58N/1eLqYVeyqZLsKeyLeCMIs37/3mk
+jCuN/gI9JN6pXV/kD0fQ22YlPodHDK4ixVAihNftSlka9pOlk7DgG4HyVsTIEFPk
+1Hax0VtpS3ey4E/EhOfUoFDuPPpE/NBXueEoU/1Tzdy5H3pAvTA/2GzS8+cHnx8i
+teoiccsq8FZ8/qyo0QYPFBRSTP5kKwxpKrgNUG4+BAe/eiCL+O5lCeHHSQgyPQ0o
+fkkdt0rvAucNgBfIXOBhYsvss2B5JdoaZXOcOBCgJjqwyBZ9kzEi7nQLiMBciUEA
+KKlHMd99SUWa9eanRRrSjhMQ34Ovmw2tfn6dNVA0BM7pINae253UqNpktNEvWS5e
+ojZh1CSggjMziqHRbO9haKPl0latxf1eYusVqHQSTC8xjOnB3xBLAer2VBvNfzu9
+XJ/B288ByvK6YBIhMe2pZLiySVgXbVrXzYxtvp5/4gJYp9vDLVj2dAZqmvZh+fYA
+tmnYOosxWd2R5nwnI4fdAw+PKowegwFOAWEMUnNt/AiiuSpm5HZNMaBWm9lTjaK2
+jwLI5jqmBNFI+8NKAnb9L9K8E7bobTQk+p0pisehKxTxlgBzuRPpwLk6R1YCcYAn
+pLwltum95OmYdBbxN4SBB7SC
+-----END CERTIFICATE-----`
+
+const comodoRSAAuthority = `-----BEGIN CERTIFICATE-----
+MIIFdDCCBFygAwIBAgIQJ2buVutJ846r13Ci/ITeIjANBgkqhkiG9w0BAQwFADBv
+MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFk
+ZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBF
+eHRlcm5hbCBDQSBSb290MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFow
+gYUxCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO
+BgNVBAcTB1NhbGZvcmQxGjAYBgNVBAoTEUNPTU9ETyBDQSBMaW1pdGVkMSswKQYD
+VQQDEyJDT01PRE8gUlNBIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkq
+hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAkehUktIKVrGsDSTdxc9EZ3SZKzejfSNw
+AHG8U9/E+ioSj0t/EFa9n3Byt2F/yUsPF6c947AEYe7/EZfH9IY+Cvo+XPmT5jR6
+2RRr55yzhaCCenavcZDX7P0N+pxs+t+wgvQUfvm+xKYvT3+Zf7X8Z0NyvQwA1onr
+ayzT7Y+YHBSrfuXjbvzYqOSSJNpDa2K4Vf3qwbxstovzDo2a5JtsaZn4eEgwRdWt
+4Q08RWD8MpZRJ7xnw8outmvqRsfHIKCxH2XeSAi6pE6p8oNGN4Tr6MyBSENnTnIq
+m1y9TBsoilwie7SrmNnu4FGDwwlGTm0+mfqVF9p8M1dBPI1R7Qu2XK8sYxrfV8g/
+vOldxJuvRZnio1oktLqpVj3Pb6r/SVi+8Kj/9Lit6Tf7urj0Czr56ENCHonYhMsT
+8dm74YlguIwoVqwUHZwK53Hrzw7dPamWoUi9PPevtQ0iTMARgexWO/bTouJbt7IE
+IlKVgJNp6I5MZfGRAy1wdALqi2cVKWlSArvX31BqVUa/oKMoYX9w0MOiqiwhqkfO
+KJwGRXa/ghgntNWutMtQ5mv0TIZxMOmm3xaG4Nj/QN370EKIf6MzOi5cHkERgWPO
+GHFrK+ymircxXDpqR+DDeVnWIBqv8mqYqnK8V0rSS527EPywTEHl7R09XiidnMy/
+s1Hap0flhFMCAwEAAaOB9DCB8TAfBgNVHSMEGDAWgBStvZh6NLQm9/rEJlTvA73g
+JMtUGjAdBgNVHQ4EFgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQD
+AgGGMA8GA1UdEwEB/wQFMAMBAf8wEQYDVR0gBAowCDAGBgRVHSAAMEQGA1UdHwQ9
+MDswOaA3oDWGM2h0dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9BZGRUcnVzdEV4dGVy
+bmFsQ0FSb290LmNybDA1BggrBgEFBQcBAQQpMCcwJQYIKwYBBQUHMAGGGWh0dHA6
+Ly9vY3NwLnVzZXJ0cnVzdC5jb20wDQYJKoZIhvcNAQEMBQADggEBAGS/g/FfmoXQ
+zbihKVcN6Fr30ek+8nYEbvFScLsePP9NDXRqzIGCJdPDoCpdTPW6i6FtxFQJdcfj
+Jw5dhHk3QBN39bSsHNA7qxcS1u80GH4r6XnTq1dFDK8o+tDb5VCViLvfhVdpfZLY
+Uspzgb8c8+a4bmYRBbMelC1/kZWSWfFMzqORcUx8Rww7Cxn2obFshj5cqsQugsv5
+B5a6SE2Q8pTIqXOi6wZ7I53eovNNVZ96YUWYGGjHXkBrI/V5eu+MtWuLt29G9Hvx
+PUsE2JOAWVrgQSQdso8VYFhH2+9uRv0V9dlfmrPb2LjkQLPNlzmuhbsdjrzch5vR
+pu/xO28QOG8=
+-----END CERTIFICATE-----`
+
+const addTrustRoot = `-----BEGIN CERTIFICATE-----
+MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEU
+MBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFs
+IFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290
+MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFowbzELMAkGA1UEBhMCU0Ux
+FDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRUcnVzdCBFeHRlcm5h
+bCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0EgUm9v
+dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvt
+H7xsD821+iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9
+uMq/NzgtHj6RQa1wVsfwTz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzX
+mk6vBbOmcZSccbNQYArHE504B4YCqOmoaSYYkKtMsE8jqzpPhNjfzp/haW+710LX
+a0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy2xSoRcRdKn23tNbE7qzN
+E0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv77+ldU9U0
+WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYD
+VR0PBAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0
+Jvf6xCZU7wO94CTLVBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRU
+cnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsx
+IjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3SCAQEwDQYJKoZIhvcN
+AQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZlj7DYd7usQWxH
+YINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5
+6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvC
+Nr4TDea9Y355e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEX
+c4g/VhsxOBi0cQ+azcgOno4uG+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5a
+mnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ=
+-----END CERTIFICATE-----`
index 3570e02359e804bce6b3c77350e467ef749b4dff..c347fb384dc73f5b63b39911cf1121a3c3d5e53e 100644 (file)
@@ -14,6 +14,7 @@ import (
        "crypto/rsa"
        "crypto/sha1"
        _ "crypto/sha256"
+       _ "crypto/sha512"
        "crypto/x509/pkix"
        "encoding/asn1"
        "encoding/pem"
index d158a9d86310871fcf8eadf92153acdab1bf42fe..41d186b5b64cf04a6cb3e10cdf7b87b340cf8f68 100644 (file)
@@ -22,6 +22,7 @@ import (
        "net"
        "os/exec"
        "reflect"
+       "runtime"
        "testing"
        "time"
 )
@@ -395,7 +396,7 @@ func TestCreateSelfSignedCertificate(t *testing.T) {
                }
 
                if cert.SignatureAlgorithm != test.sigAlgo {
-                       t.Errorf("%s: SignatureAlgorithm wasn't copied from template. Got %s, want %s", test.name, cert.SignatureAlgorithm, test.sigAlgo)
+                       t.Errorf("%s: SignatureAlgorithm wasn't copied from template. Got %v, want %v", test.name, cert.SignatureAlgorithm, test.sigAlgo)
                }
 
                if !reflect.DeepEqual(cert.ExtKeyUsage, testExtKeyUsage) {
@@ -728,6 +729,10 @@ func TestParsePEMCRL(t *testing.T) {
 
 func TestImports(t *testing.T) {
        t.Skip("gccgo does not have a go command")
+       if runtime.GOOS == "nacl" {
+               t.Skip("skipping on nacl")
+       }
+
        if err := exec.Command("go", "run", "x509_test_import.go").Run(); err != nil {
                t.Errorf("failed to run x509_test_import.go: %s", err)
        }
index 0828e63c656899f27a3af97688f8944c83f6092b..eca25f29a0aa125dbdd5537b4d7708b0b16f6d63 100644 (file)
@@ -134,7 +134,7 @@ type Stmt interface {
        // as an INSERT or UPDATE.
        Exec(args []Value) (Result, error)
 
-       // Exec executes a query that may return rows, such as a
+       // Query executes a query that may return rows, such as a
        // SELECT.
        Query(args []Value) (Rows, error)
 }
index 4f86d24b2e5e819b149e399b2e8d16f14516091f..765b80c60a20fe5004d53202a8f20245d7bfd1ab 100644 (file)
@@ -181,7 +181,8 @@ type Scanner interface {
 // defers this error until a Scan.
 var ErrNoRows = errors.New("sql: no rows in result set")
 
-// DB is a database handle. It's safe for concurrent use by multiple
+// DB is a database handle representing a pool of zero or more
+// underlying connections. It's safe for concurrent use by multiple
 // goroutines.
 //
 // The sql package creates and frees connections automatically; it
@@ -405,7 +406,7 @@ func (db *DB) removeDepLocked(x finalCloser, dep interface{}) func() error {
 // This value should be larger than the maximum typical value
 // used for db.maxOpen. If maxOpen is significantly larger than
 // connectionRequestQueueSize then it is possible for ALL calls into the *DB
-// to block until the connectionOpener can satify the backlog of requests.
+// to block until the connectionOpener can satisfy the backlog of requests.
 var connectionRequestQueueSize = 1000000
 
 // Open opens a database specified by its database driver name and a
@@ -420,6 +421,11 @@ var connectionRequestQueueSize = 1000000
 // Open may just validate its arguments without creating a connection
 // to the database. To verify that the data source name is valid, call
 // Ping.
+//
+// The returned DB is safe for concurrent use by multiple goroutines
+// and maintains its own pool of idle connections. Thus, the Open
+// function should be called just once. It is rarely necessary to
+// close a DB.
 func Open(driverName, dataSourceName string) (*DB, error) {
        driveri, ok := drivers[driverName]
        if !ok {
@@ -452,6 +458,9 @@ func (db *DB) Ping() error {
 }
 
 // Close closes the database, releasing any open resources.
+//
+// It is rare to Close a DB, as the DB handle is meant to be
+// long-lived and shared between many goroutines.
 func (db *DB) Close() error {
        db.mu.Lock()
        if db.closed { // Make DB.Close idempotent
@@ -652,13 +661,16 @@ func (db *DB) conn() (*driverConn, error) {
                return conn, nil
        }
 
+       db.numOpen++ // optimistically
        db.mu.Unlock()
        ci, err := db.driver.Open(db.dsn)
        if err != nil {
+               db.mu.Lock()
+               db.numOpen-- // correct for earlier optimism
+               db.mu.Unlock()
                return nil, err
        }
        db.mu.Lock()
-       db.numOpen++
        dc := &driverConn{
                db: db,
                ci: ci,
@@ -778,7 +790,7 @@ func (db *DB) putConn(dc *driverConn, err error) {
 // connection limit will not be exceeded.
 // If err != nil, the value of dc is ignored.
 // If err == nil, then dc must not equal nil.
-// If a connRequest was fullfilled or the *driverConn was placed in the
+// If a connRequest was fulfilled or the *driverConn was placed in the
 // freeConn list, then true is returned, otherwise false is returned.
 func (db *DB) putConnDBLocked(dc *driverConn, err error) bool {
        if db.connRequests.Len() > 0 {
@@ -1494,6 +1506,7 @@ func (s *Stmt) finalClose() error {
 //
 //     rows, err := db.Query("SELECT ...")
 //     ...
+//     defer rows.Close()
 //     for rows.Next() {
 //         var id int
 //         var name string
index a0a20df6f83600d757ee4e6e0ad37072907fc2a9..7971f149174391cf23ddf63b48ed4d988f644c47 100644 (file)
@@ -461,7 +461,7 @@ func TestTxStmt(t *testing.T) {
 }
 
 // Issue: http://golang.org/issue/2784
-// This test didn't fail before because we got luckly with the fakedb driver.
+// This test didn't fail before because we got lucky with the fakedb driver.
 // It was failing, and now not, in github.com/bradfitz/go-sql-test
 func TestTxQuery(t *testing.T) {
        db := newTestDB(t, "")
index 2aa0c270ff2509b67954fd101df039166a1fb963..6cc6bc937a51f0e2f4a4e7000e4d6893d81b52bb 100644 (file)
@@ -212,6 +212,10 @@ const (
        formExprloc     format = 0x18
        formFlagPresent format = 0x19
        formRefSig8     format = 0x20
+       // Extensions for multi-file compression (.dwz)
+       // http://www.dwarfstd.org/ShowIssue.php?issue=120604.1
+       formGnuRefAlt  format = 0x1f20
+       formGnuStrpAlt format = 0x1f21
 )
 
 // A Tag is the classification (the type) of an Entry.
index 1772221633f11d963e5e1731603689879aa68fd3..b6ba8c0d1c333fa29e741238c4eac827d059fc58 100644 (file)
@@ -241,10 +241,10 @@ func (b *buf) entry(atab abbrevTable, ubase Offset) *Entry {
                // lineptr, loclistptr, macptr, rangelistptr
                // New in DWARF 4, but clang can generate them with -gdwarf-2.
                // Section reference, replacing use of formData4 and formData8.
-               case formSecOffset:
+               case formSecOffset, formGnuRefAlt, formGnuStrpAlt:
                        is64, known := b.format.dwarf64()
                        if !known {
-                               b.error("unknown size for DW_FORM_sec_offset")
+                               b.error("unknown size for form 0x" + strconv.FormatInt(int64(fmt), 16))
                        } else if is64 {
                                val = int64(b.uint64())
                        } else {
index 03e42b034657c049cb52fcac5cfdea96d547b68e..d622dae2af7984efac16c6994895809f99c7f74f 100644 (file)
@@ -517,7 +517,7 @@ const (
        DT_INIT_ARRAY   DynTag = 25 /* Address of the array of pointers to initialization functions */
        DT_FINI_ARRAY   DynTag = 26 /* Address of the array of pointers to termination functions */
        DT_INIT_ARRAYSZ DynTag = 27 /* Size in bytes of the array of initialization functions. */
-       DT_FINI_ARRAYSZ DynTag = 28 /* Size in bytes of the array of terminationfunctions. */
+       DT_FINI_ARRAYSZ DynTag = 28 /* Size in bytes of the array of termination functions. */
        DT_RUNPATH      DynTag = 29 /* String table offset of a null-terminated library search path string. */
        DT_FLAGS        DynTag = 30 /* Object specific flag values. */
        DT_ENCODING     DynTag = 32 /* Values greater than or equal to DT_ENCODING
index a98b469b33036ae97c7bdee6399f98ae13bf6953..38269aaf4ab34598645ee753687ef772d94cd0a7 100644 (file)
@@ -522,13 +522,17 @@ func (f *File) applyRelocations(dst []byte, rels []byte) error {
        if f.Class == ELFCLASS64 && f.Machine == EM_X86_64 {
                return f.applyRelocationsAMD64(dst, rels)
        }
+       if f.Class == ELFCLASS32 && f.Machine == EM_386 {
+               return f.applyRelocations386(dst, rels)
+       }
 
        return errors.New("not implemented")
 }
 
 func (f *File) applyRelocationsAMD64(dst []byte, rels []byte) error {
-       if len(rels)%Sym64Size != 0 {
-               return errors.New("length of relocation section is not a multiple of Sym64Size")
+       // 24 is the size of Rela64.
+       if len(rels)%24 != 0 {
+               return errors.New("length of relocation section is not a multiple of 24")
        }
 
        symbols, _, err := f.getSymbols(SHT_SYMTAB)
@@ -570,6 +574,43 @@ func (f *File) applyRelocationsAMD64(dst []byte, rels []byte) error {
        return nil
 }
 
+func (f *File) applyRelocations386(dst []byte, rels []byte) error {
+       // 8 is the size of Rel32.
+       if len(rels)%8 != 0 {
+               return errors.New("length of relocation section is not a multiple of 8")
+       }
+
+       symbols, _, err := f.getSymbols(SHT_SYMTAB)
+       if err != nil {
+               return err
+       }
+
+       b := bytes.NewReader(rels)
+       var rel Rel32
+
+       for b.Len() > 0 {
+               binary.Read(b, f.ByteOrder, &rel)
+               symNo := rel.Info >> 8
+               t := R_386(rel.Info & 0xff)
+
+               if symNo == 0 || symNo > uint32(len(symbols)) {
+                       continue
+               }
+               sym := &symbols[symNo-1]
+
+               if t == R_386_32 {
+                       if rel.Off+4 >= uint32(len(dst)) {
+                               continue
+                       }
+                       val := f.ByteOrder.Uint32(dst[rel.Off : rel.Off+4])
+                       val += uint32(sym.Value)
+                       f.ByteOrder.PutUint32(dst[rel.Off:rel.Off+4], val)
+               }
+       }
+
+       return nil
+}
+
 func (f *File) DWARF() (*dwarf.Data, error) {
        // There are many other DWARF sections, but these
        // are the required ones, and the debug/dwarf package
@@ -603,6 +644,19 @@ func (f *File) DWARF() (*dwarf.Data, error) {
                }
        }
 
+       // When using clang we need to process relocations even for 386.
+       rel := f.Section(".rel.debug_info")
+       if rel != nil && rel.Type == SHT_REL && f.Machine == EM_386 {
+               data, err := rel.Data()
+               if err != nil {
+                       return nil, err
+               }
+               err = f.applyRelocations(dat[1], data)
+               if err != nil {
+                       return nil, err
+               }
+       }
+
        abbrev, info, line, ranges, str := dat[0], dat[1], dat[2], dat[3], dat[4]
        d, err := dwarf.New(abbrev, nil, nil, info, line, nil, ranges, str)
        if err != nil {
index 38b5f9e70756b6f304d0bb7ea1fd5114cebba3e1..7f88a54bcd6dec74359f46bddd29cd219f665d64 100644 (file)
@@ -260,6 +260,12 @@ var relocationTests = []relocationTest{
                        {0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "GNU C 4.2.4 (Ubuntu 4.2.4-1ubuntu4)"}, {Attr: dwarf.AttrLanguage, Val: int64(1)}, {Attr: dwarf.AttrName, Val: "go-relocation-test-gcc424.c"}, {Attr: dwarf.AttrCompDir, Val: "/tmp"}, {Attr: dwarf.AttrLowpc, Val: uint64(0x0)}, {Attr: dwarf.AttrHighpc, Val: uint64(0x6)}, {Attr: dwarf.AttrStmtList, Val: int64(0)}}}},
                },
        },
+       {
+               "testdata/go-relocation-test-clang-x86.obj",
+               []relocationTestEntry{
+                       {0, &dwarf.Entry{Offset: 0xb, Tag: dwarf.TagCompileUnit, Children: true, Field: []dwarf.Field{{Attr: dwarf.AttrProducer, Val: "clang version google3-trunk (trunk r209387)"}, {Attr: dwarf.AttrLanguage, Val: int64(12)}, {Attr: dwarf.AttrName, Val: "go-relocation-test-clang.c"}, {Attr: dwarf.AttrStmtList, Val: int64(0)}, {Attr: dwarf.AttrCompDir, Val: "/tmp"}}}},
+               },
+       },
        {
                "testdata/gcc-amd64-openbsd-debug-with-rela.obj",
                []relocationTestEntry{
diff --git a/libgo/go/debug/elf/testdata/go-relocation-test-clang-x86.obj b/libgo/go/debug/elf/testdata/go-relocation-test-clang-x86.obj
new file mode 100644 (file)
index 0000000..e909cf4
Binary files /dev/null and b/libgo/go/debug/elf/testdata/go-relocation-test-clang-x86.obj differ
diff --git a/libgo/go/debug/goobj/read.go b/libgo/go/debug/goobj/read.go
deleted file mode 100644 (file)
index f65abb6..0000000
+++ /dev/null
@@ -1,655 +0,0 @@
-// Copyright 2013 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 goobj implements reading of Go object files and archives.
-//
-// TODO(rsc): Decide where this package should live. (golang.org/issue/6932)
-// TODO(rsc): Decide the appropriate integer types for various fields.
-// TODO(rsc): Write tests. (File format still up in the air a little.)
-package goobj
-
-import (
-       "bufio"
-       "bytes"
-       "errors"
-       "fmt"
-       "io"
-       "strconv"
-       "strings"
-)
-
-// A SymKind describes the kind of memory represented by a symbol.
-type SymKind int
-
-// This list is taken from include/link.h.
-
-// Defined SymKind values.
-// TODO(rsc): Give idiomatic Go names.
-// TODO(rsc): Reduce the number of symbol types in the object files.
-const (
-       _ SymKind = iota
-
-       // readonly, executable
-       STEXT
-       SELFRXSECT
-
-       // readonly, non-executable
-       STYPE
-       SSTRING
-       SGOSTRING
-       SGOFUNC
-       SRODATA
-       SFUNCTAB
-       STYPELINK
-       SSYMTAB // TODO: move to unmapped section
-       SPCLNTAB
-       SELFROSECT
-
-       // writable, non-executable
-       SMACHOPLT
-       SELFSECT
-       SMACHO // Mach-O __nl_symbol_ptr
-       SMACHOGOT
-       SNOPTRDATA
-       SINITARR
-       SDATA
-       SWINDOWS
-       SBSS
-       SNOPTRBSS
-       STLSBSS
-
-       // not mapped
-       SXREF
-       SMACHOSYMSTR
-       SMACHOSYMTAB
-       SMACHOINDIRECTPLT
-       SMACHOINDIRECTGOT
-       SFILE
-       SFILEPATH
-       SCONST
-       SDYNIMPORT
-       SHOSTOBJ
-)
-
-var symKindStrings = []string{
-       SBSS:              "SBSS",
-       SCONST:            "SCONST",
-       SDATA:             "SDATA",
-       SDYNIMPORT:        "SDYNIMPORT",
-       SELFROSECT:        "SELFROSECT",
-       SELFRXSECT:        "SELFRXSECT",
-       SELFSECT:          "SELFSECT",
-       SFILE:             "SFILE",
-       SFILEPATH:         "SFILEPATH",
-       SFUNCTAB:          "SFUNCTAB",
-       SGOFUNC:           "SGOFUNC",
-       SGOSTRING:         "SGOSTRING",
-       SHOSTOBJ:          "SHOSTOBJ",
-       SINITARR:          "SINITARR",
-       SMACHO:            "SMACHO",
-       SMACHOGOT:         "SMACHOGOT",
-       SMACHOINDIRECTGOT: "SMACHOINDIRECTGOT",
-       SMACHOINDIRECTPLT: "SMACHOINDIRECTPLT",
-       SMACHOPLT:         "SMACHOPLT",
-       SMACHOSYMSTR:      "SMACHOSYMSTR",
-       SMACHOSYMTAB:      "SMACHOSYMTAB",
-       SNOPTRBSS:         "SNOPTRBSS",
-       SNOPTRDATA:        "SNOPTRDATA",
-       SPCLNTAB:          "SPCLNTAB",
-       SRODATA:           "SRODATA",
-       SSTRING:           "SSTRING",
-       SSYMTAB:           "SSYMTAB",
-       STEXT:             "STEXT",
-       STLSBSS:           "STLSBSS",
-       STYPE:             "STYPE",
-       STYPELINK:         "STYPELINK",
-       SWINDOWS:          "SWINDOWS",
-       SXREF:             "SXREF",
-}
-
-func (k SymKind) String() string {
-       if k < 0 || int(k) >= len(symKindStrings) {
-               return fmt.Sprintf("SymKind(%d)", k)
-       }
-       return symKindStrings[k]
-}
-
-// A Sym is a named symbol in an object file.
-type Sym struct {
-       SymID         // symbol identifier (name and version)
-       Kind  SymKind // kind of symbol
-       DupOK bool    // are duplicate definitions okay?
-       Size  int     // size of corresponding data
-       Type  SymID   // symbol for Go type information
-       Data  Data    // memory image of symbol
-       Reloc []Reloc // relocations to apply to Data
-       Func  *Func   // additional data for functions
-}
-
-// A SymID - the combination of Name and Version - uniquely identifies
-// a symbol within a package.
-type SymID struct {
-       // Name is the name of a symbol.
-       Name string
-
-       // Version is zero for symbols with global visibility.
-       // Symbols with only file visibility (such as file-level static
-       // declarations in C) have a non-zero version distinguising
-       // a symbol in one file from a symbol of the same name
-       // in another file
-       Version int
-}
-
-func (s SymID) String() string {
-       if s.Version == 0 {
-               return s.Name
-       }
-       return fmt.Sprintf("%s<%d>", s.Name, s.Version)
-}
-
-// A Data is a reference to data stored in an object file.
-// It records the offset and size of the data, so that a client can
-// read the data only if necessary.
-type Data struct {
-       Offset int64
-       Size   int64
-}
-
-// A Reloc describes a relocation applied to a memory image to refer
-// to an address within a particular symbol.
-type Reloc struct {
-       // The bytes at [Offset, Offset+Size) within the memory image
-       // should be updated to refer to the address Add bytes after the start
-       // of the symbol Sym.
-       Offset int
-       Size   int
-       Sym    SymID
-       Add    int
-
-       // The Type records the form of address expected in the bytes
-       // described by the previous fields: absolute, PC-relative, and so on.
-       // TODO(rsc): The interpretation of Type is not exposed by this package.
-       Type int
-}
-
-// A Var describes a variable in a function stack frame: a declared
-// local variable, an input argument, or an output result.
-type Var struct {
-       // The combination of Name, Kind, and Offset uniquely
-       // identifies a variable in a function stack frame.
-       // Using fewer of these - in particular, using only Name - does not.
-       Name   string // Name of variable.
-       Kind   int    // TODO(rsc): Define meaning.
-       Offset int    // Frame offset. TODO(rsc): Define meaning.
-
-       Type SymID // Go type for variable.
-}
-
-// Func contains additional per-symbol information specific to functions.
-type Func struct {
-       Args     int        // size in bytes of of argument frame: inputs and outputs
-       Frame    int        // size in bytes of local variable frame
-       Var      []Var      // detail about local variables
-       PCSP     Data       // PC → SP offset map
-       PCFile   Data       // PC → file number map (index into File)
-       PCLine   Data       // PC → line number map
-       PCData   []Data     // PC → runtime support data map
-       FuncData []FuncData // non-PC-specific runtime support data
-       File     []string   // paths indexed by PCFile
-}
-
-// TODO: Add PCData []byte and PCDataIter (similar to liblink).
-
-// A FuncData is a single function-specific data value.
-type FuncData struct {
-       Sym    SymID // symbol holding data
-       Offset int64 // offset into symbol for funcdata pointer
-}
-
-// A Package is a parsed Go object file or archive defining a Go package.
-type Package struct {
-       ImportPath string   // import path denoting this package
-       Imports    []string // packages imported by this package
-       Syms       []*Sym   // symbols defined by this package
-       MaxVersion int      // maximum Version in any SymID in Syms
-}
-
-var (
-       archiveHeader = []byte("!<arch>\n")
-       archiveMagic  = []byte("`\n")
-       goobjHeader   = []byte("go objec") // truncated to size of archiveHeader
-
-       errCorruptArchive   = errors.New("corrupt archive")
-       errTruncatedArchive = errors.New("truncated archive")
-       errNotArchive       = errors.New("unrecognized archive format")
-
-       errCorruptObject   = errors.New("corrupt object file")
-       errTruncatedObject = errors.New("truncated object file")
-       errNotObject       = errors.New("unrecognized object file format")
-)
-
-// An objReader is an object file reader.
-type objReader struct {
-       p         *Package
-       b         *bufio.Reader
-       f         io.ReadSeeker
-       err       error
-       offset    int64
-       limit     int64
-       tmp       [256]byte
-       pkg       string
-       pkgprefix string
-}
-
-// importPathToPrefix returns the prefix that will be used in the
-// final symbol table for the given import path.
-// We escape '%', '"', all control characters and non-ASCII bytes,
-// and any '.' after the final slash.
-//
-// See ../../../cmd/ld/lib.c:/^pathtoprefix and
-// ../../../cmd/gc/subr.c:/^pathtoprefix.
-func importPathToPrefix(s string) string {
-       // find index of last slash, if any, or else -1.
-       // used for determining whether an index is after the last slash.
-       slash := strings.LastIndex(s, "/")
-
-       // check for chars that need escaping
-       n := 0
-       for r := 0; r < len(s); r++ {
-               if c := s[r]; c <= ' ' || (c == '.' && r > slash) || c == '%' || c == '"' || c >= 0x7F {
-                       n++
-               }
-       }
-
-       // quick exit
-       if n == 0 {
-               return s
-       }
-
-       // escape
-       const hex = "0123456789abcdef"
-       p := make([]byte, 0, len(s)+2*n)
-       for r := 0; r < len(s); r++ {
-               if c := s[r]; c <= ' ' || (c == '.' && r > slash) || c == '%' || c == '"' || c >= 0x7F {
-                       p = append(p, '%', hex[c>>4], hex[c&0xF])
-               } else {
-                       p = append(p, c)
-               }
-       }
-
-       return string(p)
-}
-
-// init initializes r to read package p from f.
-func (r *objReader) init(f io.ReadSeeker, p *Package) {
-       r.f = f
-       r.p = p
-       r.offset, _ = f.Seek(0, 1)
-       r.limit, _ = f.Seek(0, 2)
-       f.Seek(r.offset, 0)
-       r.b = bufio.NewReader(f)
-       r.pkgprefix = importPathToPrefix(p.ImportPath) + "."
-}
-
-// error records that an error occurred.
-// It returns only the first error, so that an error
-// caused by an earlier error does not discard information
-// about the earlier error.
-func (r *objReader) error(err error) error {
-       if r.err == nil {
-               if err == io.EOF {
-                       err = io.ErrUnexpectedEOF
-               }
-               r.err = err
-       }
-       // panic("corrupt") // useful for debugging
-       return r.err
-}
-
-// readByte reads and returns a byte from the input file.
-// On I/O error or EOF, it records the error but returns byte 0.
-// A sequence of 0 bytes will eventually terminate any
-// parsing state in the object file. In particular, it ends the
-// reading of a varint.
-func (r *objReader) readByte() byte {
-       if r.err != nil {
-               return 0
-       }
-       if r.offset >= r.limit {
-               r.error(io.ErrUnexpectedEOF)
-               return 0
-       }
-       b, err := r.b.ReadByte()
-       if err != nil {
-               if err == io.EOF {
-                       err = io.ErrUnexpectedEOF
-               }
-               r.error(err)
-               b = 0
-       } else {
-               r.offset++
-       }
-       return b
-}
-
-// read reads exactly len(b) bytes from the input file.
-// If an error occurs, read returns the error but also
-// records it, so it is safe for callers to ignore the result
-// as long as delaying the report is not a problem.
-func (r *objReader) readFull(b []byte) error {
-       if r.err != nil {
-               return r.err
-       }
-       if r.offset+int64(len(b)) > r.limit {
-               return r.error(io.ErrUnexpectedEOF)
-       }
-       n, err := io.ReadFull(r.b, b)
-       r.offset += int64(n)
-       if err != nil {
-               return r.error(err)
-       }
-       return nil
-}
-
-// readInt reads a zigzag varint from the input file.
-func (r *objReader) readInt() int {
-       var u uint64
-
-       for shift := uint(0); ; shift += 7 {
-               if shift >= 64 {
-                       r.error(errCorruptObject)
-                       return 0
-               }
-               c := r.readByte()
-               u |= uint64(c&0x7F) << shift
-               if c&0x80 == 0 {
-                       break
-               }
-       }
-
-       v := int64(u>>1) ^ (int64(u) << 63 >> 63)
-       if int64(int(v)) != v {
-               r.error(errCorruptObject) // TODO
-               return 0
-       }
-       return int(v)
-}
-
-// readString reads a length-delimited string from the input file.
-func (r *objReader) readString() string {
-       n := r.readInt()
-       buf := make([]byte, n)
-       r.readFull(buf)
-       return string(buf)
-}
-
-// readSymID reads a SymID from the input file.
-func (r *objReader) readSymID() SymID {
-       name, vers := r.readString(), r.readInt()
-
-       // In a symbol name in an object file, "". denotes the
-       // prefix for the package in which the object file has been found.
-       // Expand it.
-       name = strings.Replace(name, `"".`, r.pkgprefix, -1)
-
-       // An individual object file only records version 0 (extern) or 1 (static).
-       // To make static symbols unique across all files being read, we
-       // replace version 1 with the version corresponding to the current
-       // file number. The number is incremented on each call to parseObject.
-       if vers != 0 {
-               vers = r.p.MaxVersion
-       }
-
-       return SymID{name, vers}
-}
-
-// readData reads a data reference from the input file.
-func (r *objReader) readData() Data {
-       n := r.readInt()
-       d := Data{Offset: r.offset, Size: int64(n)}
-       r.skip(int64(n))
-       return d
-}
-
-// skip skips n bytes in the input.
-func (r *objReader) skip(n int64) {
-       if n < 0 {
-               r.error(fmt.Errorf("debug/goobj: internal error: misuse of skip"))
-       }
-       if n < int64(len(r.tmp)) {
-               // Since the data is so small, a just reading from the buffered
-               // reader is better than flushing the buffer and seeking.
-               r.readFull(r.tmp[:n])
-       } else if n <= int64(r.b.Buffered()) {
-               // Even though the data is not small, it has already been read.
-               // Advance the buffer instead of seeking.
-               for n > int64(len(r.tmp)) {
-                       r.readFull(r.tmp[:])
-                       n -= int64(len(r.tmp))
-               }
-               r.readFull(r.tmp[:n])
-       } else {
-               // Seek, giving up buffered data.
-               _, err := r.f.Seek(r.offset+n, 0)
-               if err != nil {
-                       r.error(err)
-               }
-               r.offset += n
-               r.b.Reset(r.f)
-       }
-}
-
-// Parse parses an object file or archive from r,
-// assuming that its import path is pkgpath.
-func Parse(r io.ReadSeeker, pkgpath string) (*Package, error) {
-       if pkgpath == "" {
-               pkgpath = `""`
-       }
-       p := new(Package)
-       p.ImportPath = pkgpath
-
-       var rd objReader
-       rd.init(r, p)
-       err := rd.readFull(rd.tmp[:8])
-       if err != nil {
-               if err == io.EOF {
-                       err = io.ErrUnexpectedEOF
-               }
-               return nil, err
-       }
-
-       switch {
-       default:
-               return nil, errNotObject
-
-       case bytes.Equal(rd.tmp[:8], archiveHeader):
-               if err := rd.parseArchive(); err != nil {
-                       return nil, err
-               }
-       case bytes.Equal(rd.tmp[:8], goobjHeader):
-               if err := rd.parseObject(goobjHeader); err != nil {
-                       return nil, err
-               }
-       }
-
-       return p, nil
-}
-
-// trimSpace removes trailing spaces from b and returns the corresponding string.
-// This effectively parses the form used in archive headers.
-func trimSpace(b []byte) string {
-       return string(bytes.TrimRight(b, " "))
-}
-
-// parseArchive parses a Unix archive of Go object files.
-// TODO(rsc): Need to skip non-Go object files.
-// TODO(rsc): Maybe record table of contents in r.p so that
-// linker can avoid having code to parse archives too.
-func (r *objReader) parseArchive() error {
-       for r.offset < r.limit {
-               if err := r.readFull(r.tmp[:60]); err != nil {
-                       return err
-               }
-               data := r.tmp[:60]
-
-               // Each file is preceded by this text header (slice indices in first column):
-               //       0:16   name
-               //      16:28 date
-               //      28:34 uid
-               //      34:40 gid
-               //      40:48 mode
-               //      48:58 size
-               //      58:60 magic - `\n
-               // We only care about name, size, and magic.
-               // The fields are space-padded on the right.
-               // The size is in decimal.
-               // The file data - size bytes - follows the header.
-               // Headers are 2-byte aligned, so if size is odd, an extra padding
-               // byte sits between the file data and the next header.
-               // The file data that follows is padded to an even number of bytes:
-               // if size is odd, an extra padding byte is inserted betw the next header.
-               if len(data) < 60 {
-                       return errTruncatedArchive
-               }
-               if !bytes.Equal(data[58:60], archiveMagic) {
-                       return errCorruptArchive
-               }
-               name := trimSpace(data[0:16])
-               size, err := strconv.ParseInt(trimSpace(data[48:58]), 10, 64)
-               if err != nil {
-                       return errCorruptArchive
-               }
-               data = data[60:]
-               fsize := size + size&1
-               if fsize < 0 || fsize < size {
-                       return errCorruptArchive
-               }
-               switch name {
-               case "__.SYMDEF", "__.GOSYMDEF", "__.PKGDEF":
-                       r.skip(size)
-               default:
-                       oldLimit := r.limit
-                       r.limit = r.offset + size
-                       if err := r.parseObject(nil); err != nil {
-                               return fmt.Errorf("parsing archive member %q: %v", name, err)
-                       }
-                       r.skip(r.limit - r.offset)
-                       r.limit = oldLimit
-               }
-               if size&1 != 0 {
-                       r.skip(1)
-               }
-       }
-       return nil
-}
-
-// parseObject parses a single Go object file.
-// The prefix is the bytes already read from the file,
-// typically in order to detect that this is an object file.
-// The object file consists of a textual header ending in "\n!\n"
-// and then the part we want to parse begins.
-// The format of that part is defined in a comment at the top
-// of src/liblink/objfile.c.
-func (r *objReader) parseObject(prefix []byte) error {
-       // TODO(rsc): Maybe use prefix and the initial input to
-       // record the header line from the file, which would
-       // give the architecture and other version information.
-
-       r.p.MaxVersion++
-       var c1, c2, c3 byte
-       for {
-               c1, c2, c3 = c2, c3, r.readByte()
-               if c3 == 0 { // NUL or EOF, either is bad
-                       return errCorruptObject
-               }
-               if c1 == '\n' && c2 == '!' && c3 == '\n' {
-                       break
-               }
-       }
-
-       r.readFull(r.tmp[:8])
-       if !bytes.Equal(r.tmp[:8], []byte("\x00\x00go13ld")) {
-               return r.error(errCorruptObject)
-       }
-
-       // Direct package dependencies.
-       for {
-               s := r.readString()
-               if s == "" {
-                       break
-               }
-               r.p.Imports = append(r.p.Imports, s)
-       }
-
-       // Symbols.
-       for {
-               if b := r.readByte(); b != 0xfe {
-                       if b != 0xff {
-                               return r.error(errCorruptObject)
-                       }
-                       break
-               }
-
-               typ := r.readInt()
-               s := &Sym{SymID: r.readSymID()}
-               r.p.Syms = append(r.p.Syms, s)
-               s.Kind = SymKind(typ)
-               s.DupOK = r.readInt() != 0
-               s.Size = r.readInt()
-               s.Type = r.readSymID()
-               s.Data = r.readData()
-               s.Reloc = make([]Reloc, r.readInt())
-               for i := range s.Reloc {
-                       rel := &s.Reloc[i]
-                       rel.Offset = r.readInt()
-                       rel.Size = r.readInt()
-                       rel.Type = r.readInt()
-                       rel.Add = r.readInt()
-                       r.readInt() // Xadd - ignored
-                       rel.Sym = r.readSymID()
-                       r.readSymID() // Xsym - ignored
-               }
-
-               if s.Kind == STEXT {
-                       f := new(Func)
-                       s.Func = f
-                       f.Args = r.readInt()
-                       f.Frame = r.readInt()
-                       f.Var = make([]Var, r.readInt())
-                       for i := range f.Var {
-                               v := &f.Var[i]
-                               v.Name = r.readSymID().Name
-                               v.Offset = r.readInt()
-                               v.Kind = r.readInt()
-                               v.Type = r.readSymID()
-                       }
-
-                       f.PCSP = r.readData()
-                       f.PCFile = r.readData()
-                       f.PCLine = r.readData()
-                       f.PCData = make([]Data, r.readInt())
-                       for i := range f.PCData {
-                               f.PCData[i] = r.readData()
-                       }
-                       f.FuncData = make([]FuncData, r.readInt())
-                       for i := range f.FuncData {
-                               f.FuncData[i].Sym = r.readSymID()
-                       }
-                       for i := range f.FuncData {
-                               f.FuncData[i].Offset = int64(r.readInt()) // TODO
-                       }
-                       f.File = make([]string, r.readInt())
-                       for i := range f.File {
-                               f.File[i] = r.readSymID().Name
-                       }
-               }
-       }
-
-       r.readFull(r.tmp[:7])
-       if !bytes.Equal(r.tmp[:7], []byte("\xffgo13ld")) {
-               return r.error(errCorruptObject)
-       }
-
-       return nil
-}
diff --git a/libgo/go/debug/goobj/read_test.go b/libgo/go/debug/goobj/read_test.go
deleted file mode 100644 (file)
index dee1405..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2013 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 goobj
-
-import "testing"
-
-var importPathToPrefixTests = []struct {
-       in  string
-       out string
-}{
-       {"runtime", "runtime"},
-       {"sync/atomic", "sync/atomic"},
-       {"code.google.com/p/go.tools/godoc", "code.google.com/p/go.tools/godoc"},
-       {"foo.bar/baz.quux", "foo.bar/baz%2equux"},
-       {"", ""},
-       {"%foo%bar", "%25foo%25bar"},
-       {"\x01\x00\x7F☺", "%01%00%7f%e2%98%ba"},
-}
-
-func TestImportPathToPrefix(t *testing.T) {
-       for _, tt := range importPathToPrefixTests {
-               if out := importPathToPrefix(tt.in); out != tt.out {
-                       t.Errorf("importPathToPrefix(%q) = %q, want %q", tt.in, out, tt.out)
-               }
-       }
-}
index ed9b2a6912017d0842036ced34fbc66a327e283c..da13c510064fc8c2c58285f75c2aa00a5381da98 100644 (file)
@@ -11,7 +11,6 @@ import (
        "bytes"
        "debug/dwarf"
        "encoding/binary"
-       "errors"
        "fmt"
        "io"
        "os"
@@ -481,7 +480,7 @@ func (f *File) DWARF() (*dwarf.Data, error) {
                name = "__debug_" + name
                s := f.Section(name)
                if s == nil {
-                       return nil, errors.New("missing Mach-O section " + name)
+                       continue
                }
                b, err := s.Data()
                if err != nil && uint64(len(b)) < s.Size {
index 0de9184c22746a22d4e1eb05f8b08b8ec7041b8b..4797780ce77918173d954c1c57ad4f8f59ba2f52 100644 (file)
@@ -184,7 +184,7 @@ func TestOpenFat(t *testing.T) {
                ftArch := &fileTests[i]
 
                if arch.Cpu != ftArch.hdr.Cpu || arch.SubCpu != ftArch.hdr.SubCpu {
-                       t.Error("OpenFat: architecture #%d got cpu=%#x subtype=%#x, expected cpu=%#x, subtype=%#x", i, arch.Cpu, arch.SubCpu, ftArch.hdr.Cpu, ftArch.hdr.SubCpu)
+                       t.Errorf("OpenFat: architecture #%d got cpu=%#x subtype=%#x, expected cpu=%#x, subtype=%#x", i, arch.Cpu, arch.SubCpu, ftArch.hdr.Cpu, ftArch.hdr.SubCpu)
                }
 
                if !reflect.DeepEqual(arch.FileHeader, ftArch.hdr) {
@@ -202,9 +202,9 @@ func TestOpenFatFailure(t *testing.T) {
        filename = "testdata/gcc-386-darwin-exec" // not a fat Mach-O
        ff, err := OpenFat(filename)
        if err != ErrNotFat {
-               t.Errorf("OpenFat %s: got %v, want ErrNotFat", err)
+               t.Errorf("OpenFat %s: got %v, want ErrNotFat", filename, err)
        }
        if ff != nil {
-               t.Errorf("OpenFat %s: got %v, want nil", ff)
+               t.Errorf("OpenFat %s: got %v, want nil", filename, ff)
        }
 }
index 09f4d0ec91c63b7fcf2c8931929aa8aa75847aff..d9678c8eda40887342c1aaa48043b36da597a45b 100644 (file)
@@ -44,14 +44,22 @@ const (
 // A Cpu is a Mach-O cpu type.
 type Cpu uint32
 
+const cpuArch64 = 0x01000000
+
 const (
        Cpu386   Cpu = 7
-       CpuAmd64 Cpu = Cpu386 + 1<<24
+       CpuAmd64 Cpu = Cpu386 | cpuArch64
+       CpuArm   Cpu = 12
+       CpuPpc   Cpu = 18
+       CpuPpc64 Cpu = CpuPpc | cpuArch64
 )
 
 var cpuStrings = []intName{
        {uint32(Cpu386), "Cpu386"},
        {uint32(CpuAmd64), "CpuAmd64"},
+       {uint32(CpuArm), "CpuArm"},
+       {uint32(CpuPpc), "CpuPpc"},
+       {uint32(CpuPpc64), "CpuPpc64"},
 }
 
 func (i Cpu) String() string   { return stringName(uint32(i), cpuStrings, false) }
index d0005bacf383ee9e6ff4107092ba07e3827eb184..ce6f1408fe98f43e1c2afe516482b9c4dc098928 100644 (file)
@@ -13,13 +13,15 @@ import (
        "io"
        "os"
        "strconv"
+       "unsafe"
 )
 
 // A File represents an open PE file.
 type File struct {
        FileHeader
-       Sections []*Section
-       Symbols  []*Symbol
+       OptionalHeader interface{} // of type *OptionalHeader32 or *OptionalHeader64
+       Sections       []*Section
+       Symbols        []*Symbol
 
        closer io.Closer
 }
@@ -196,10 +198,33 @@ func NewFile(r io.ReaderAt) (*File, error) {
                }
        }
 
-       // Process sections.
+       // Read optional header.
        sr.Seek(base, os.SEEK_SET)
-       binary.Read(sr, binary.LittleEndian, &f.FileHeader)
-       sr.Seek(int64(f.FileHeader.SizeOfOptionalHeader), os.SEEK_CUR) //Skip OptionalHeader
+       if err := binary.Read(sr, binary.LittleEndian, &f.FileHeader); err != nil {
+               return nil, err
+       }
+       var oh32 OptionalHeader32
+       var oh64 OptionalHeader64
+       switch uintptr(f.FileHeader.SizeOfOptionalHeader) {
+       case unsafe.Sizeof(oh32):
+               if err := binary.Read(sr, binary.LittleEndian, &oh32); err != nil {
+                       return nil, err
+               }
+               if oh32.Magic != 0x10b { // PE32
+                       return nil, fmt.Errorf("pe32 optional header has unexpected Magic of 0x%x", oh32.Magic)
+               }
+               f.OptionalHeader = &oh32
+       case unsafe.Sizeof(oh64):
+               if err := binary.Read(sr, binary.LittleEndian, &oh64); err != nil {
+                       return nil, err
+               }
+               if oh64.Magic != 0x20b { // PE32+
+                       return nil, fmt.Errorf("pe32+ optional header has unexpected Magic of 0x%x", oh64.Magic)
+               }
+               f.OptionalHeader = &oh64
+       }
+
+       // Process sections.
        f.Sections = make([]*Section, f.FileHeader.NumberOfSections)
        for i := 0; i < int(f.FileHeader.NumberOfSections); i++ {
                sh := new(SectionHeader32)
index c0f9fcb95db770e999f5a24186d6cde42e4ef73b..ddbb271744146c8b052740e9fc251f1acfc29dd9 100644 (file)
@@ -12,6 +12,7 @@ import (
 type fileTest struct {
        file     string
        hdr      FileHeader
+       opthdr   interface{}
        sections []*SectionHeader
        symbols  []*Symbol
 }
@@ -20,6 +21,7 @@ var fileTests = []fileTest{
        {
                "testdata/gcc-386-mingw-obj",
                FileHeader{0x014c, 0x000c, 0x0, 0x64a, 0x1e, 0x0, 0x104},
+               nil,
                []*SectionHeader{
                        {".text", 0, 0, 36, 500, 1440, 0, 3, 0, 0x60300020},
                        {".data", 0, 0, 0, 0, 0, 0, 0, 0, 3224371264},
@@ -56,27 +58,130 @@ var fileTests = []fileTest{
        {
                "testdata/gcc-386-mingw-exec",
                FileHeader{0x014c, 0x000f, 0x4c6a1b60, 0x3c00, 0x282, 0xe0, 0x107},
+               &OptionalHeader32{
+                       0x10b, 0x2, 0x38, 0xe00, 0x1a00, 0x200, 0x1160, 0x1000, 0x2000, 0x400000, 0x1000, 0x200, 0x4, 0x0, 0x1, 0x0, 0x4, 0x0, 0x0, 0x10000, 0x400, 0x14abb, 0x3, 0x0, 0x200000, 0x1000, 0x100000, 0x1000, 0x0, 0x10,
+                       [16]DataDirectory{
+                               {0x0, 0x0},
+                               {0x5000, 0x3c8},
+                               {0x0, 0x0},
+                               {0x0, 0x0},
+                               {0x0, 0x0},
+                               {0x0, 0x0},
+                               {0x0, 0x0},
+                               {0x0, 0x0},
+                               {0x0, 0x0},
+                               {0x7000, 0x18},
+                               {0x0, 0x0},
+                               {0x0, 0x0},
+                               {0x0, 0x0},
+                               {0x0, 0x0},
+                               {0x0, 0x0},
+                               {0x0, 0x0},
+                       },
+               },
+               []*SectionHeader{
+                       {".text", 0xcd8, 0x1000, 0xe00, 0x400, 0x0, 0x0, 0x0, 0x0, 0x60500060},
+                       {".data", 0x10, 0x2000, 0x200, 0x1200, 0x0, 0x0, 0x0, 0x0, 0xc0300040},
+                       {".rdata", 0x120, 0x3000, 0x200, 0x1400, 0x0, 0x0, 0x0, 0x0, 0x40300040},
+                       {".bss", 0xdc, 0x4000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0400080},
+                       {".idata", 0x3c8, 0x5000, 0x400, 0x1600, 0x0, 0x0, 0x0, 0x0, 0xc0300040},
+                       {".CRT", 0x18, 0x6000, 0x200, 0x1a00, 0x0, 0x0, 0x0, 0x0, 0xc0300040},
+                       {".tls", 0x20, 0x7000, 0x200, 0x1c00, 0x0, 0x0, 0x0, 0x0, 0xc0300040},
+                       {".debug_aranges", 0x20, 0x8000, 0x200, 0x1e00, 0x0, 0x0, 0x0, 0x0, 0x42100000},
+                       {".debug_pubnames", 0x51, 0x9000, 0x200, 0x2000, 0x0, 0x0, 0x0, 0x0, 0x42100000},
+                       {".debug_pubtypes", 0x91, 0xa000, 0x200, 0x2200, 0x0, 0x0, 0x0, 0x0, 0x42100000},
+                       {".debug_info", 0xe22, 0xb000, 0x1000, 0x2400, 0x0, 0x0, 0x0, 0x0, 0x42100000},
+                       {".debug_abbrev", 0x157, 0xc000, 0x200, 0x3400, 0x0, 0x0, 0x0, 0x0, 0x42100000},
+                       {".debug_line", 0x144, 0xd000, 0x200, 0x3600, 0x0, 0x0, 0x0, 0x0, 0x42100000},
+                       {".debug_frame", 0x34, 0xe000, 0x200, 0x3800, 0x0, 0x0, 0x0, 0x0, 0x42300000},
+                       {".debug_loc", 0x38, 0xf000, 0x200, 0x3a00, 0x0, 0x0, 0x0, 0x0, 0x42100000},
+               },
+               []*Symbol{},
+       },
+       {
+               "testdata/gcc-amd64-mingw-obj",
+               FileHeader{0x8664, 0x6, 0x0, 0x198, 0x12, 0x0, 0x4},
+               nil,
+               []*SectionHeader{
+                       {".text", 0x0, 0x0, 0x30, 0x104, 0x15c, 0x0, 0x3, 0x0, 0x60500020},
+                       {".data", 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0500040},
+                       {".bss", 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0500080},
+                       {".rdata", 0x0, 0x0, 0x10, 0x134, 0x0, 0x0, 0x0, 0x0, 0x40500040},
+                       {".xdata", 0x0, 0x0, 0xc, 0x144, 0x0, 0x0, 0x0, 0x0, 0x40300040},
+                       {".pdata", 0x0, 0x0, 0xc, 0x150, 0x17a, 0x0, 0x3, 0x0, 0x40300040},
+               },
+               []*Symbol{
+                       {".file", 0x0, -2, 0x0, 0x67},
+                       {"main", 0x0, 1, 0x20, 0x2},
+                       {".text", 0x0, 1, 0x0, 0x3},
+                       {".data", 0x0, 2, 0x0, 0x3},
+                       {".bss", 0x0, 3, 0x0, 0x3},
+                       {".rdata", 0x0, 4, 0x0, 0x3},
+                       {".xdata", 0x0, 5, 0x0, 0x3},
+                       {".pdata", 0x0, 6, 0x0, 0x3},
+                       {"__main", 0x0, 0, 0x20, 0x2},
+                       {"puts", 0x0, 0, 0x20, 0x2},
+               },
+       },
+       {
+               "testdata/gcc-amd64-mingw-exec",
+               FileHeader{0x8664, 0x9, 0x53472993, 0x0, 0x0, 0xf0, 0x22f},
+               &OptionalHeader64{
+                       0x20b, 0x2, 0x16, 0x6a00, 0x2400, 0x1600, 0x14e0, 0x1000, 0x400000, 0x1000, 0x200, 0x4, 0x0, 0x0, 0x0, 0x5, 0x2, 0x0, 0x11000, 0x400, 0x1841e, 0x3, 0x0, 0x200000, 0x1000, 0x100000, 0x1000, 0x0, 0x10,
+                       [16]DataDirectory{
+                               {0x0, 0x0},
+                               {0xe000, 0x990},
+                               {0x0, 0x0},
+                               {0xa000, 0x498},
+                               {0x0, 0x0},
+                               {0x0, 0x0},
+                               {0x0, 0x0},
+                               {0x0, 0x0},
+                               {0x0, 0x0},
+                               {0x10000, 0x28},
+                               {0x0, 0x0},
+                               {0x0, 0x0},
+                               {0xe254, 0x218},
+                               {0x0, 0x0},
+                               {0x0, 0x0},
+                               {0x0, 0x0},
+                       },
+               },
                []*SectionHeader{
-                       {Name: ".text", VirtualSize: 0xcd8, VirtualAddress: 0x1000, Size: 0xe00, Offset: 0x400, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x60500060},
-                       {Name: ".data", VirtualSize: 0x10, VirtualAddress: 0x2000, Size: 0x200, Offset: 0x1200, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0xc0300040},
-                       {Name: ".rdata", VirtualSize: 0x120, VirtualAddress: 0x3000, Size: 0x200, Offset: 0x1400, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x40300040},
-                       {Name: ".bss", VirtualSize: 0xdc, VirtualAddress: 0x4000, Size: 0x0, Offset: 0x0, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0xc0400080},
-                       {Name: ".idata", VirtualSize: 0x3c8, VirtualAddress: 0x5000, Size: 0x400, Offset: 0x1600, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0xc0300040},
-                       {Name: ".CRT", VirtualSize: 0x18, VirtualAddress: 0x6000, Size: 0x200, Offset: 0x1a00, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0xc0300040},
-                       {Name: ".tls", VirtualSize: 0x20, VirtualAddress: 0x7000, Size: 0x200, Offset: 0x1c00, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0xc0300040},
-                       {Name: ".debug_aranges", VirtualSize: 0x20, VirtualAddress: 0x8000, Size: 0x200, Offset: 0x1e00, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42100000},
-                       {Name: ".debug_pubnames", VirtualSize: 0x51, VirtualAddress: 0x9000, Size: 0x200, Offset: 0x2000, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42100000},
-                       {Name: ".debug_pubtypes", VirtualSize: 0x91, VirtualAddress: 0xa000, Size: 0x200, Offset: 0x2200, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42100000},
-                       {Name: ".debug_info", VirtualSize: 0xe22, VirtualAddress: 0xb000, Size: 0x1000, Offset: 0x2400, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42100000},
-                       {Name: ".debug_abbrev", VirtualSize: 0x157, VirtualAddress: 0xc000, Size: 0x200, Offset: 0x3400, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42100000},
-                       {Name: ".debug_line", VirtualSize: 0x144, VirtualAddress: 0xd000, Size: 0x200, Offset: 0x3600, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42100000},
-                       {Name: ".debug_frame", VirtualSize: 0x34, VirtualAddress: 0xe000, Size: 0x200, Offset: 0x3800, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42300000},
-                       {Name: ".debug_loc", VirtualSize: 0x38, VirtualAddress: 0xf000, Size: 0x200, Offset: 0x3a00, PointerToRelocations: 0x0, PointerToLineNumbers: 0x0, NumberOfRelocations: 0x0, NumberOfLineNumbers: 0x0, Characteristics: 0x42100000},
+                       {".text", 0x6860, 0x1000, 0x6a00, 0x400, 0x0, 0x0, 0x0, 0x0, 0x60500020},
+                       {".data", 0xe0, 0x8000, 0x200, 0x6e00, 0x0, 0x0, 0x0, 0x0, 0xc0500040},
+                       {".rdata", 0x6b0, 0x9000, 0x800, 0x7000, 0x0, 0x0, 0x0, 0x0, 0x40600040},
+                       {".pdata", 0x498, 0xa000, 0x600, 0x7800, 0x0, 0x0, 0x0, 0x0, 0x40300040},
+                       {".xdata", 0x488, 0xb000, 0x600, 0x7e00, 0x0, 0x0, 0x0, 0x0, 0x40300040},
+                       {".bss", 0x1410, 0xc000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc0600080},
+                       {".idata", 0x990, 0xe000, 0xa00, 0x8400, 0x0, 0x0, 0x0, 0x0, 0xc0300040},
+                       {".CRT", 0x68, 0xf000, 0x200, 0x8e00, 0x0, 0x0, 0x0, 0x0, 0xc0400040},
+                       {".tls", 0x48, 0x10000, 0x200, 0x9000, 0x0, 0x0, 0x0, 0x0, 0xc0600040},
                },
                []*Symbol{},
        },
 }
 
+func isOptHdrEq(a, b interface{}) bool {
+       switch va := a.(type) {
+       case *OptionalHeader32:
+               vb, ok := b.(*OptionalHeader32)
+               if !ok {
+                       return false
+               }
+               return *vb == *va
+       case *OptionalHeader64:
+               vb, ok := b.(*OptionalHeader64)
+               if !ok {
+                       return false
+               }
+               return *vb == *va
+       case nil:
+               return b == nil
+       }
+       return false
+}
+
 func TestOpen(t *testing.T) {
        for i := range fileTests {
                tt := &fileTests[i]
@@ -90,6 +195,10 @@ func TestOpen(t *testing.T) {
                        t.Errorf("open %s:\n\thave %#v\n\twant %#v\n", tt.file, f.FileHeader, tt.hdr)
                        continue
                }
+               if !isOptHdrEq(tt.opthdr, f.OptionalHeader) {
+                       t.Errorf("open %s:\n\thave %#v\n\twant %#v\n", tt.file, f.OptionalHeader, tt.opthdr)
+                       continue
+               }
 
                for i, sh := range f.Sections {
                        if i >= len(tt.sections) {
index 0606217b3bfdf3f9c641dfd57acebd5ae09e9c63..8e90b1b513acb1b687eae99099fbdcb041e9b442 100644 (file)
@@ -14,6 +14,78 @@ type FileHeader struct {
        Characteristics      uint16
 }
 
+type DataDirectory struct {
+       VirtualAddress uint32
+       Size           uint32
+}
+
+type OptionalHeader32 struct {
+       Magic                       uint16
+       MajorLinkerVersion          uint8
+       MinorLinkerVersion          uint8
+       SizeOfCode                  uint32
+       SizeOfInitializedData       uint32
+       SizeOfUninitializedData     uint32
+       AddressOfEntryPoint         uint32
+       BaseOfCode                  uint32
+       BaseOfData                  uint32
+       ImageBase                   uint32
+       SectionAlignment            uint32
+       FileAlignment               uint32
+       MajorOperatingSystemVersion uint16
+       MinorOperatingSystemVersion uint16
+       MajorImageVersion           uint16
+       MinorImageVersion           uint16
+       MajorSubsystemVersion       uint16
+       MinorSubsystemVersion       uint16
+       Win32VersionValue           uint32
+       SizeOfImage                 uint32
+       SizeOfHeaders               uint32
+       CheckSum                    uint32
+       Subsystem                   uint16
+       DllCharacteristics          uint16
+       SizeOfStackReserve          uint32
+       SizeOfStackCommit           uint32
+       SizeOfHeapReserve           uint32
+       SizeOfHeapCommit            uint32
+       LoaderFlags                 uint32
+       NumberOfRvaAndSizes         uint32
+       DataDirectory               [16]DataDirectory
+}
+
+type OptionalHeader64 struct {
+       Magic                       uint16
+       MajorLinkerVersion          uint8
+       MinorLinkerVersion          uint8
+       SizeOfCode                  uint32
+       SizeOfInitializedData       uint32
+       SizeOfUninitializedData     uint32
+       AddressOfEntryPoint         uint32
+       BaseOfCode                  uint32
+       ImageBase                   uint64
+       SectionAlignment            uint32
+       FileAlignment               uint32
+       MajorOperatingSystemVersion uint16
+       MinorOperatingSystemVersion uint16
+       MajorImageVersion           uint16
+       MinorImageVersion           uint16
+       MajorSubsystemVersion       uint16
+       MinorSubsystemVersion       uint16
+       Win32VersionValue           uint32
+       SizeOfImage                 uint32
+       SizeOfHeaders               uint32
+       CheckSum                    uint32
+       Subsystem                   uint16
+       DllCharacteristics          uint16
+       SizeOfStackReserve          uint64
+       SizeOfStackCommit           uint64
+       SizeOfHeapReserve           uint64
+       SizeOfHeapCommit            uint64
+       LoaderFlags                 uint32
+       NumberOfRvaAndSizes         uint32
+       DataDirectory               [16]DataDirectory
+}
+
 type SectionHeader32 struct {
        Name                 [8]uint8
        VirtualSize          uint32
diff --git a/libgo/go/debug/pe/testdata/gcc-amd64-mingw-exec b/libgo/go/debug/pe/testdata/gcc-amd64-mingw-exec
new file mode 100644 (file)
index 0000000..78d4e5f
Binary files /dev/null and b/libgo/go/debug/pe/testdata/gcc-amd64-mingw-exec differ
diff --git a/libgo/go/debug/pe/testdata/gcc-amd64-mingw-obj b/libgo/go/debug/pe/testdata/gcc-amd64-mingw-obj
new file mode 100644 (file)
index 0000000..48ae792
Binary files /dev/null and b/libgo/go/debug/pe/testdata/gcc-amd64-mingw-obj differ
index a4c95a92a5c032f29cb9a9198a4b32a884fdae08..60a5857193e9f2770124e18811b8834e497584e1 100644 (file)
@@ -13,9 +13,12 @@ import (
        "os"
 )
 
-// A FileHeader represents an Plan 9 a.out file header.
+// A FileHeader represents a Plan 9 a.out file header.
 type FileHeader struct {
-       Ptrsz int
+       Magic   uint32
+       Bss     uint32
+       Entry   uint64
+       PtrSize int
 }
 
 // A File represents an open Plan 9 a.out file.
@@ -25,13 +28,16 @@ type File struct {
        closer   io.Closer
 }
 
+// A SectionHeader represents a single Plan 9 a.out section header.
+// This structure doesn't exist on-disk, but eases navigation
+// through the object file.
 type SectionHeader struct {
        Name   string
        Size   uint32
        Offset uint32
 }
 
-// A Section represents a single section in an Plan 9 a.out file.
+// A Section represents a single section in a Plan 9 a.out file.
 type Section struct {
        SectionHeader
 
@@ -49,41 +55,15 @@ type Section struct {
 func (s *Section) Data() ([]byte, error) {
        dat := make([]byte, s.sr.Size())
        n, err := s.sr.ReadAt(dat, 0)
+       if n == len(dat) {
+               err = nil
+       }
        return dat[0:n], err
 }
 
 // Open returns a new ReadSeeker reading the Plan 9 a.out section.
 func (s *Section) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) }
 
-// A ProgHeader represents a single Plan 9 a.out program header.
-type ProgHeader struct {
-       Magic uint32
-       Text  uint32
-       Data  uint32
-       Bss   uint32
-       Syms  uint32
-       Entry uint64
-       Spsz  uint32
-       Pcsz  uint32
-}
-
-// A Prog represents the program header in an Plan 9 a.out binary.
-type Prog struct {
-       ProgHeader
-
-       // Embed ReaderAt for ReadAt method.
-       // Do not embed SectionReader directly
-       // to avoid having Read and Seek.
-       // If a client wants Read and Seek it must use
-       // Open() to avoid fighting over the seek offset
-       // with other clients.
-       io.ReaderAt
-       sr *io.SectionReader
-}
-
-// Open returns a new ReadSeeker reading the Plan 9 a.out program body.
-func (p *Prog) Open() io.ReadSeeker { return io.NewSectionReader(p.sr, 0, 1<<63-1) }
-
 // A Symbol represents an entry in a Plan 9 a.out symbol table section.
 type Sym struct {
        Value uint64
@@ -95,13 +75,15 @@ type Sym struct {
  * Plan 9 a.out reader
  */
 
-type FormatError struct {
+// formatError is returned by some operations if the data does
+// not have the correct format for an object file.
+type formatError struct {
        off int
        msg string
        val interface{}
 }
 
-func (e *FormatError) Error() string {
+func (e *formatError) Error() string {
        msg := e.msg
        if e.val != nil {
                msg += fmt.Sprintf(" '%v'", e.val)
@@ -110,7 +92,7 @@ func (e *FormatError) Error() string {
        return msg
 }
 
-// Open opens the named file using os.Open and prepares it for use as an Plan 9 a.out binary.
+// Open opens the named file using os.Open and prepares it for use as a Plan 9 a.out binary.
 func Open(name string) (*File, error) {
        f, err := os.Open(name)
        if err != nil {
@@ -137,16 +119,16 @@ func (f *File) Close() error {
        return err
 }
 
-func parseMagic(magic [4]byte) (*ExecTable, error) {
-       for _, e := range exectab {
-               if string(magic[:]) == e.Magic {
-                       return &e, nil
-               }
+func parseMagic(magic []byte) (uint32, error) {
+       m := binary.BigEndian.Uint32(magic)
+       switch m {
+       case Magic386, MagicAMD64, MagicARM:
+               return m, nil
        }
-       return nil, &FormatError{0, "bad magic number", magic[:]}
+       return 0, &formatError{0, "bad magic number", magic}
 }
 
-// NewFile creates a new File for accessing an Plan 9 binary in an underlying reader.
+// NewFile creates a new File for accessing a Plan 9 binary in an underlying reader.
 // The Plan 9 binary is expected to start at position 0 in the ReaderAt.
 func NewFile(r io.ReaderAt) (*File, error) {
        sr := io.NewSectionReader(r, 0, 1<<63-1)
@@ -155,34 +137,31 @@ func NewFile(r io.ReaderAt) (*File, error) {
        if _, err := r.ReadAt(magic[:], 0); err != nil {
                return nil, err
        }
-       mp, err := parseMagic(magic)
+       _, err := parseMagic(magic[:])
        if err != nil {
                return nil, err
        }
 
-       f := &File{FileHeader{mp.Ptrsz}, nil, nil}
-
        ph := new(prog)
        if err := binary.Read(sr, binary.BigEndian, ph); err != nil {
                return nil, err
        }
 
-       p := new(Prog)
-       p.ProgHeader = ProgHeader{
-               Magic: ph.Magic,
-               Text:  ph.Text,
-               Data:  ph.Data,
-               Bss:   ph.Bss,
-               Syms:  ph.Syms,
-               Entry: uint64(ph.Entry),
-               Spsz:  ph.Spsz,
-               Pcsz:  ph.Pcsz,
-       }
+       f := &File{FileHeader: FileHeader{
+               Magic:   ph.Magic,
+               Bss:     ph.Bss,
+               Entry:   uint64(ph.Entry),
+               PtrSize: 4,
+       }}
+
+       hdrSize := 4 * 8
 
-       if mp.Ptrsz == 8 {
-               if err := binary.Read(sr, binary.BigEndian, &p.Entry); err != nil {
+       if ph.Magic&Magic64 != 0 {
+               if err := binary.Read(sr, binary.BigEndian, &f.Entry); err != nil {
                        return nil, err
                }
+               f.PtrSize = 8
+               hdrSize += 8
        }
 
        var sects = []struct {
@@ -198,7 +177,7 @@ func NewFile(r io.ReaderAt) (*File, error) {
 
        f.Sections = make([]*Section, 5)
 
-       off := mp.Hsize
+       off := uint32(hdrSize)
 
        for i, sect := range sects {
                s := new(Section)
@@ -208,7 +187,7 @@ func NewFile(r io.ReaderAt) (*File, error) {
                        Offset: off,
                }
                off += sect.size
-               s.sr = io.NewSectionReader(r, int64(s.SectionHeader.Offset), int64(s.SectionHeader.Size))
+               s.sr = io.NewSectionReader(r, int64(s.Offset), int64(s.Size))
                s.ReaderAt = s.sr
                f.Sections[i] = s
        }
@@ -223,7 +202,7 @@ func walksymtab(data []byte, ptrsz int, fn func(sym) error) error {
        for len(p) >= 4 {
                // Symbol type, value.
                if len(p) < ptrsz {
-                       return &FormatError{len(data), "unexpected EOF", nil}
+                       return &formatError{len(data), "unexpected EOF", nil}
                }
                // fixed-width value
                if ptrsz == 8 {
@@ -259,7 +238,7 @@ func walksymtab(data []byte, ptrsz int, fn func(sym) error) error {
                        }
                }
                if len(p) < i+nnul {
-                       return &FormatError{len(data), "unexpected EOF", nil}
+                       return &formatError{len(data), "unexpected EOF", nil}
                }
                s.name = p[0:i]
                i += nnul
@@ -298,7 +277,7 @@ func newTable(symtab []byte, ptrsz int) ([]Sym, error) {
                                eltIdx := binary.BigEndian.Uint16(s.name[i : i+2])
                                elt, ok := fname[eltIdx]
                                if !ok {
-                                       return &FormatError{-1, "bad filename code", eltIdx}
+                                       return &formatError{-1, "bad filename code", eltIdx}
                                }
                                if n := len(ts.Name); n > 0 && ts.Name[n-1] != '/' {
                                        ts.Name += "/"
@@ -331,7 +310,7 @@ func (f *File) Symbols() ([]Sym, error) {
                return nil, errors.New("cannot load symbol section")
        }
 
-       return newTable(symtab, f.Ptrsz)
+       return newTable(symtab, f.PtrSize)
 }
 
 // Section returns a section with the given name, or nil if no such
index cc1db409295a96ebb9080c913c735f34ca194ef6..96186d815651872c84d1c63f3bf863b9751ce46d 100644 (file)
@@ -18,7 +18,7 @@ type fileTest struct {
 var fileTests = []fileTest{
        {
                "testdata/386-plan9-exec",
-               FileHeader{4},
+               FileHeader{Magic386, 0x324, 0x14, 4},
                []*SectionHeader{
                        {"text", 0x4c5f, 0x20},
                        {"data", 0x94c, 0x4c7f},
@@ -29,7 +29,7 @@ var fileTests = []fileTest{
        },
        {
                "testdata/amd64-plan9-exec",
-               FileHeader{8},
+               FileHeader{MagicAMD64, 0x618, 0x13, 8},
                []*SectionHeader{
                        {"text", 0x4213, 0x28},
                        {"data", 0xa80, 0x423b},
index 4e3b08f4164fd7056becf0301b7b68ab6f810d6d..af9858562f9e2b6d4ad51e9f3e0497bea2ff5aa7 100644 (file)
@@ -8,11 +8,6 @@
 
 package plan9obj
 
-import (
-       "bytes"
-       "encoding/binary"
-)
-
 // Plan 9 Program header.
 type prog struct {
        Magic uint32 /* magic number */
@@ -33,59 +28,9 @@ type sym struct {
 }
 
 const (
-       hsize      = 4 * 8
-       _HDR_MAGIC = 0x00008000 /* header expansion */
-)
-
-func magic(f, b int) string {
-       buf := new(bytes.Buffer)
-       var i uint32 = uint32((f) | ((((4 * (b)) + 0) * (b)) + 7))
-       binary.Write(buf, binary.BigEndian, i)
-       return string(buf.Bytes())
-}
+       Magic64 = 0x8000 // 64-bit expanded header
 
-var (
-       _A_MAGIC = magic(0, 8)           /* 68020 (retired) */
-       _I_MAGIC = magic(0, 11)          /* intel 386 */
-       _J_MAGIC = magic(0, 12)          /* intel 960 (retired) */
-       _K_MAGIC = magic(0, 13)          /* sparc */
-       _V_MAGIC = magic(0, 16)          /* mips 3000 BE */
-       _X_MAGIC = magic(0, 17)          /* att dsp 3210 (retired) */
-       _M_MAGIC = magic(0, 18)          /* mips 4000 BE */
-       _D_MAGIC = magic(0, 19)          /* amd 29000 (retired) */
-       _E_MAGIC = magic(0, 20)          /* arm */
-       _Q_MAGIC = magic(0, 21)          /* powerpc */
-       _N_MAGIC = magic(0, 22)          /* mips 4000 LE */
-       _L_MAGIC = magic(0, 23)          /* dec alpha (retired) */
-       _P_MAGIC = magic(0, 24)          /* mips 3000 LE */
-       _U_MAGIC = magic(0, 25)          /* sparc64 (retired) */
-       _S_MAGIC = magic(_HDR_MAGIC, 26) /* amd64 */
-       _T_MAGIC = magic(_HDR_MAGIC, 27) /* powerpc64 */
-       _R_MAGIC = magic(_HDR_MAGIC, 28) /* arm64 */
+       Magic386   = (4*11+0)*11 + 7
+       MagicAMD64 = (4*26+0)*26 + 7 + Magic64
+       MagicARM   = (4*20+0)*20 + 7
 )
-
-type ExecTable struct {
-       Magic string
-       Ptrsz int
-       Hsize uint32
-}
-
-var exectab = []ExecTable{
-       {_A_MAGIC, 4, hsize},
-       {_I_MAGIC, 4, hsize},
-       {_J_MAGIC, 4, hsize},
-       {_K_MAGIC, 4, hsize},
-       {_V_MAGIC, 4, hsize},
-       {_X_MAGIC, 4, hsize},
-       {_M_MAGIC, 4, hsize},
-       {_D_MAGIC, 4, hsize},
-       {_E_MAGIC, 4, hsize},
-       {_Q_MAGIC, 4, hsize},
-       {_N_MAGIC, 4, hsize},
-       {_L_MAGIC, 4, hsize},
-       {_P_MAGIC, 4, hsize},
-       {_U_MAGIC, 4, hsize},
-       {_S_MAGIC, 8, hsize + 8},
-       {_T_MAGIC, 8, hsize + 8},
-       {_R_MAGIC, 8, hsize + 8},
-}
index e2afc587140a3fedfe00cb1966105fd38668e0db..60da304b55e5e8330d7e870beba1ec61f5aaa135 100644 (file)
@@ -281,6 +281,18 @@ func (d *decoder) Read(p []byte) (n int, err error) {
                                d.nbuf = copy(d.buf[0:], d.buf[nsrc:d.nbuf])
                                continue // copy out and return
                        }
+                       if ndst == 0 && d.err == nil {
+                               // Special case: input buffer is mostly filled with non-data bytes.
+                               // Filter out such bytes to make room for more input.
+                               off := 0
+                               for i := 0; i < d.nbuf; i++ {
+                                       if d.buf[i] > ' ' {
+                                               d.buf[off] = d.buf[i]
+                                               off++
+                                       }
+                               }
+                               d.nbuf = off
+                       }
                }
 
                // Out of input, out of decoded output.  Check errors.
index 77bc465d5949c916df1c6a75c8d244fb53e25aa4..aad199b4fad56be2fe05d8f089a5e0dfb002ac54 100644 (file)
@@ -197,3 +197,14 @@ func TestBig(t *testing.T) {
                t.Errorf("Decode(Encode(%d-byte string)) failed at offset %d", n, i)
        }
 }
+
+func TestDecoderInternalWhitespace(t *testing.T) {
+       s := strings.Repeat(" ", 2048) + "z"
+       decoded, err := ioutil.ReadAll(NewDecoder(strings.NewReader(s)))
+       if err != nil {
+               t.Errorf("Decode gave error %v", err)
+       }
+       if want := []byte("\000\000\000\000"); !bytes.Equal(want, decoded) {
+               t.Errorf("Decode failed: got %v, want %v", decoded, want)
+       }
+}
index 7a3c3797c8bb8240455c133c208f912073315dc5..ec7f91c1bba5ebd8909aecabd24e7e6078b977a2 100644 (file)
@@ -465,11 +465,15 @@ func parseSequenceOf(bytes []byte, sliceType reflect.Type, elemType reflect.Type
                if err != nil {
                        return
                }
-               // We pretend that various other string types are PRINTABLE STRINGs
-               // so that a sequence of them can be parsed into a []string.
                switch t.tag {
                case tagIA5String, tagGeneralString, tagT61String, tagUTF8String:
+                       // We pretend that various other string types are
+                       // PRINTABLE STRINGs so that a sequence of them can be
+                       // parsed into a []string.
                        t.tag = tagPrintableString
+               case tagGeneralizedTime, tagUTCTime:
+                       // Likewise, both time types are treated the same.
+                       t.tag = tagUTCTime
                }
 
                if t.class != classUniversal || t.isCompound != compoundType || t.tag != expectedTag {
index da50cf25e83a7ab5c38a3ed2d3dc6005bf9d780c..e26fe59b305b29e1c8f81a06a61b150e3adb7ddb 100644 (file)
@@ -295,8 +295,23 @@ func marshalTwoDigits(out *forkableWriter, v int) (err error) {
        return out.WriteByte(byte('0' + v%10))
 }
 
+func marshalFourDigits(out *forkableWriter, v int) (err error) {
+       var bytes [4]byte
+       for i := range bytes {
+               bytes[3-i] = '0' + byte(v%10)
+               v /= 10
+       }
+       _, err = out.Write(bytes[:])
+       return
+}
+
+func outsideUTCRange(t time.Time) bool {
+       year := t.Year()
+       return year < 1950 || year >= 2050
+}
+
 func marshalUTCTime(out *forkableWriter, t time.Time) (err error) {
-       year, month, day := t.Date()
+       year := t.Year()
 
        switch {
        case 1950 <= year && year < 2000:
@@ -310,6 +325,24 @@ func marshalUTCTime(out *forkableWriter, t time.Time) (err error) {
                return
        }
 
+       return marshalTimeCommon(out, t)
+}
+
+func marshalGeneralizedTime(out *forkableWriter, t time.Time) (err error) {
+       year := t.Year()
+       if year < 0 || year > 9999 {
+               return StructuralError{"cannot represent time as GeneralizedTime"}
+       }
+       if err = marshalFourDigits(out, year); err != nil {
+               return
+       }
+
+       return marshalTimeCommon(out, t)
+}
+
+func marshalTimeCommon(out *forkableWriter, t time.Time) (err error) {
+       _, month, day := t.Date()
+
        err = marshalTwoDigits(out, int(month))
        if err != nil {
                return
@@ -378,7 +411,12 @@ func stripTagAndLength(in []byte) []byte {
 func marshalBody(out *forkableWriter, value reflect.Value, params fieldParameters) (err error) {
        switch value.Type() {
        case timeType:
-               return marshalUTCTime(out, value.Interface().(time.Time))
+               t := value.Interface().(time.Time)
+               if outsideUTCRange(t) {
+                       return marshalGeneralizedTime(out, t)
+               } else {
+                       return marshalUTCTime(out, t)
+               }
        case bitStringType:
                return marshalBitString(out, value.Interface().(BitString))
        case objectIdentifierType:
@@ -504,7 +542,8 @@ func marshalField(out *forkableWriter, v reflect.Value, params fieldParameters)
                return StructuralError{"explicit string type given to non-string member"}
        }
 
-       if tag == tagPrintableString {
+       switch tag {
+       case tagPrintableString:
                if params.stringType == 0 {
                        // This is a string without an explicit string type. We'll use
                        // a PrintableString if the character set in the string is
@@ -521,6 +560,10 @@ func marshalField(out *forkableWriter, v reflect.Value, params fieldParameters)
                } else {
                        tag = params.stringType
                }
+       case tagUTCTime:
+               if outsideUTCRange(v.Interface().(time.Time)) {
+                       tag = tagGeneralizedTime
+               }
        }
 
        if params.set {
index 763c86da23f5b0b5b2ea7e2cb361fd8e8f6375e0..a15acbed012d15e4b74d47a261ffa289b22635d9 100644 (file)
@@ -67,6 +67,14 @@ type marshalTest struct {
        out string // hex encoded
 }
 
+func farFuture() time.Time {
+       t, err := time.Parse(time.RFC3339, "2100-04-05T12:01:01Z")
+       if err != nil {
+               panic(err)
+       }
+       return t
+}
+
 var marshalTests = []marshalTest{
        {10, "02010a"},
        {127, "02017f"},
@@ -83,6 +91,7 @@ var marshalTests = []marshalTest{
        {time.Unix(0, 0).UTC(), "170d3730303130313030303030305a"},
        {time.Unix(1258325776, 0).UTC(), "170d3039313131353232353631365a"},
        {time.Unix(1258325776, 0).In(PST), "17113039313131353134353631362d30383030"},
+       {farFuture(), "180f32313030303430353132303130315a"},
        {BitString{[]byte{0x80}, 1}, "03020780"},
        {BitString{[]byte{0x81, 0xf0}, 12}, "03030481f0"},
        {ObjectIdentifier([]int{1, 2, 3, 4}), "06032a0304"},
index fe17b732207c82219d97bae3a39b0be83aab0536..d770de3915fb09df4ca74e7895c864b6444380e7 100644 (file)
@@ -179,13 +179,11 @@ func (e *encoder) Write(p []byte) (n int, err error) {
                nn := len(e.out) / 8 * 5
                if nn > len(p) {
                        nn = len(p)
+                       nn -= nn % 5
                }
-               nn -= nn % 5
-               if nn > 0 {
-                       e.enc.Encode(e.out[0:], p[0:nn])
-                       if _, e.err = e.w.Write(e.out[0 : nn/5*8]); e.err != nil {
-                               return n, e.err
-                       }
+               e.enc.Encode(e.out[0:], p[0:nn])
+               if _, e.err = e.w.Write(e.out[0 : nn/5*8]); e.err != nil {
+                       return n, e.err
                }
                n += nn
                p = p[nn:]
@@ -268,7 +266,7 @@ func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err error) {
                                // 7, 5 and 2 are not valid padding lengths, and so 1, 3 and 6 are not
                                // valid dlen values. See RFC 4648 Section 6 "Base 32 Encoding" listing
                                // the five valid padding lengths, and Section 9 "Illustrations and
-                               // Examples" for an illustration for how the the 1st, 3rd and 6th base32
+                               // Examples" for an illustration for how the 1st, 3rd and 6th base32
                                // src bytes do not yield enough information to decode a dst byte.
                                if dlen == 1 || dlen == 3 || dlen == 6 {
                                        return n, false, CorruptInputError(olen - len(src) - 1)
index 85e398fd0b7be3b59e85df6f8a565cb53d0f8e91..e38c26d0ec73cc508306f8e200de26173b9e849f 100644 (file)
@@ -159,13 +159,11 @@ func (e *encoder) Write(p []byte) (n int, err error) {
                nn := len(e.out) / 4 * 3
                if nn > len(p) {
                        nn = len(p)
+                       nn -= nn % 3
                }
-               nn -= nn % 3
-               if nn > 0 {
-                       e.enc.Encode(e.out[0:], p[0:nn])
-                       if _, e.err = e.w.Write(e.out[0 : nn/3*4]); e.err != nil {
-                               return n, e.err
-                       }
+               e.enc.Encode(e.out[0:], p[0:nn])
+               if _, e.err = e.w.Write(e.out[0 : nn/3*4]); e.err != nil {
+                       return n, e.err
                }
                n += nn
                p = p[nn:]
@@ -226,21 +224,33 @@ func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err error) {
                var dbuf [4]byte
                dlen := 4
 
-               for j := 0; j < 4; {
+               for j := range dbuf {
                        if len(src) == 0 {
                                return n, false, CorruptInputError(olen - len(src) - j)
                        }
                        in := src[0]
                        src = src[1:]
-                       if in == '=' && j >= 2 && len(src) < 4 {
+                       if in == '=' {
                                // We've reached the end and there's padding
-                               if len(src)+j < 4-1 {
-                                       // not enough padding
-                                       return n, false, CorruptInputError(olen)
-                               }
-                               if len(src) > 0 && src[0] != '=' {
+                               switch j {
+                               case 0, 1:
                                        // incorrect padding
                                        return n, false, CorruptInputError(olen - len(src) - 1)
+                               case 2:
+                                       // "==" is expected, the first "=" is already consumed.
+                                       if len(src) == 0 {
+                                               // not enough padding
+                                               return n, false, CorruptInputError(olen)
+                                       }
+                                       if src[0] != '=' {
+                                               // incorrect padding
+                                               return n, false, CorruptInputError(olen - len(src) - 1)
+                                       }
+                                       src = src[1:]
+                               }
+                               if len(src) > 0 {
+                                       // trailing garbage
+                                       err = CorruptInputError(olen - len(src))
                                }
                                dlen, end = j, true
                                break
@@ -249,7 +259,6 @@ func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err error) {
                        if dbuf[j] == 0xFF {
                                return n, false, CorruptInputError(olen - len(src) - 1)
                        }
-                       j++
                }
 
                // Pack 4x 6-bit source blocks into 3 byte destination
@@ -268,7 +277,7 @@ func (enc *Encoding) decode(dst, src []byte) (n int, end bool, err error) {
                n += dlen - 1
        }
 
-       return n, end, nil
+       return n, end, err
 }
 
 // Decode decodes src using the encoding enc.  It writes at most
index 6bcc724d9b5702fc4d42e67bd5df2d9a1cef5cb1..a075194e03ef1345fb1e90bff49813b1ff25ea22 100644 (file)
@@ -9,6 +9,7 @@ import (
        "errors"
        "io"
        "io/ioutil"
+       "reflect"
        "strings"
        "testing"
        "time"
@@ -149,9 +150,13 @@ func TestDecodeCorrupt(t *testing.T) {
        }{
                {"", -1},
                {"!!!!", 0},
+               {"====", 0},
                {"x===", 1},
+               {"=AAA", 0},
+               {"A=AA", 1},
                {"AA=A", 2},
-               {"AAA=AAAA", 3},
+               {"AA==A", 4},
+               {"AAA=AAAA", 4},
                {"AAAAA", 4},
                {"AAAAAA", 4},
                {"A=", 1},
@@ -161,6 +166,7 @@ func TestDecodeCorrupt(t *testing.T) {
                {"AAA=", -1},
                {"AAAA", -1},
                {"AAAAAA=", 7},
+               {"YWJjZA=====", 8},
        }
        for _, tc := range testCases {
                dbuf := make([]byte, StdEncoding.DecodedLen(len(tc.input)))
@@ -325,3 +331,14 @@ bqbPb06551Y4
                t.Error("Decoded results not equal")
        }
 }
+
+func TestDecoderIssue7733(t *testing.T) {
+       s, err := StdEncoding.DecodeString("YWJjZA=====")
+       want := CorruptInputError(8)
+       if !reflect.DeepEqual(want, err) {
+               t.Errorf("Error = %v; want CorruptInputError(8)", err)
+       }
+       if string(s) != "abcd" {
+               t.Errorf("DecodeString = %q; want abcd", s)
+       }
+}
index f3466b9af0313e279dcf93de8cea6bcf84e307b1..a5694876ac46eff5f80679c725cf3fd0699afaeb 100644 (file)
@@ -133,6 +133,7 @@ func (bigEndian) GoString() string { return "binary.BigEndian" }
 // When reading into structs, the field data for fields with
 // blank (_) field names is skipped; i.e., blank field names
 // may be used for padding.
+// When reading into a struct, all non-blank fields must be exported.
 func Read(r io.Reader, order ByteOrder, data interface{}) error {
        // Fast path for basic types and slices.
        if n := intDataSize(data); n != 0 {
index 1aa6ecd2486ba007cd565209e49bbd05928bff70..c80c90383afbeadb3cf30620f8e878cbfda4c293 100644 (file)
@@ -265,6 +265,30 @@ func TestBlankFields(t *testing.T) {
        }
 }
 
+// An attempt to read into a struct with an unexported field will
+// panic.  This is probably not the best choice, but at this point
+// anything else would be an API change.
+
+type Unexported struct {
+       a int32
+}
+
+func TestUnexportedRead(t *testing.T) {
+       var buf bytes.Buffer
+       u1 := Unexported{a: 1}
+       if err := Write(&buf, LittleEndian, &u1); err != nil {
+               t.Fatal(err)
+       }
+
+       defer func() {
+               if recover() == nil {
+                       t.Fatal("did not panic")
+               }
+       }()
+       var u2 Unexported
+       Read(&buf, LittleEndian, &u2)
+}
+
 type byteSliceReader struct {
        remain []byte
 }
index aa186a582e87a906f6101cb294e32c246b244203..d8513148ec2d5bbe9b34c468abf1f718b4f7b1b1 100644 (file)
@@ -685,7 +685,7 @@ func (dec *Decoder) ignoreSlice(state *decoderState, elemOp decOp) {
 // but first it checks that the assignment will succeed.
 func setInterfaceValue(ivalue reflect.Value, value reflect.Value) {
        if !value.Type().AssignableTo(ivalue.Type()) {
-               errorf("cannot assign value of type %s to %s", value.Type(), ivalue.Type())
+               errorf("%s is not assignable to type %s", value.Type(), ivalue.Type())
        }
        ivalue.Set(value)
 }
index 04f706ca540327602488de59dad780b4e22a8846..3a769ec1254ad15d080f6a61c8741eb343835352 100644 (file)
@@ -183,11 +183,13 @@ func (dec *Decoder) decodeTypeSequence(isInterface bool) typeId {
        return -1
 }
 
-// Decode reads the next value from the connection and stores
+// Decode reads the next value from the input stream and stores
 // it in the data represented by the empty interface value.
 // If e is nil, the value will be discarded. Otherwise,
 // the value underlying e must be a pointer to the
 // correct type for the next data item received.
+// If the input is at EOF, Decode returns io.EOF and
+// does not modify e.
 func (dec *Decoder) Decode(e interface{}) error {
        if e == nil {
                return dec.DecodeValue(reflect.Value{})
@@ -202,10 +204,12 @@ func (dec *Decoder) Decode(e interface{}) error {
        return dec.DecodeValue(value)
 }
 
-// DecodeValue reads the next value from the connection.
+// DecodeValue reads the next value from the input stream.
 // If v is the zero reflect.Value (v.Kind() == Invalid), DecodeValue discards the value.
 // Otherwise, it stores the value into v.  In that case, v must represent
 // a non-nil pointer to data or be an assignable reflect.Value (v.CanSet())
+// If the input is at EOF, DecodeValue returns io.EOF and
+// does not modify e.
 func (dec *Decoder) DecodeValue(v reflect.Value) error {
        if v.IsValid() {
                if v.Kind() == reflect.Ptr && !v.IsNil() {
index d158b6442a8c7f9f3348d3443683cdfbe2d1504a..7831c02d139a2e66c44af9ecd8477eeaa80fe99f 100644 (file)
@@ -491,7 +491,7 @@ func isZero(val reflect.Value) bool {
                return !val.Bool()
        case reflect.Complex64, reflect.Complex128:
                return val.Complex() == 0
-       case reflect.Chan, reflect.Func, reflect.Ptr:
+       case reflect.Chan, reflect.Func, reflect.Interface, reflect.Ptr:
                return val.IsNil()
        case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
                return val.Int() == 0
index 0193e2b67d4312b452676cbd787b0563eed55998..157b7723a75ff6774264b56882bc4bcdbe548431 100644 (file)
@@ -705,13 +705,14 @@ func TestGobEncoderExtraIndirect(t *testing.T) {
 }
 
 // Another bug: this caused a crash with the new Go1 Time type.
-// We throw in a gob-encoding array, to test another case of isZero
-
+// We throw in a gob-encoding array, to test another case of isZero,
+// and a struct containing an nil interface, to test a third.
 type isZeroBug struct {
        T time.Time
        S string
        I int
        A isZeroBugArray
+       F isZeroBugInterface
 }
 
 type isZeroBugArray [2]uint8
@@ -731,8 +732,20 @@ func (a *isZeroBugArray) GobDecode(data []byte) error {
        return nil
 }
 
+type isZeroBugInterface struct {
+       I interface{}
+}
+
+func (i isZeroBugInterface) GobEncode() (b []byte, e error) {
+       return []byte{}, nil
+}
+
+func (i *isZeroBugInterface) GobDecode(data []byte) error {
+       return nil
+}
+
 func TestGobEncodeIsZero(t *testing.T) {
-       x := isZeroBug{time.Now(), "hello", -55, isZeroBugArray{1, 2}}
+       x := isZeroBug{time.Now(), "hello", -55, isZeroBugArray{1, 2}, isZeroBugInterface{}}
        b := new(bytes.Buffer)
        enc := NewEncoder(b)
        err := enc.Encode(x)
index 167d00e032aec9a56be8fb728fc415426d54bdc4..d1fc7024a97fa24c0ea8b096dfb5a7d144550848 100644 (file)
@@ -146,6 +146,9 @@ func (h *dumper) Write(data []byte) (n int, err error) {
                        h.buf[12] = ' '
                        h.buf[13] = ' '
                        _, err = h.w.Write(h.buf[4:])
+                       if err != nil {
+                               return
+                       }
                }
                Encode(h.buf[:], data[i:i+1])
                h.buf[2] = ' '
index dde0d78e327380724270cab3e8b8a45ae7e1339c..af1c908ad77d61f49d2aa6abded0a936480a867f 100644 (file)
@@ -54,6 +54,11 @@ import (
 // If no more serious errors are encountered, Unmarshal returns
 // an UnmarshalTypeError describing the earliest such error.
 //
+// The JSON null value unmarshals into an interface, map, pointer, or slice
+// by setting that Go value to nil. Because null is often used in JSON to mean
+// ``not present,'' unmarshaling a JSON null into any other Go type has no effect
+// on the value and produces no error.
+//
 // When unmarshaling quoted strings, invalid UTF-8 or
 // invalid UTF-16 surrogate pairs are not treated as an error.
 // Instead, they are replaced by the Unicode replacement
index 11ef709cce7ce9478c1cfef733c4f59c3978ac6a..e1bacafd6b860e2e2d5bbf081b4bbd41b09ebcf4 100644 (file)
@@ -69,8 +69,9 @@ func newline(dst *bytes.Buffer, prefix, indent string, depth int) {
 // Each element in a JSON object or array begins on a new,
 // indented line beginning with prefix followed by one or more
 // copies of indent according to the indentation nesting.
-// The data appended to dst has no trailing newline, to make it easier
-// to embed inside other formatted JSON data.
+// The data appended to dst does not begin with the prefix nor
+// any indentation, and has no trailing newline, to make it
+// easier to embed inside other formatted JSON data.
 func Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error {
        origLen := dst.Len()
        var scan scanner
index e8d6bd4c2ce00a4f4ed7bbb1e7dc647b4db66b9e..1cb289fd84317679b7247a767a65e21cbd8ec76c 100644 (file)
@@ -148,7 +148,8 @@ func NewEncoder(w io.Writer) *Encoder {
        return &Encoder{w: w}
 }
 
-// Encode writes the JSON encoding of v to the stream.
+// Encode writes the JSON encoding of v to the stream,
+// followed by a newline character.
 //
 // See the documentation for Marshal for details about the
 // conversion of Go values to JSON.
index d9522e0b39f718c0a24dfadfdd9a5ade616cd2f6..8c6342013d330484aca25b65f967d0b3f3c47658 100644 (file)
@@ -184,10 +184,12 @@ var (
 // EncodeToken does not call Flush, because usually it is part of a larger operation
 // such as Encode or EncodeElement (or a custom Marshaler's MarshalXML invoked
 // during those), and those will call Flush when finished.
-//
 // Callers that create an Encoder and then invoke EncodeToken directly, without
 // using Encode or EncodeElement, need to call Flush when finished to ensure
 // that the XML is written to the underlying writer.
+//
+// EncodeToken allows writing a ProcInst with Target set to "xml" only as the first token
+// in the stream.
 func (enc *Encoder) EncodeToken(t Token) error {
        p := &enc.p
        switch t := t.(type) {
@@ -210,7 +212,12 @@ func (enc *Encoder) EncodeToken(t Token) error {
                p.WriteString("-->")
                return p.cachedWriteError()
        case ProcInst:
-               if t.Target == "xml" || !isNameString(t.Target) {
+               // First token to be encoded which is also a ProcInst with target of xml
+               // is the xml declaration.  The only ProcInst where target of xml is allowed.
+               if t.Target == "xml" && p.Buffered() != 0 {
+                       return fmt.Errorf("xml: EncodeToken of ProcInst xml target only valid for xml declaration, first token encoded")
+               }
+               if !isNameString(t.Target) {
                        return fmt.Errorf("xml: EncodeToken of ProcInst with invalid Target")
                }
                if bytes.Contains(t.Inst, endProcInst) {
index d34118a3d8b7f7cf9708387e4936eec3fa891dd5..14f73a75d5fbd8f381461537b45177102d36b724 100644 (file)
@@ -314,6 +314,31 @@ type MarshalerStruct struct {
        Foo MyMarshalerAttrTest `xml:",attr"`
 }
 
+type InnerStruct struct {
+       XMLName Name `xml:"testns outer"`
+}
+
+type OuterStruct struct {
+       InnerStruct
+       IntAttr int `xml:"int,attr"`
+}
+
+type OuterNamedStruct struct {
+       InnerStruct
+       XMLName Name `xml:"outerns test"`
+       IntAttr int  `xml:"int,attr"`
+}
+
+type OuterNamedOrderedStruct struct {
+       XMLName Name `xml:"outerns test"`
+       InnerStruct
+       IntAttr int `xml:"int,attr"`
+}
+
+type OuterOuterStruct struct {
+       OuterStruct
+}
+
 func ifaceptr(x interface{}) interface{} {
        return &x
 }
@@ -883,6 +908,22 @@ var marshalTests = []struct {
                ExpectXML: `<MarshalerStruct Foo="hello world"></MarshalerStruct>`,
                Value:     &MarshalerStruct{},
        },
+       {
+               ExpectXML: `<outer xmlns="testns" int="10"></outer>`,
+               Value:     &OuterStruct{IntAttr: 10},
+       },
+       {
+               ExpectXML: `<test xmlns="outerns" int="10"></test>`,
+               Value:     &OuterNamedStruct{XMLName: Name{Space: "outerns", Local: "test"}, IntAttr: 10},
+       },
+       {
+               ExpectXML: `<test xmlns="outerns" int="10"></test>`,
+               Value:     &OuterNamedOrderedStruct{XMLName: Name{Space: "outerns", Local: "test"}, IntAttr: 10},
+       },
+       {
+               ExpectXML: `<outer xmlns="testns" int="10"></outer>`,
+               Value:     &OuterOuterStruct{OuterStruct{IntAttr: 10}},
+       },
 }
 
 func TestMarshal(t *testing.T) {
@@ -1149,3 +1190,77 @@ func TestStructPointerMarshal(t *testing.T) {
                t.Fatal(err)
        }
 }
+
+var encodeTokenTests = []struct {
+       tok  Token
+       want string
+       ok   bool
+}{
+       {StartElement{Name{"space", "local"}, nil}, "<local xmlns=\"space\">", true},
+       {StartElement{Name{"space", ""}, nil}, "", false},
+       {EndElement{Name{"space", ""}}, "", false},
+       {CharData("foo"), "foo", true},
+       {Comment("foo"), "<!--foo-->", true},
+       {Comment("foo-->"), "", false},
+       {ProcInst{"Target", []byte("Instruction")}, "<?Target Instruction?>", true},
+       {ProcInst{"", []byte("Instruction")}, "", false},
+       {ProcInst{"Target", []byte("Instruction?>")}, "", false},
+       {Directive("foo"), "<!foo>", true},
+       {Directive("foo>"), "", false},
+}
+
+func TestEncodeToken(t *testing.T) {
+       for _, tt := range encodeTokenTests {
+               var buf bytes.Buffer
+               enc := NewEncoder(&buf)
+               err := enc.EncodeToken(tt.tok)
+               switch {
+               case !tt.ok && err == nil:
+                       t.Errorf("enc.EncodeToken(%#v): expected error; got none", tt.tok)
+               case tt.ok && err != nil:
+                       t.Fatalf("enc.EncodeToken: %v", err)
+               case !tt.ok && err != nil:
+                       // expected error, got one
+               }
+               if err := enc.Flush(); err != nil {
+                       t.Fatalf("enc.EncodeToken: %v", err)
+               }
+               if got := buf.String(); got != tt.want {
+                       t.Errorf("enc.EncodeToken = %s; want: %s", got, tt.want)
+               }
+       }
+}
+
+func TestProcInstEncodeToken(t *testing.T) {
+       var buf bytes.Buffer
+       enc := NewEncoder(&buf)
+
+       if err := enc.EncodeToken(ProcInst{"xml", []byte("Instruction")}); err != nil {
+               t.Fatalf("enc.EncodeToken: expected to be able to encode xml target ProcInst as first token, %s", err)
+       }
+
+       if err := enc.EncodeToken(ProcInst{"Target", []byte("Instruction")}); err != nil {
+               t.Fatalf("enc.EncodeToken: expected to be able to add non-xml target ProcInst")
+       }
+
+       if err := enc.EncodeToken(ProcInst{"xml", []byte("Instruction")}); err == nil {
+               t.Fatalf("enc.EncodeToken: expected to not be allowed to encode xml target ProcInst when not first token")
+       }
+}
+
+func TestDecodeEncode(t *testing.T) {
+       var in, out bytes.Buffer
+       in.WriteString(`<?xml version="1.0" encoding="UTF-8"?>
+<?Target Instruction?>
+<root>
+</root>        
+`)
+       dec := NewDecoder(&in)
+       enc := NewEncoder(&out)
+       for tok, err := dec.Token(); err == nil; tok, err = dec.Token() {
+               err = enc.EncodeToken(tok)
+               if err != nil {
+                       t.Fatalf("enc.EncodeToken: Unable to encode token (%#v), %v", tok, err)
+               }
+       }
+}
index 651d13d4d094dd6adba063ac79c82d9117833115..75b9f2ba1b2ee44fad5e1a72e53058c988364db2 100644 (file)
@@ -284,6 +284,15 @@ func (p *Decoder) unmarshal(val reflect.Value, start *StartElement) error {
                }
        }
 
+       // Load value from interface, but only if the result will be
+       // usefully addressable.
+       if val.Kind() == reflect.Interface && !val.IsNil() {
+               e := val.Elem()
+               if e.Kind() == reflect.Ptr && !e.IsNil() {
+                       val = e
+               }
+       }
+
        if val.Kind() == reflect.Ptr {
                if val.IsNil() {
                        val.Set(reflect.New(val.Type().Elem()))
index 1404c900f50506977c98c3f6d80f02cd0c841fa3..01f55d0dd00cec4ae81876f3f5854678705655ae 100644 (file)
@@ -685,3 +685,30 @@ func TestUnmarshaler(t *testing.T) {
                t.Errorf("m=%#+v\n", m)
        }
 }
+
+type Pea struct {
+       Cotelydon string
+}
+
+type Pod struct {
+       Pea interface{} `xml:"Pea"`
+}
+
+// https://code.google.com/p/go/issues/detail?id=6836
+func TestUnmarshalIntoInterface(t *testing.T) {
+       pod := new(Pod)
+       pod.Pea = new(Pea)
+       xml := `<Pod><Pea><Cotelydon>Green stuff</Cotelydon></Pea></Pod>`
+       err := Unmarshal([]byte(xml), pod)
+       if err != nil {
+               t.Fatalf("failed to unmarshal %q: %v", xml, err)
+       }
+       pea, ok := pod.Pea.(*Pea)
+       if !ok {
+               t.Fatalf("unmarshalled into wrong type: have %T want *Pea", pod.Pea)
+       }
+       have, want := pea.Cotelydon, "Green stuff"
+       if have != want {
+               t.Errorf("failed to unmarshal into interface, have %q want %q", have, want)
+       }
+}
index 83e65402c00ebf6f0529402771a6879497b5c0c9..22248d20a6d669e348c93f7d4b5feb558eee8e6d 100644 (file)
@@ -75,6 +75,9 @@ func getTypeInfo(typ reflect.Type) (*typeInfo, error) {
                                        if err != nil {
                                                return nil, err
                                        }
+                                       if tinfo.xmlname == nil {
+                                               tinfo.xmlname = inner.xmlname
+                                       }
                                        for _, finfo := range inner.fields {
                                                finfo.idx = append([]int{i}, finfo.idx...)
                                                if err := addFieldInfo(typ, tinfo, &finfo); err != nil {
index 5b9d670024e792e022db314e6e87d29d7addc680..b473cb845847447de8f3947197751a31c700ef20 100644 (file)
@@ -200,6 +200,8 @@ type Decoder struct {
 }
 
 // NewDecoder creates a new XML parser reading from r.
+// If r does not implement io.ByteReader, NewDecoder will
+// do its own buffering.
 func NewDecoder(r io.Reader) *Decoder {
        d := &Decoder{
                ns:       make(map[string]string),
index c590782a8d266f0fad192cccfa009d51f248a919..9b6dab487cb6209e9b3e407dce9f90ce23bca372 100644 (file)
@@ -108,11 +108,11 @@ func (v *Map) String() string {
        var b bytes.Buffer
        fmt.Fprintf(&b, "{")
        first := true
-       v.Do(func(kv KeyValue) {
+       v.doLocked(func(kv KeyValue) {
                if !first {
                        fmt.Fprintf(&b, ", ")
                }
-               fmt.Fprintf(&b, "\"%s\": %v", kv.Key, kv.Value)
+               fmt.Fprintf(&b, "%q: %v", kv.Key, kv.Value)
                first = false
        })
        fmt.Fprintf(&b, "}")
@@ -202,6 +202,12 @@ func (v *Map) AddFloat(key string, delta float64) {
 func (v *Map) Do(f func(KeyValue)) {
        v.mu.RLock()
        defer v.mu.RUnlock()
+       v.doLocked(f)
+}
+
+// doLocked calls f for each entry in the map.
+// v.mu must be held for reads.
+func (v *Map) doLocked(f func(KeyValue)) {
        for _, k := range v.keys {
                f(KeyValue{k, v.m[k]})
        }
index d2ea484935e9c5fe08cb52e4c27c01a26b7cab79..765e3b757e9a48a95ea216a34efc83baec8e836e 100644 (file)
@@ -97,15 +97,15 @@ func TestMapCounter(t *testing.T) {
        colors.Add("red", 1)
        colors.Add("red", 2)
        colors.Add("blue", 4)
-       colors.AddFloat("green", 4.125)
+       colors.AddFloat(`green "midori"`, 4.125)
        if x := colors.m["red"].(*Int).i; x != 3 {
                t.Errorf("colors.m[\"red\"] = %v, want 3", x)
        }
        if x := colors.m["blue"].(*Int).i; x != 4 {
                t.Errorf("colors.m[\"blue\"] = %v, want 4", x)
        }
-       if x := colors.m["green"].(*Float).f; x != 4.125 {
-               t.Errorf("colors.m[\"green\"] = %v, want 3.14", x)
+       if x := colors.m[`green "midori"`].(*Float).f; x != 4.125 {
+               t.Errorf("colors.m[`green \"midori\"] = %v, want 3.14", x)
        }
 
        // colors.String() should be '{"red":3, "blue":4}',
index d3b454fefd4e814e185364842f38cfa6c395a997..cd2a165be19f9ac529fbc589349ef4460ae9d993 100644 (file)
@@ -50,7 +50,8 @@
        ("-" is a non-flag argument) or after the terminator "--".
 
        Integer flags accept 1234, 0664, 0x1234 and may be negative.
-       Boolean flags may be 1, 0, t, f, true, false, TRUE, FALSE, True, False.
+       Boolean flags may be:
+               1, 0, t, f, T, F, true, false, TRUE, FALSE, True, False
        Duration flags accept any input valid for time.ParseDuration.
 
        The default set of command-line flags is controlled by
@@ -754,7 +755,7 @@ func (f *FlagSet) parseOne() (bool, error) {
        if fv, ok := flag.Value.(boolFlag); ok && fv.IsBoolFlag() { // special case: doesn't need an arg
                if has_value {
                        if err := fv.Set(value); err != nil {
-                               return false, f.failf("invalid boolean value %q for  -%s: %v", value, name, err)
+                               return false, f.failf("invalid boolean value %q for -%s: %v", value, name, err)
                        }
                } else {
                        fv.Set("true")
index 095fd03b23ddd4042d09852dd906790f3accbb99..02642d6ae775d6700e3448174cbf1d316893ec13 100644 (file)
@@ -37,6 +37,7 @@
                %e      scientific notation, e.g. -1234.456e+78
                %E      scientific notation, e.g. -1234.456E+78
                %f      decimal point but no exponent, e.g. 123.456
+               %F      synonym for %f
                %g      whichever of %e or %f produces more compact output
                %G      whichever of %E or %f produces more compact output
        String and slice of bytes:
        There is no 'u' flag.  Integers are printed unsigned if they have unsigned type.
        Similarly, there is no need to specify the size of the operand (int8, int64).
 
-       The width and precision control formatting and are in units of Unicode
-       code points.  (This differs from C's printf where the units are numbers
+       Width is specified by an optional decimal number immediately following the verb.
+       If absent, the width is whatever is necessary to represent the value.
+       Precision is specified after the (optional) width by a period followed by a
+       decimal number. If no period is present, a default precision is used.
+       A period with no following number specifies a precision of zero.
+       Examples:
+               %f:    default width, default precision
+               %9f    width 9, default precision
+               %.2f   default width, precision 2
+               %9.2f  width 9, precision 2
+               %9.f   width 9, precision 0
+
+       Width and precision are measured in units of Unicode code points.
+       (This differs from C's printf where the units are numbers
        of bytes.) Either or both of the flags may be replaced with the
        character '*', causing their values to be obtained from the next
        operand, which must be of type int.
 
-       For numeric values, width sets the minimum width of the field and
+       For most values, width is the minimum number of characters to output,
+       padding the formatted form with spaces if necessary.
+       For strings, precision is the maximum number of characters to output,
+       truncating if necessary.
+
+       For floating-point values, width sets the minimum width of the field and
        precision sets the number of places after the decimal, if appropriate,
        except that for %g/%G it sets the total number of digits. For example,
        given 123.45 the format %6.2f prints 123.45 while %.4g prints 123.5.
        The default precision for %e and %f is 6; for %g it is the smallest
        number of digits necessary to identify the value uniquely.
 
-       For most values, width is the minimum number of characters to output,
-       padding the formatted form with spaces if necessary.
-       For strings, precision is the maximum number of characters to output,
-       truncating if necessary.
+       For complex numbers, the width and precision apply to the two
+       components independently and the result is parenthesized, so %f applied
+       to 1.2+3.4i produces (1.200000+3.400000i).
 
        Other flags:
                +       always print a sign for numeric values;
                fmt.Printf("%v\n", i)
        will print 23.
 
-       If an operand implements interface Formatter, that interface
-       can be used for fine control of formatting.
+       Except when printed using the verbs %T and %p, special
+       formatting considerations apply for operands that implement
+       certain interfaces. In order of application:
+
+       1. If an operand implements the Formatter interface, it will
+       be invoked. Formatter provides fine control of formatting.
+
+       2. If the %v verb is used with the # flag (%#v) and the operand
+       implements the GoStringer interface, that will be invoked.
 
        If the format (which is implicitly %v for Println etc.) is valid
-       for a string (%s %q %v %x %X), the following two rules also apply:
+       for a string (%s %q %v %x %X), the following two rules apply:
 
-       1. If an operand implements the error interface, the Error method
-       will be used to convert the object to a string, which will then
+       3. If an operand implements the error interface, the Error method
+       will be invoked to convert the object to a string, which will then
        be formatted as required by the verb (if any).
 
-       2. If an operand implements method String() string, that method
-       will be used to convert the object to a string, which will then
+       4. If an operand implements method String() string, that method
+       will be invoked to convert the object to a string, which will then
        be formatted as required by the verb (if any).
 
+       For compound operands such as slices and structs, the format
+       applies to the elements of each operand, recursively, not to the
+       operand as a whole. Thus %q will quote each element of a slice
+       of strings, and %6.2f will control formatting for each element
+       of a floating-point array.
+
        To avoid recursion in cases such as
                type X string
                func (x X) String() string { return Sprintf("<%s>", x) }
index ce837ba63e81bd2f569f2cabfedb4886084a1464..8e6923119de80ff72a401a6103756a44566a212b 100644 (file)
@@ -11,7 +11,6 @@ import (
        "math"
        "runtime"
        "strings"
-       "sync/atomic"
        "testing"
        "time"
        "unicode"
@@ -221,8 +220,12 @@ var fmtTests = []struct {
        {"%+.3e", 0.0, "+0.000e+00"},
        {"%+.3e", 1.0, "+1.000e+00"},
        {"%+.3f", -1.0, "-1.000"},
+       {"%+.3F", -1.0, "-1.000"},
+       {"%+.3F", float32(-1.0), "-1.000"},
        {"%+07.2f", 1.0, "+001.00"},
        {"%+07.2f", -1.0, "-001.00"},
+       {"%+10.2f", +1.0, "     +1.00"},
+       {"%+10.2f", -1.0, "     -1.00"},
        {"% .3E", -1.0, "-1.000E+00"},
        {"% .3e", 1.0, " 1.000e+00"},
        {"%+.3g", 0.0, "+0"},
@@ -242,6 +245,8 @@ var fmtTests = []struct {
        {"%+.3g", 1 + 2i, "(+1+2i)"},
        {"%.3e", 0i, "(0.000e+00+0.000e+00i)"},
        {"%.3f", 0i, "(0.000+0.000i)"},
+       {"%.3F", 0i, "(0.000+0.000i)"},
+       {"%.3F", complex64(0i), "(0.000+0.000i)"},
        {"%.3g", 0i, "(0+0i)"},
        {"%.3e", 1 + 2i, "(1.000e+00+2.000e+00i)"},
        {"%.3f", 1 + 2i, "(1.000+2.000i)"},
@@ -400,6 +405,8 @@ var fmtTests = []struct {
        {"%#v", "foo", `"foo"`},
        {"%#v", barray, `[5]fmt_test.renamedUint8{0x1, 0x2, 0x3, 0x4, 0x5}`},
        {"%#v", bslice, `[]fmt_test.renamedUint8{0x1, 0x2, 0x3, 0x4, 0x5}`},
+       {"%#v", []byte(nil), "[]byte(nil)"},
+       {"%#v", []int32(nil), "[]int32(nil)"},
 
        // slices with other formats
        {"%#x", []int{1, 2, 15}, `[0x1 0x2 0xf]`},
@@ -506,9 +513,76 @@ var fmtTests = []struct {
        {"%0.100f", 1.0, zeroFill("1.", 100, "")},
        {"%0.100f", -1.0, zeroFill("-1.", 100, "")},
 
-       // Zero padding floats used to put the minus sign in the middle.
-       {"%020f", -1.0, "-000000000001.000000"},
+       // Comparison of padding rules with C printf.
+       /*
+               C program:
+               #include <stdio.h>
+
+               char *format[] = {
+                       "[%.2f]",
+                       "[% .2f]",
+                       "[%+.2f]",
+                       "[%7.2f]",
+                       "[% 7.2f]",
+                       "[%+7.2f]",
+                       "[%07.2f]",
+                       "[% 07.2f]",
+                       "[%+07.2f]",
+               };
+
+               int main(void) {
+                       int i;
+                       for(i = 0; i < 9; i++) {
+                               printf("%s: ", format[i]);
+                               printf(format[i], 1.0);
+                               printf(" ");
+                               printf(format[i], -1.0);
+                               printf("\n");
+                       }
+               }
+
+               Output:
+                       [%.2f]: [1.00] [-1.00]
+                       [% .2f]: [ 1.00] [-1.00]
+                       [%+.2f]: [+1.00] [-1.00]
+                       [%7.2f]: [   1.00] [  -1.00]
+                       [% 7.2f]: [   1.00] [  -1.00]
+                       [%+7.2f]: [  +1.00] [  -1.00]
+                       [%07.2f]: [0001.00] [-001.00]
+                       [% 07.2f]: [ 001.00] [-001.00]
+                       [%+07.2f]: [+001.00] [-001.00]
+       */
+       {"%.2f", 1.0, "1.00"},
+       {"%.2f", -1.0, "-1.00"},
+       {"% .2f", 1.0, " 1.00"},
+       {"% .2f", -1.0, "-1.00"},
+       {"%+.2f", 1.0, "+1.00"},
+       {"%+.2f", -1.0, "-1.00"},
+       {"%7.2f", 1.0, "   1.00"},
+       {"%7.2f", -1.0, "  -1.00"},
+       {"% 7.2f", 1.0, "   1.00"},
+       {"% 7.2f", -1.0, "  -1.00"},
+       {"%+7.2f", 1.0, "  +1.00"},
+       {"%+7.2f", -1.0, "  -1.00"},
+       {"%07.2f", 1.0, "0001.00"},
+       {"%07.2f", -1.0, "-001.00"},
+       {"% 07.2f", 1.0, " 001.00"},
+       {"% 07.2f", -1.0, "-001.00"},
+       {"%+07.2f", 1.0, "+001.00"},
+       {"%+07.2f", -1.0, "-001.00"},
+
+       // Complex numbers: exhaustively tested in TestComplexFormatting.
+       {"%7.2f", 1 + 2i, "(   1.00  +2.00i)"},
+       {"%+07.2f", -1 - 2i, "(-001.00-002.00i)"},
+       // Zero padding does not apply to infinities.
+       {"%020f", math.Inf(-1), "                -Inf"},
+       {"%020f", math.Inf(+1), "                +Inf"},
+       {"% 020f", math.Inf(-1), "                -Inf"},
+       {"% 020f", math.Inf(+1), "                 Inf"},
+       {"%+020f", math.Inf(-1), "                -Inf"},
+       {"%+020f", math.Inf(+1), "                +Inf"},
        {"%20f", -1.0, "           -1.000000"},
+       // Make sure we can handle very large widths.
        {"%0100f", -1.0, zeroFill("-", 99, "1.000000")},
 
        // Complex fmt used to leave the plus flag set for future entries in the array
@@ -539,6 +613,16 @@ var fmtTests = []struct {
        {"%#072o", -1, zeroFill("-", 71, "1")},
        {"%#072d", 1, zeroFill("", 72, "1")},
        {"%#072d", -1, zeroFill("-", 71, "1")},
+
+       // Padding for complex numbers. Has been bad, then fixed, then bad again.
+       {"%+10.2f", +104.66 + 440.51i, "(   +104.66   +440.51i)"},
+       {"%+10.2f", -104.66 + 440.51i, "(   -104.66   +440.51i)"},
+       {"%+10.2f", +104.66 - 440.51i, "(   +104.66   -440.51i)"},
+       {"%+10.2f", -104.66 - 440.51i, "(   -104.66   -440.51i)"},
+       {"%+010.2f", +104.66 + 440.51i, "(+000104.66+000440.51i)"},
+       {"%+010.2f", -104.66 + 440.51i, "(-000104.66+000440.51i)"},
+       {"%+010.2f", +104.66 - 440.51i, "(+000104.66-000440.51i)"},
+       {"%+010.2f", -104.66 - 440.51i, "(-000104.66-000440.51i)"},
 }
 
 // zeroFill generates zero-filled strings of the specified width. The length
@@ -584,6 +668,50 @@ func TestSprintf(t *testing.T) {
        }
 }
 
+// TestComplexFormatting checks that a complex always formats to the same
+// thing as if done by hand with two singleton prints.
+func TestComplexFormatting(t *testing.T) {
+       var yesNo = []bool{true, false}
+       var signs = []float64{1, 0, -1}
+       for _, plus := range yesNo {
+               for _, zero := range yesNo {
+                       for _, space := range yesNo {
+                               for _, char := range "fFeEgG" {
+                                       realFmt := "%"
+                                       if zero {
+                                               realFmt += "0"
+                                       }
+                                       if space {
+                                               realFmt += " "
+                                       }
+                                       if plus {
+                                               realFmt += "+"
+                                       }
+                                       realFmt += "10.2"
+                                       realFmt += string(char)
+                                       // Imaginary part always has a sign, so force + and ignore space.
+                                       imagFmt := "%"
+                                       if zero {
+                                               imagFmt += "0"
+                                       }
+                                       imagFmt += "+"
+                                       imagFmt += "10.2"
+                                       imagFmt += string(char)
+                                       for _, realSign := range signs {
+                                               for _, imagSign := range signs {
+                                                       one := Sprintf(realFmt, complex(realSign, imagSign))
+                                                       two := Sprintf("("+realFmt+imagFmt+"i)", realSign, imagSign)
+                                                       if one != two {
+                                                               t.Error(f, one, two)
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+}
+
 type SE []interface{} // slice of empty; notational compactness.
 
 var reorderTests = []struct {
@@ -634,69 +762,63 @@ func TestReorder(t *testing.T) {
 }
 
 func BenchmarkSprintfEmpty(b *testing.B) {
-       benchmarkSprintf(b, func(buf *bytes.Buffer) {
-               Sprintf("")
+       b.RunParallel(func(pb *testing.PB) {
+               for pb.Next() {
+                       Sprintf("")
+               }
        })
 }
 
 func BenchmarkSprintfString(b *testing.B) {
-       benchmarkSprintf(b, func(buf *bytes.Buffer) {
-               Sprintf("%s", "hello")
+       b.RunParallel(func(pb *testing.PB) {
+               for pb.Next() {
+                       Sprintf("%s", "hello")
+               }
        })
 }
 
 func BenchmarkSprintfInt(b *testing.B) {
-       benchmarkSprintf(b, func(buf *bytes.Buffer) {
-               Sprintf("%d", 5)
+       b.RunParallel(func(pb *testing.PB) {
+               for pb.Next() {
+                       Sprintf("%d", 5)
+               }
        })
 }
 
 func BenchmarkSprintfIntInt(b *testing.B) {
-       benchmarkSprintf(b, func(buf *bytes.Buffer) {
-               Sprintf("%d %d", 5, 6)
+       b.RunParallel(func(pb *testing.PB) {
+               for pb.Next() {
+                       Sprintf("%d %d", 5, 6)
+               }
        })
 }
 
 func BenchmarkSprintfPrefixedInt(b *testing.B) {
-       benchmarkSprintf(b, func(buf *bytes.Buffer) {
-               Sprintf("This is some meaningless prefix text that needs to be scanned %d", 6)
+       b.RunParallel(func(pb *testing.PB) {
+               for pb.Next() {
+                       Sprintf("This is some meaningless prefix text that needs to be scanned %d", 6)
+               }
        })
 }
 
 func BenchmarkSprintfFloat(b *testing.B) {
-       benchmarkSprintf(b, func(buf *bytes.Buffer) {
-               Sprintf("%g", 5.23184)
+       b.RunParallel(func(pb *testing.PB) {
+               for pb.Next() {
+                       Sprintf("%g", 5.23184)
+               }
        })
 }
 
 func BenchmarkManyArgs(b *testing.B) {
-       benchmarkSprintf(b, func(buf *bytes.Buffer) {
-               buf.Reset()
-               Fprintf(buf, "%2d/%2d/%2d %d:%d:%d %s %s\n", 3, 4, 5, 11, 12, 13, "hello", "world")
+       b.RunParallel(func(pb *testing.PB) {
+               var buf bytes.Buffer
+               for pb.Next() {
+                       buf.Reset()
+                       Fprintf(&buf, "%2d/%2d/%2d %d:%d:%d %s %s\n", 3, 4, 5, 11, 12, 13, "hello", "world")
+               }
        })
 }
 
-func benchmarkSprintf(b *testing.B, f func(buf *bytes.Buffer)) {
-       const CallsPerSched = 1000
-       procs := runtime.GOMAXPROCS(-1)
-       N := int32(b.N / CallsPerSched)
-       c := make(chan bool, procs)
-       for p := 0; p < procs; p++ {
-               go func() {
-                       var buf bytes.Buffer
-                       for atomic.AddInt32(&N, -1) >= 0 {
-                               for g := 0; g < CallsPerSched; g++ {
-                                       f(&buf)
-                               }
-                       }
-                       c <- true
-               }()
-       }
-       for p := 0; p < procs; p++ {
-               <-c
-       }
-}
-
 var mallocBuf bytes.Buffer
 
 // gccgo numbers are different because gccgo does not have escape
index 3835aa9823a0e0f2f4b1253f64c70328d03c8fcd..a89c542cfb553cec42e0ca132328375a7d91e129 100644 (file)
@@ -5,6 +5,7 @@
 package fmt
 
 import (
+       "math"
        "strconv"
        "unicode/utf8"
 )
@@ -360,38 +361,48 @@ func doPrec(f *fmt, def int) int {
 
 // formatFloat formats a float64; it is an efficient equivalent to  f.pad(strconv.FormatFloat()...).
 func (f *fmt) formatFloat(v float64, verb byte, prec, n int) {
-       // We leave one byte at the beginning of f.intbuf for a sign if needed,
-       // and make it a space, which we might be able to use.
-       f.intbuf[0] = ' '
-       slice := strconv.AppendFloat(f.intbuf[0:1], v, verb, prec, n)
-       // Add a plus sign or space to the floating-point string representation if missing and required.
-       // The formatted number starts at slice[1].
-       switch slice[1] {
-       case '-', '+':
-               // If we're zero padding, want the sign before the leading zeros.
-               // Achieve this by writing the sign out and padding the postive number.
-               if f.zero && f.widPresent && f.wid > len(slice) {
-                       f.buf.WriteByte(slice[1])
-                       f.wid--
-                       f.pad(slice[2:])
-                       return
+       // Format number, reserving space for leading + sign if needed.
+       num := strconv.AppendFloat(f.intbuf[0:1], v, verb, prec, n)
+       if num[1] == '-' || num[1] == '+' {
+               num = num[1:]
+       } else {
+               num[0] = '+'
+       }
+       // Special handling for infinity, which doesn't look like a number so shouldn't be padded with zeros.
+       if math.IsInf(v, 0) {
+               if f.zero {
+                       defer func() { f.zero = true }()
+                       f.zero = false
                }
-               // We're set; drop the leading space.
-               slice = slice[1:]
-       default:
-               // There's no sign, but we might need one.
-               if f.plus {
-                       f.buf.WriteByte('+')
+       }
+       // num is now a signed version of the number.
+       // If we're zero padding, want the sign before the leading zeros.
+       // Achieve this by writing the sign out and then padding the unsigned number.
+       if f.zero && f.widPresent && f.wid > len(num) {
+               if f.space && v >= 0 {
+                       f.buf.WriteByte(' ') // This is what C does: even with zero, f.space means space.
+                       f.wid--
+               } else if f.plus || v < 0 {
+                       f.buf.WriteByte(num[0])
                        f.wid--
-                       f.pad(slice[1:])
-                       return
-               } else if f.space {
-                       // space is already there
-               } else {
-                       slice = slice[1:]
                }
+               f.pad(num[1:])
+               return
        }
-       f.pad(slice)
+       // f.space says to replace a leading + with a space.
+       if f.space && num[0] == '+' {
+               num[0] = ' '
+               f.pad(num)
+               return
+       }
+       // Now we know the sign is attached directly to the number, if present at all.
+       // We want a sign if asked for, if it's negative, or if it's infinity (+Inf vs. -Inf).
+       if f.plus || num[0] == '-' || math.IsInf(v, 0) {
+               f.pad(num)
+               return
+       }
+       // No sign to show and the number is positive; just print the unsigned number.
+       f.pad(num[1:])
 }
 
 // fmt_e64 formats a float64 in the form -1.23e+12.
@@ -436,60 +447,46 @@ func (f *fmt) fmt_fb32(v float32) { f.formatFloat(float64(v), 'b', 0, 32) }
 
 // fmt_c64 formats a complex64 according to the verb.
 func (f *fmt) fmt_c64(v complex64, verb rune) {
-       f.buf.WriteByte('(')
-       r := real(v)
-       oldPlus := f.plus
-       for i := 0; ; i++ {
-               switch verb {
-               case 'b':
-                       f.fmt_fb32(r)
-               case 'e':
-                       f.fmt_e32(r)
-               case 'E':
-                       f.fmt_E32(r)
-               case 'f':
-                       f.fmt_f32(r)
-               case 'g':
-                       f.fmt_g32(r)
-               case 'G':
-                       f.fmt_G32(r)
-               }
-               if i != 0 {
-                       break
-               }
-               f.plus = true
-               r = imag(v)
-       }
-       f.plus = oldPlus
-       f.buf.Write(irparenBytes)
+       f.fmt_complex(float64(real(v)), float64(imag(v)), 32, verb)
 }
 
 // fmt_c128 formats a complex128 according to the verb.
 func (f *fmt) fmt_c128(v complex128, verb rune) {
+       f.fmt_complex(real(v), imag(v), 64, verb)
+}
+
+// fmt_complex formats a complex number as (r+ji).
+func (f *fmt) fmt_complex(r, j float64, size int, verb rune) {
        f.buf.WriteByte('(')
-       r := real(v)
        oldPlus := f.plus
+       oldSpace := f.space
+       oldWid := f.wid
        for i := 0; ; i++ {
                switch verb {
                case 'b':
-                       f.fmt_fb64(r)
+                       f.formatFloat(r, 'b', 0, size)
                case 'e':
-                       f.fmt_e64(r)
+                       f.formatFloat(r, 'e', doPrec(f, 6), size)
                case 'E':
-                       f.fmt_E64(r)
-               case 'f':
-                       f.fmt_f64(r)
+                       f.formatFloat(r, 'E', doPrec(f, 6), size)
+               case 'f', 'F':
+                       f.formatFloat(r, 'f', doPrec(f, 6), size)
                case 'g':
-                       f.fmt_g64(r)
+                       f.formatFloat(r, 'g', doPrec(f, -1), size)
                case 'G':
-                       f.fmt_G64(r)
+                       f.formatFloat(r, 'G', doPrec(f, -1), size)
                }
                if i != 0 {
                        break
                }
+               // Imaginary part always has a sign.
                f.plus = true
-               r = imag(v)
+               f.space = false
+               f.wid = oldWid
+               r = j
        }
+       f.space = oldSpace
        f.plus = oldPlus
+       f.wid = oldWid
        f.buf.Write(irparenBytes)
 }
index 2f13bcd95e971415401e75a10b0f7d36d209d930..302661f4c85fc46285f7e1ccc5e490b5ea1f3355 100644 (file)
@@ -447,7 +447,7 @@ func (p *pp) fmtFloat32(v float32, verb rune) {
                p.fmt.fmt_e32(v)
        case 'E':
                p.fmt.fmt_E32(v)
-       case 'f':
+       case 'f', 'F':
                p.fmt.fmt_f32(v)
        case 'g', 'v':
                p.fmt.fmt_g32(v)
@@ -466,7 +466,7 @@ func (p *pp) fmtFloat64(v float64, verb rune) {
                p.fmt.fmt_e64(v)
        case 'E':
                p.fmt.fmt_E64(v)
-       case 'f':
+       case 'f', 'F':
                p.fmt.fmt_f64(v)
        case 'g', 'v':
                p.fmt.fmt_g64(v)
@@ -523,6 +523,15 @@ func (p *pp) fmtString(v string, verb rune, goSyntax bool) {
 func (p *pp) fmtBytes(v []byte, verb rune, goSyntax bool, typ reflect.Type, depth int) {
        if verb == 'v' || verb == 'd' {
                if goSyntax {
+                       if v == nil {
+                               if typ == nil {
+                                       p.buf.WriteString("[]byte(nil)")
+                               } else {
+                                       p.buf.WriteString(typ.String())
+                                       p.buf.Write(nilParenBytes)
+                               }
+                               return
+                       }
                        if typ == nil {
                                p.buf.Write(bytesBytes)
                        } else {
index 1fb4867dd28439382adf616714683ff489df738f..ac999d627cecbf739050e79a23ceaf40069dfa26 100644 (file)
@@ -149,7 +149,7 @@ func NewCommentMap(fset *token.FileSet, node Node, comments []*CommentGroup) Com
 
        // set up comment reader r
        tmp := make([]*CommentGroup, len(comments))
-       copy(tmp, comments) // don't change incomming comments
+       copy(tmp, comments) // don't change incoming comments
        sortComments(tmp)
        r := commentListReader{fset: fset, list: tmp} // !r.eol() because len(comments) > 0
        r.next()
index 096028d34c37118cd76f9d639c6e16089b987dca..4df0b8983ac7719ed856ec73fa7acd925db1c344 100644 (file)
@@ -264,6 +264,7 @@ var cgoEnabled = map[string]bool{
        "dragonfly/amd64": true,
        "freebsd/386":     true,
        "freebsd/amd64":   true,
+       "freebsd/arm":     true,
        "linux/386":       true,
        "linux/amd64":     true,
        "linux/arm":       true,
@@ -291,10 +292,10 @@ func defaultContext() Context {
        // say "+build go1.x", and code that should only be built before Go 1.x
        // (perhaps it is the stub to use in that case) should say "+build !go1.x".
        //
-       // When we reach Go 1.3 the line will read
-       //      c.ReleaseTags = []string{"go1.1", "go1.2", "go1.3"}
+       // When we reach Go 1.4 the line will read
+       //      c.ReleaseTags = []string{"go1.1", "go1.2", "go1.3", "go1.4"}
        // and so on.
-       c.ReleaseTags = []string{"go1.1", "go1.2"}
+       c.ReleaseTags = []string{"go1.1", "go1.2", "go1.3"}
 
        switch os.Getenv("CGO_ENABLED") {
        case "1":
@@ -1209,7 +1210,7 @@ func ArchChar(goarch string) (string, error) {
        switch goarch {
        case "386":
                return "8", nil
-       case "amd64":
+       case "amd64", "amd64p32":
                return "6", nil
        case "arm":
                return "5", nil
index d238e96eaa850954720898b5f17d2b57a6d1f175..3a795fdcc4bbed23494d5c4bc3a2a1d177f78da0 100644 (file)
@@ -8,6 +8,7 @@
 package build
 
 import (
+       "runtime"
        "sort"
        "testing"
 )
@@ -359,7 +360,7 @@ func allowed(pkg string) map[string]bool {
 }
 
 var bools = []bool{false, true}
-var geese = []string{"darwin", "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "plan9", "solaris", "windows"}
+var geese = []string{"darwin", "dragonfly", "freebsd", "linux", "nacl", "netbsd", "openbsd", "plan9", "solaris", "windows"}
 var goarches = []string{"386", "amd64", "arm", "arm64"}
 
 type osPkg struct {
@@ -374,6 +375,11 @@ var allowedErrors = map[osPkg]bool{
 }
 
 func TestDependencies(t *testing.T) {
+       if runtime.GOOS == "nacl" {
+               // NaCl tests run in a limited file system and we do not
+               // provide access to every source file.
+               t.Skip("skipping on NaCl")
+       }
        var all []string
 
        for k := range pkgDeps {
@@ -387,6 +393,9 @@ func TestDependencies(t *testing.T) {
                        if isMacro(pkg) {
                                continue
                        }
+                       if pkg == "runtime/cgo" && !ctxt.CgoEnabled {
+                               continue
+                       }
                        p, err := ctxt.Import(pkg, "", 0)
                        if err != nil {
                                if allowedErrors[osPkg{ctxt.GOOS, pkg}] {
index 83292f29f82fb423cd15fe1611d2ae4537e1f0b5..f17f76ccc7bb0566bd134966eb2f28713267429b 100644 (file)
@@ -99,6 +99,7 @@
 //     - "cgo", if ctxt.CgoEnabled is true
 //     - "go1.1", from Go version 1.1 onward
 //     - "go1.2", from Go version 1.2 onward
+//     - "go1.3", from Go version 1.3 onward
 //     - any additional words listed in ctxt.BuildTags
 //
 // If a file's name, after stripping the extension and a possible _test suffix,
index 71484aa9f1671795e63f8e269bc316f4ec54f303..b1d5bbe4f44f54ed0cdd24fbdf412d764f7e41b1 100644 (file)
@@ -4,5 +4,5 @@
 
 package build
 
-const goosList = "darwin dragonfly freebsd linux netbsd openbsd plan9 solaris windows "
-const goarchList = "386 amd64 arm arm64 alpha m68k mipso32 mipsn32 mipsn64 mipso64 ppc ppc64 sparc sparc64 "
+const goosList = "darwin dragonfly freebsd linux nacl netbsd openbsd plan9 solaris windows "
+const goarchList = "386 amd64 amd64p32 arm arm64 alpha m68k mipso32 mipsn32 mipsn64 mipso64 ppc ppc64 sparc sparc64 "
index 5c8c43e0c1aa6ee77469512c31460d6ace89a0de..f414ca4090e27dfbdb26774969eecb1853468045 100644 (file)
@@ -45,13 +45,13 @@ func commentEscape(w io.Writer, text string, nice bool) {
 
 const (
        // Regexp for Go identifiers
-       identRx = `[a-zA-Z_][a-zA-Z_0-9]*` // TODO(gri) ASCII only for now - fix this
+       identRx = `[\pL_][\pL_0-9]*`
 
        // Regexp for URLs
-       protocol = `(https?|ftp|file|gopher|mailto|news|nntp|telnet|wais|prospero):`
+       protocol = `https?|ftp|file|gopher|mailto|news|nntp|telnet|wais|prospero`
        hostPart = `[a-zA-Z0-9_@\-]+`
-       filePart = `[a-zA-Z0-9_?%#~&/\-+=]+`
-       urlRx    = protocol + `//` + // http://
+       filePart = `[a-zA-Z0-9_?%#~&/\-+=()]+` // parentheses may not be matching; see pairedParensPrefixLen
+       urlRx    = `(` + protocol + `)://` +   // http://
                hostPart + `([.:]` + hostPart + `)*/?` + // //www.google.com:8080/
                filePart + `([:.,]` + filePart + `)*`
 )
@@ -73,6 +73,29 @@ var (
        html_endh   = []byte("</h3>\n")
 )
 
+// pairedParensPrefixLen returns the length of the longest prefix of s containing paired parentheses.
+func pairedParensPrefixLen(s string) int {
+       parens := 0
+       l := len(s)
+       for i, ch := range s {
+               switch ch {
+               case '(':
+                       if parens == 0 {
+                               l = i
+                       }
+                       parens++
+               case ')':
+                       parens--
+                       if parens == 0 {
+                               l = len(s)
+                       } else if parens < 0 {
+                               return i
+                       }
+               }
+       }
+       return l
+}
+
 // Emphasize and escape a line of text for HTML. URLs are converted into links;
 // if the URL also appears in the words map, the link is taken from the map (if
 // the corresponding map value is the empty string, the URL is not converted
@@ -92,18 +115,26 @@ func emphasize(w io.Writer, line string, words map[string]string, nice bool) {
                // write text before match
                commentEscape(w, line[0:m[0]], nice)
 
-               // analyze match
+               // adjust match if necessary
                match := line[m[0]:m[1]]
+               if n := pairedParensPrefixLen(match); n < len(match) {
+                       // match contains unpaired parentheses (rare);
+                       // redo matching with shortened line for correct indices
+                       m = matchRx.FindStringSubmatchIndex(line[:m[0]+n])
+                       match = match[:n]
+               }
+
+               // analyze match
                url := ""
                italics := false
                if words != nil {
-                       url, italics = words[string(match)]
+                       url, italics = words[match]
                }
                if m[2] >= 0 {
                        // match against first parenthesized sub-regexp; must be match against urlRx
                        if !italics {
                                // no alternative URL in words list, use match instead
-                               url = string(match)
+                               url = match
                        }
                        italics = false // don't italicize URLs
                }
@@ -392,7 +423,9 @@ func ToText(w io.Writer, text string, indent, preIndent string, width int) {
                case opPre:
                        w.Write(nl)
                        for _, line := range b.lines {
-                               if !isBlank(line) {
+                               if isBlank(line) {
+                                       w.Write([]byte("\n"))
+                               } else {
                                        w.Write([]byte(preIndent))
                                        w.Write([]byte(line))
                                }
index aa21b8d1b30765e8699445f97f8f105f1ee19721..ad65c2a27f8a09d22237f59287e0a06cd91c4a5c 100644 (file)
@@ -42,8 +42,9 @@ func TestIsHeading(t *testing.T) {
 }
 
 var blocksTests = []struct {
-       in  string
-       out []block
+       in   string
+       out  []block
+       text string
 }{
        {
                in: `Para 1.
@@ -59,6 +60,22 @@ Para 3.
        pre1
 
 Para 4.
+
+       pre
+       pre1
+
+       pre2
+
+Para 5.
+
+
+       pre
+
+
+       pre1
+       pre2
+
+Para 6.
        pre
        pre2
 `,
@@ -69,8 +86,44 @@ Para 4.
                        {opPara, []string{"Para 3.\n"}},
                        {opPre, []string{"pre\n", "pre1\n"}},
                        {opPara, []string{"Para 4.\n"}},
+                       {opPre, []string{"pre\n", "pre1\n", "\n", "pre2\n"}},
+                       {opPara, []string{"Para 5.\n"}},
+                       {opPre, []string{"pre\n", "\n", "\n", "pre1\n", "pre2\n"}},
+                       {opPara, []string{"Para 6.\n"}},
                        {opPre, []string{"pre\n", "pre2\n"}},
                },
+               text: `.   Para 1. Para 1 line 2.
+
+.   Para 2.
+
+
+.   Section
+
+.   Para 3.
+
+$      pre
+$      pre1
+
+.   Para 4.
+
+$      pre
+$      pre1
+
+$      pre2
+
+.   Para 5.
+
+$      pre
+
+
+$      pre1
+$      pre2
+
+.   Para 6.
+
+$      pre
+$      pre2
+`,
        },
 }
 
@@ -83,14 +136,28 @@ func TestBlocks(t *testing.T) {
        }
 }
 
+func TestToText(t *testing.T) {
+       var buf bytes.Buffer
+       for i, tt := range blocksTests {
+               ToText(&buf, tt.in, ".   ", "$\t", 40)
+               if have := buf.String(); have != tt.text {
+                       t.Errorf("#%d: mismatch\nhave: %s\nwant: %s\nhave vs want:\n%q\n%q", i, have, tt.text, have, tt.text)
+               }
+               buf.Reset()
+       }
+}
+
 var emphasizeTests = []struct {
-       in  string
-       out string
+       in, out string
 }{
        {"http://www.google.com/", `<a href="http://www.google.com/">http://www.google.com/</a>`},
        {"https://www.google.com/", `<a href="https://www.google.com/">https://www.google.com/</a>`},
        {"http://www.google.com/path.", `<a href="http://www.google.com/path">http://www.google.com/path</a>.`},
+       {"http://en.wikipedia.org/wiki/Camellia_(cipher)", `<a href="http://en.wikipedia.org/wiki/Camellia_(cipher)">http://en.wikipedia.org/wiki/Camellia_(cipher)</a>`},
        {"(http://www.google.com/)", `(<a href="http://www.google.com/">http://www.google.com/</a>)`},
+       {"http://gmail.com)", `<a href="http://gmail.com">http://gmail.com</a>)`},
+       {"((http://gmail.com))", `((<a href="http://gmail.com">http://gmail.com</a>))`},
+       {"http://gmail.com ((http://gmail.com)) ()", `<a href="http://gmail.com">http://gmail.com</a> ((<a href="http://gmail.com">http://gmail.com</a>)) ()`},
        {"Foo bar http://example.com/ quux!", `Foo bar <a href="http://example.com/">http://example.com/</a> quux!`},
        {"Hello http://example.com/%2f/ /world.", `Hello <a href="http://example.com/%2f/">http://example.com/%2f/</a> /world.`},
        {"Lorem http: ipsum //host/path", "Lorem http: ipsum //host/path"},
@@ -107,3 +174,34 @@ func TestEmphasize(t *testing.T) {
                }
        }
 }
+
+var pairedParensPrefixLenTests = []struct {
+       in, out string
+}{
+       {"", ""},
+       {"foo", "foo"},
+       {"()", "()"},
+       {"foo()", "foo()"},
+       {"foo()()()", "foo()()()"},
+       {"foo()((()()))", "foo()((()()))"},
+       {"foo()((()()))bar", "foo()((()()))bar"},
+       {"foo)", "foo"},
+       {"foo))", "foo"},
+       {"foo)))))", "foo"},
+       {"(foo", ""},
+       {"((foo", ""},
+       {"(((((foo", ""},
+       {"(foo)", "(foo)"},
+       {"((((foo))))", "((((foo))))"},
+       {"foo()())", "foo()()"},
+       {"foo((()())", "foo"},
+       {"foo((()())) (() foo ", "foo((()())) "},
+}
+
+func TestPairedParensPrefixLen(t *testing.T) {
+       for i, tt := range pairedParensPrefixLenTests {
+               if out := tt.in[:pairedParensPrefixLen(tt.in)]; out != tt.out {
+                       t.Errorf("#%d: mismatch\nhave: %q\nwant: %q", i, out, tt.out)
+               }
+       }
+}
index f4ce9f654154247ed6e16eb3ace05593fe953880..c414e548ccd7524a1935d8b10d97418f3a28b025 100644 (file)
@@ -255,7 +255,7 @@ func playExample(file *ast.File, body *ast.BlockStmt) *ast.File {
                }
        }
 
-       // Strip "Output:" commment and adjust body end position.
+       // Strip "Output:" comment and adjust body end position.
        body, comments = stripOutputComment(body, comments)
 
        // Synthesize import declaration.
@@ -318,7 +318,7 @@ func playExampleFile(file *ast.File) *ast.File {
        return &f
 }
 
-// stripOutputComment finds and removes an "Output:" commment from body
+// stripOutputComment finds and removes an "Output:" comment from body
 // and comments, and adjusts the body block's end position.
 func stripOutputComment(body *ast.BlockStmt, comments []*ast.CommentGroup) (*ast.BlockStmt, []*ast.CommentGroup) {
        // Do nothing if no "Output:" comment found.
index d4d4f909d3ffd909c5dfa36bae9f6c3e8c745d00..8506077cee626d7464b0b8deb115fc0d1e39e12f 100644 (file)
@@ -59,8 +59,11 @@ func getPos(filename string, offset int) token.Pos {
 
 // ERROR comments must be of the form /* ERROR "rx" */ and rx is
 // a regular expression that matches the expected error message.
+// The special form /* ERROR HERE "rx" */ must be used for error
+// messages that appear immediately after a token, rather than at
+// a token's position.
 //
-var errRx = regexp.MustCompile(`^/\* *ERROR *"([^"]*)" *\*/$`)
+var errRx = regexp.MustCompile(`^/\* *ERROR *(HERE)? *"([^"]*)" *\*/$`)
 
 // expectedErrors collects the regular expressions of ERROR comments found
 // in files and returns them as a map of error positions to error messages.
@@ -74,6 +77,7 @@ func expectedErrors(t *testing.T, filename string, src []byte) map[token.Pos]str
        // not match the position information collected by the parser
        s.Init(getFile(filename), src, nil, scanner.ScanComments)
        var prev token.Pos // position of last non-comment, non-semicolon token
+       var here token.Pos // position immediately after the token at position prev
 
        for {
                pos, tok, lit := s.Scan()
@@ -82,11 +86,22 @@ func expectedErrors(t *testing.T, filename string, src []byte) map[token.Pos]str
                        return errors
                case token.COMMENT:
                        s := errRx.FindStringSubmatch(lit)
-                       if len(s) == 2 {
-                               errors[prev] = string(s[1])
+                       if len(s) == 3 {
+                               pos := prev
+                               if s[1] == "HERE" {
+                                       pos = here
+                               }
+                               errors[pos] = string(s[2])
                        }
                default:
                        prev = pos
+                       var l int // token length
+                       if tok.IsLiteral() {
+                               l = len(lit)
+                       } else {
+                               l = len(tok.String())
+                       }
+                       here = prev + token.Pos(l)
                }
        }
 }
index 0f83ca9314362f3d6cd09e86a0931bc0b7dcbb9b..57da4ddcd9302352c168c005799cb75e4f75a677 100644 (file)
@@ -182,6 +182,13 @@ func ParseExpr(x string) (ast.Expr, error) {
        p.closeScope()
        assert(p.topScope == nil, "unbalanced scopes")
 
+       // If a semicolon was inserted, consume it;
+       // report an error if there's more tokens.
+       if p.tok == token.SEMICOLON {
+               p.next()
+       }
+       p.expect(token.EOF)
+
        if p.errors.Len() > 0 {
                p.errors.Sort()
                return nil, p.errors.Err()
index c3e3ee859ab2f623c47634e0c5296b84054e3198..00dd532b2395e86e45fb8f24f4563e455d561a4a 100644 (file)
@@ -492,6 +492,26 @@ func syncDecl(p *parser) {
        }
 }
 
+// safePos returns a valid file position for a given position: If pos
+// is valid to begin with, safePos returns pos. If pos is out-of-range,
+// safePos returns the EOF position.
+//
+// This is hack to work around "artificial" end positions in the AST which
+// are computed by adding 1 to (presumably valid) token positions. If the
+// token positions are invalid due to parse errors, the resulting end position
+// may be past the file's EOF position, which would lead to panics if used
+// later on.
+//
+func (p *parser) safePos(pos token.Pos) (res token.Pos) {
+       defer func() {
+               if recover() != nil {
+                       res = token.Pos(p.file.Base() + p.file.Size()) // EOF position
+               }
+       }()
+       _ = p.file.Offset(pos) // trigger a panic if position is out-of-range
+       return pos
+}
+
 // ----------------------------------------------------------------------------
 // Identifiers
 
@@ -679,7 +699,7 @@ func (p *parser) parseFieldDecl(scope *ast.Scope) *ast.Field {
                if n := len(list); n > 1 || !isTypeName(deref(typ)) {
                        pos := typ.Pos()
                        p.errorExpected(pos, "anonymous field")
-                       typ = &ast.BadExpr{From: pos, To: list[n-1].End()}
+                       typ = &ast.BadExpr{From: pos, To: p.safePos(list[n-1].End())}
                }
        }
 
@@ -1337,7 +1357,7 @@ func (p *parser) checkExpr(x ast.Expr) ast.Expr {
        default:
                // all other nodes are not proper expressions
                p.errorExpected(x.Pos(), "expression")
-               x = &ast.BadExpr{From: x.Pos(), To: x.End()}
+               x = &ast.BadExpr{From: x.Pos(), To: p.safePos(x.End())}
        }
        return x
 }
@@ -1400,7 +1420,7 @@ func (p *parser) checkExprOrType(x ast.Expr) ast.Expr {
        case *ast.ArrayType:
                if len, isEllipsis := t.Len.(*ast.Ellipsis); isEllipsis {
                        p.error(len.Pos(), "expected array length, found '...'")
-                       x = &ast.BadExpr{From: x.Pos(), To: x.End()}
+                       x = &ast.BadExpr{From: x.Pos(), To: p.safePos(x.End())}
                }
        }
 
@@ -1686,14 +1706,14 @@ func (p *parser) parseSimpleStmt(mode int) (ast.Stmt, bool) {
        return &ast.ExprStmt{X: x[0]}, false
 }
 
-func (p *parser) parseCallExpr() *ast.CallExpr {
+func (p *parser) parseCallExpr(callType string) *ast.CallExpr {
        x := p.parseRhsOrType() // could be a conversion: (some type)(x)
        if call, isCall := x.(*ast.CallExpr); isCall {
                return call
        }
        if _, isBad := x.(*ast.BadExpr); !isBad {
                // only report error if it's a new one
-               p.errorExpected(x.Pos(), "function/method call")
+               p.error(p.safePos(x.End()), fmt.Sprintf("function must be invoked in %s statement", callType))
        }
        return nil
 }
@@ -1704,7 +1724,7 @@ func (p *parser) parseGoStmt() ast.Stmt {
        }
 
        pos := p.expect(token.GO)
-       call := p.parseCallExpr()
+       call := p.parseCallExpr("go")
        p.expectSemi()
        if call == nil {
                return &ast.BadStmt{From: pos, To: pos + 2} // len("go")
@@ -1719,7 +1739,7 @@ func (p *parser) parseDeferStmt() ast.Stmt {
        }
 
        pos := p.expect(token.DEFER)
-       call := p.parseCallExpr()
+       call := p.parseCallExpr("defer")
        p.expectSemi()
        if call == nil {
                return &ast.BadStmt{From: pos, To: pos + 5} // len("defer")
@@ -1770,7 +1790,7 @@ func (p *parser) makeExpr(s ast.Stmt, kind string) ast.Expr {
                return p.checkExpr(es.X)
        }
        p.error(s.Pos(), fmt.Sprintf("expected %s, found simple statement (missing parentheses around composite literal?)", kind))
-       return &ast.BadExpr{From: s.Pos(), To: s.End()}
+       return &ast.BadExpr{From: s.Pos(), To: p.safePos(s.End())}
 }
 
 func (p *parser) parseIfStmt() *ast.IfStmt {
@@ -2052,7 +2072,7 @@ func (p *parser) parseForStmt() ast.Stmt {
                        key = as.Lhs[0]
                default:
                        p.errorExpected(as.Lhs[0].Pos(), "1 or 2 expressions")
-                       return &ast.BadStmt{From: pos, To: body.End()}
+                       return &ast.BadStmt{From: pos, To: p.safePos(body.End())}
                }
                // parseSimpleStmt returned a right-hand side that
                // is a single unary expression of the form "range x"
@@ -2299,7 +2319,7 @@ func (p *parser) parseReceiver(scope *ast.Scope) *ast.FieldList {
                        p.errorExpected(base.Pos(), "(unqualified) identifier")
                }
                par.List = []*ast.Field{
-                       {Type: &ast.BadExpr{From: recv.Pos(), To: recv.End()}},
+                       {Type: &ast.BadExpr{From: recv.Pos(), To: p.safePos(recv.End())}},
                }
        }
 
index 0a34b7e505e9b3277b9872ff0d54bdc0097e3187..2797ea518bd0e23c23aaf021444d242286da6865 100644 (file)
@@ -78,7 +78,7 @@ func TestParseExpr(t *testing.T) {
        }
        // sanity check
        if _, ok := x.(*ast.BinaryExpr); !ok {
-               t.Errorf("ParseExpr(%s): got %T, expected *ast.BinaryExpr", src, x)
+               t.Errorf("ParseExpr(%s): got %T, want *ast.BinaryExpr", src, x)
        }
 
        // a valid type expression
@@ -89,17 +89,24 @@ func TestParseExpr(t *testing.T) {
        }
        // sanity check
        if _, ok := x.(*ast.StructType); !ok {
-               t.Errorf("ParseExpr(%s): got %T, expected *ast.StructType", src, x)
+               t.Errorf("ParseExpr(%s): got %T, want *ast.StructType", src, x)
        }
 
        // an invalid expression
        src = "a + *"
        _, err = ParseExpr(src)
        if err == nil {
-               t.Fatalf("ParseExpr(%s): %v", src, err)
+               t.Fatalf("ParseExpr(%s): got no error", src)
+       }
+
+       // a valid expression followed by extra tokens is invalid
+       src = "a[i] := x"
+       _, err = ParseExpr(src)
+       if err == nil {
+               t.Fatalf("ParseExpr(%s): got no error", src)
        }
 
-       // it must not crash
+       // ParseExpr must not crash
        for _, src := range valids {
                ParseExpr(src)
        }
index 22f79930b372ef46b856b28912b579d54eceb532..b794060998f66de3d7b9ac639a06fcf0d101e3a1 100644 (file)
@@ -86,6 +86,9 @@ var invalids = []string{
        `package p; func f() { for x /* ERROR "boolean or range expression" */ := []string {} }`,
        `package p; func f() { for i /* ERROR "boolean or range expression" */ , x = []string {} }`,
        `package p; func f() { for i /* ERROR "boolean or range expression" */ , x := []string {} }`,
+       `package p; func f() { go f /* ERROR HERE "function must be invoked" */ }`,
+       `package p; func f() { defer func() {} /* ERROR HERE "function must be invoked" */ }`,
+       `package p; func f() { go func() { func() { f(x func /* ERROR "expected '\)'" */ (){}) } } }`,
 }
 
 func TestInvalid(t *testing.T) {
index 583c6c37090b60d2271f1c158dc742bb8bb13523..04b5f1a76a93eb1c7b9e09489e6646666776fd3b 100644 (file)
@@ -378,10 +378,6 @@ func (p *printer) setLineComment(text string) {
        p.setComment(&ast.CommentGroup{List: []*ast.Comment{{Slash: token.NoPos, Text: text}}})
 }
 
-func (p *printer) isMultiLine(n ast.Node) bool {
-       return p.lineFor(n.End())-p.lineFor(n.Pos()) > 0
-}
-
 func (p *printer) fieldList(fields *ast.FieldList, isStruct, isIncomplete bool) {
        lbrace := fields.Opening
        list := fields.List
@@ -428,13 +424,14 @@ func (p *printer) fieldList(fields *ast.FieldList, isStruct, isIncomplete bool)
                if len(list) == 1 {
                        sep = blank
                }
-               newSection := false
+               var line int
                for i, f := range list {
                        if i > 0 {
-                               p.linebreak(p.lineFor(f.Pos()), 1, ignore, newSection)
+                               p.linebreak(p.lineFor(f.Pos()), 1, ignore, p.linesFrom(line) > 0)
                        }
                        extraTabs := 0
                        p.setComment(f.Doc)
+                       p.recordLine(&line)
                        if len(f.Names) > 0 {
                                // named fields
                                p.identList(f.Names, false)
@@ -460,7 +457,6 @@ func (p *printer) fieldList(fields *ast.FieldList, isStruct, isIncomplete bool)
                                }
                                p.setComment(f.Comment)
                        }
-                       newSection = p.isMultiLine(f)
                }
                if isIncomplete {
                        if len(list) > 0 {
@@ -472,12 +468,13 @@ func (p *printer) fieldList(fields *ast.FieldList, isStruct, isIncomplete bool)
 
        } else { // interface
 
-               newSection := false
+               var line int
                for i, f := range list {
                        if i > 0 {
-                               p.linebreak(p.lineFor(f.Pos()), 1, ignore, newSection)
+                               p.linebreak(p.lineFor(f.Pos()), 1, ignore, p.linesFrom(line) > 0)
                        }
                        p.setComment(f.Doc)
+                       p.recordLine(&line)
                        if ftyp, isFtyp := f.Type.(*ast.FuncType); isFtyp {
                                // method
                                p.expr(f.Names[0])
@@ -487,7 +484,6 @@ func (p *printer) fieldList(fields *ast.FieldList, isStruct, isIncomplete bool)
                                p.expr(f.Type)
                        }
                        p.setComment(f.Comment)
-                       newSection = p.isMultiLine(f)
                }
                if isIncomplete {
                        if len(list) > 0 {
@@ -826,10 +822,16 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int) {
                }
                p.print(x.Lbrace, token.LBRACE)
                p.exprList(x.Lbrace, x.Elts, 1, commaTerm, x.Rbrace)
-               // do not insert extra line breaks because of comments before
-               // the closing '}' as it might break the code if there is no
-               // trailing ','
-               p.print(noExtraLinebreak, x.Rbrace, token.RBRACE, noExtraLinebreak)
+               // do not insert extra line break following a /*-style comment
+               // before the closing '}' as it might break the code if there
+               // is no trailing ','
+               mode := noExtraLinebreak
+               // do not insert extra blank following a /*-style comment
+               // before the closing '}' unless the literal is empty
+               if len(x.Elts) > 0 {
+                       mode |= noExtraBlank
+               }
+               p.print(mode, x.Rbrace, token.RBRACE, mode)
 
        case *ast.Ellipsis:
                p.print(token.ELLIPSIS)
@@ -901,20 +903,31 @@ func (p *printer) stmtList(list []ast.Stmt, nindent int, nextIsRBrace bool) {
        if nindent > 0 {
                p.print(indent)
        }
-       multiLine := false
+       var line int
        i := 0
        for _, s := range list {
                // ignore empty statements (was issue 3466)
                if _, isEmpty := s.(*ast.EmptyStmt); !isEmpty {
-                       // _indent == 0 only for lists of switch/select case clauses;
+                       // nindent == 0 only for lists of switch/select case clauses;
                        // in those cases each clause is a new section
                        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.linebreak(p.lineFor(s.Pos()), 1, ignore, i == 0 || nindent == 0 || p.linesFrom(line) > 0)
                        }
+                       p.recordLine(&line)
                        p.stmt(s, nextIsRBrace && i == len(list)-1)
-                       multiLine = p.isMultiLine(s)
+                       // labeled statements put labels on a separate line, but here
+                       // we only care about the start line of the actual statement
+                       // without label - correct line for each label
+                       for t := s; ; {
+                               lt, _ := t.(*ast.LabeledStmt)
+                               if lt == nil {
+                                       break
+                               }
+                               line++
+                               t = lt.Stmt
+                       }
                        i++
                }
        }
@@ -1375,22 +1388,22 @@ func (p *printer) genDecl(d *ast.GenDecl) {
                                // two or more grouped const/var declarations:
                                // determine if the type column must be kept
                                keepType := keepTypeColumn(d.Specs)
-                               newSection := false
+                               var line int
                                for i, s := range d.Specs {
                                        if i > 0 {
-                                               p.linebreak(p.lineFor(s.Pos()), 1, ignore, newSection)
+                                               p.linebreak(p.lineFor(s.Pos()), 1, ignore, p.linesFrom(line) > 0)
                                        }
+                                       p.recordLine(&line)
                                        p.valueSpec(s.(*ast.ValueSpec), keepType[i])
-                                       newSection = p.isMultiLine(s)
                                }
                        } else {
-                               newSection := false
+                               var line int
                                for i, s := range d.Specs {
                                        if i > 0 {
-                                               p.linebreak(p.lineFor(s.Pos()), 1, ignore, newSection)
+                                               p.linebreak(p.lineFor(s.Pos()), 1, ignore, p.linesFrom(line) > 0)
                                        }
+                                       p.recordLine(&line)
                                        p.spec(s, n, false)
-                                       newSection = p.isMultiLine(s)
                                }
                        }
                        p.print(unindent, formfeed)
@@ -1448,13 +1461,16 @@ func (p *printer) bodySize(b *ast.BlockStmt, maxSize int) int {
                // opening and closing brace are on different lines - don't make it a one-liner
                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
+       if len(b.List) > 5 {
+               // too many statements - don't make it a one-liner
                return maxSize + 1
        }
        // otherwise, estimate body size
-       bodySize := 0
+       bodySize := p.commentSizeBefore(p.posFor(pos2))
        for i, s := range b.List {
+               if bodySize > maxSize {
+                       break // no need to continue
+               }
                if i > 0 {
                        bodySize += 2 // space for a semicolon and blank
                }
@@ -1488,7 +1504,7 @@ func (p *printer) adjBlock(headerSize int, sep whiteSpace, b *ast.BlockStmt) {
                        }
                        p.print(blank)
                }
-               p.print(b.Rbrace, token.RBRACE)
+               p.print(noExtraLinebreak, b.Rbrace, token.RBRACE, noExtraLinebreak)
                return
        }
 
index e06d2edfb21c2e8da51d07c2c9f572c21a99c3e3..280c697a0dd7caced68425182c1c11a4da0c9074 100644 (file)
@@ -39,9 +39,17 @@ const (
 type pmode int
 
 const (
-       noExtraLinebreak pmode = 1 << iota
+       noExtraBlank     pmode = 1 << iota // disables extra blank after /*-style comment
+       noExtraLinebreak                   // disables extra line break after /*-style comment
 )
 
+type commentInfo struct {
+       cindex         int               // current comment index
+       comment        *ast.CommentGroup // = printer.comments[cindex]; or nil
+       commentOffset  int               // = printer.posFor(printer.comments[cindex].List[0].Pos()).Offset; or infinity
+       commentNewline bool              // true if the comment group contains newlines
+}
+
 type printer struct {
        // Configuration (does not change after initialization)
        Config
@@ -52,7 +60,8 @@ type printer struct {
        indent      int          // current indentation
        mode        pmode        // current printer mode
        impliedSemi bool         // if set, a linebreak implies a semicolon
-       lastTok     token.Token  // the last token printed (token.ILLEGAL if it's whitespace)
+       lastTok     token.Token  // last token printed (token.ILLEGAL if it's whitespace)
+       prevOpen    token.Token  // previous non-brace "open" token (, [, or token.ILLEGAL
        wsbuf       []whiteSpace // delayed white space
 
        // Positions
@@ -61,19 +70,17 @@ type printer struct {
        // white space). If there's a difference and SourcePos is set in
        // ConfigMode, //line comments are used in the output to restore
        // original source positions for a reader.
-       pos  token.Position // current position in AST (source) space
-       out  token.Position // current position in output space
-       last token.Position // value of pos after calling writeString
+       pos     token.Position // current position in AST (source) space
+       out     token.Position // current position in output space
+       last    token.Position // value of pos after calling writeString
+       linePtr *int           // if set, record out.Line for the next token in *linePtr
 
        // The list of all source comments, in order of appearance.
        comments        []*ast.CommentGroup // may be nil
-       cindex          int                 // current comment index
        useNodeComments bool                // if not set, ignore lead and line comments of nodes
 
        // Information about p.comments[p.cindex]; set up by nextComment.
-       comment        *ast.CommentGroup // = p.comments[p.cindex]; or nil
-       commentOffset  int               // = p.posFor(p.comments[p.cindex].List[0].Pos()).Offset; or infinity
-       commentNewline bool              // true if the comment group contains newlines
+       commentInfo
 
        // Cache of already computed node sizes.
        nodeSizes map[ast.Node]int
@@ -93,6 +100,14 @@ func (p *printer) init(cfg *Config, fset *token.FileSet, nodeSizes map[ast.Node]
        p.cachedPos = -1
 }
 
+func (p *printer) internalError(msg ...interface{}) {
+       if debug {
+               fmt.Print(p.pos.String() + ": ")
+               fmt.Println(msg...)
+               panic("go/printer")
+       }
+}
+
 // commentsHaveNewline reports whether a list of comments belonging to
 // an *ast.CommentGroup contains newlines. Because the position information
 // may only be partially correct, we also have to read the comment text.
@@ -129,12 +144,49 @@ func (p *printer) nextComment() {
        p.commentOffset = infinity
 }
 
-func (p *printer) internalError(msg ...interface{}) {
-       if debug {
-               fmt.Print(p.pos.String() + ": ")
-               fmt.Println(msg...)
-               panic("go/printer")
+// commentBefore returns true iff the current comment group occurs
+// before the next position in the source code and printing it does
+// not introduce implicit semicolons.
+//
+func (p *printer) commentBefore(next token.Position) bool {
+       return p.commentOffset < next.Offset && (!p.impliedSemi || !p.commentNewline)
+}
+
+// commentSizeBefore returns the estimated size of the
+// comments on the same line before the next position.
+//
+func (p *printer) commentSizeBefore(next token.Position) int {
+       // save/restore current p.commentInfo (p.nextComment() modifies it)
+       defer func(info commentInfo) {
+               p.commentInfo = info
+       }(p.commentInfo)
+
+       size := 0
+       for p.commentBefore(next) {
+               for _, c := range p.comment.List {
+                       size += len(c.Text)
+               }
+               p.nextComment()
        }
+       return size
+}
+
+// recordLine records the output line number for the next non-whitespace
+// token in *linePtr. It is used to compute an accurate line number for a
+// formatted construct, independent of pending (not yet emitted) whitespace
+// or comments.
+//
+func (p *printer) recordLine(linePtr *int) {
+       p.linePtr = linePtr
+}
+
+// linesFrom returns the number of output lines between the current
+// output line and the line argument, ignoring any pending (not yet
+// emitted) whitespace or comments. It is used to compute an accurate
+// size (in number of lines) for a formatted construct.
+//
+func (p *printer) linesFrom(line int) int {
+       return p.out.Line - line
 }
 
 func (p *printer) posFor(pos token.Pos) token.Position {
@@ -675,10 +727,14 @@ func (p *printer) intersperseComments(next token.Position, tok token.Token) (wro
 
        if last != nil {
                // if the last comment is a /*-style comment and the next item
-               // follows on the same line but is not a comma or a "closing"
-               // token, add an extra blank for separation
-               if last.Text[1] == '*' && p.lineFor(last.Pos()) == next.Line && tok != token.COMMA &&
-                       tok != token.RPAREN && tok != token.RBRACK && tok != token.RBRACE {
+               // follows on the same line but is not a comma, and not a "closing"
+               // token immediately following its corresponding "opening" token,
+               // add an extra blank for separation unless explicitly disabled
+               if p.mode&noExtraBlank == 0 &&
+                       last.Text[1] == '*' && p.lineFor(last.Pos()) == next.Line &&
+                       tok != token.COMMA &&
+                       (tok != token.RPAREN || p.prevOpen == token.LPAREN) &&
+                       (tok != token.RBRACK || p.prevOpen == token.LBRACK) {
                        p.writeByte(' ', 1)
                }
                // ensure that there is a line break after a //-style comment,
@@ -735,12 +791,8 @@ func (p *printer) writeWhitespace(n int) {
        }
 
        // shift remaining entries down
-       i := 0
-       for ; n < len(p.wsbuf); n++ {
-               p.wsbuf[i] = p.wsbuf[n]
-               i++
-       }
-       p.wsbuf = p.wsbuf[0:i]
+       l := copy(p.wsbuf, p.wsbuf[n:])
+       p.wsbuf = p.wsbuf[:l]
 }
 
 // ----------------------------------------------------------------------------
@@ -790,6 +842,17 @@ func (p *printer) print(args ...interface{}) {
                var isLit bool
                var impliedSemi bool // value for p.impliedSemi after this arg
 
+               // record previous opening token, if any
+               switch p.lastTok {
+               case token.ILLEGAL:
+                       // ignore (white space)
+               case token.LPAREN, token.LBRACK:
+                       p.prevOpen = p.lastTok
+               default:
+                       // other tokens followed any opening token
+                       p.prevOpen = token.ILLEGAL
+               }
+
                switch x := arg.(type) {
                case pmode:
                        // toggle printer mode
@@ -899,19 +962,17 @@ func (p *printer) print(args ...interface{}) {
                        }
                }
 
+               // the next token starts now - record its line number if requested
+               if p.linePtr != nil {
+                       *p.linePtr = p.out.Line
+                       p.linePtr = nil
+               }
+
                p.writeString(next, data, isLit)
                p.impliedSemi = impliedSemi
        }
 }
 
-// commentBefore returns true iff the current comment group occurs
-// before the next position in the source code and printing it does
-// not introduce implicit semicolons.
-//
-func (p *printer) commentBefore(next token.Position) (result bool) {
-       return p.commentOffset < next.Offset && (!p.impliedSemi || !p.commentNewline)
-}
-
 // flush prints any pending comments and whitespace occurring textually
 // before the position of the next token tok. The flush result indicates
 // if a newline was written or if a formfeed was dropped from the whitespace
index 8454ac12b9e7e45afa45d52ef0b25dd782d5d83e..306928a69a26af9691dc2bcbe281263ecd3f1760 100644 (file)
@@ -63,7 +63,7 @@ func format(src []byte, mode checkMode) ([]byte, error) {
                return nil, fmt.Errorf("print: %s", err)
        }
 
-       // make sure formated output is syntactically correct
+       // make sure formatted output is syntactically correct
        res := buf.Bytes()
        if _, err := parser.ParseFile(fset, "", res, 0); err != nil {
                return nil, fmt.Errorf("re-parse: %s\n%s", err, buf.Bytes())
@@ -179,7 +179,7 @@ func check(t *testing.T, source, golden string, mode checkMode) {
                // test running past time out
                t.Errorf("%s: running too slowly", source)
        case <-cc:
-               // test finished within alloted time margin
+               // test finished within allotted time margin
        }
 }
 
@@ -212,7 +212,7 @@ func TestFiles(t *testing.T) {
        }
 }
 
-// TestLineComments, using a simple test case, checks that consequtive line
+// TestLineComments, using a simple test case, checks that consecutive line
 // comments are properly terminated with a newline even if the AST position
 // information is incorrect.
 //
index 610a42a68bda5bf1b9ea7258404c9a11b5d114a7..b1af7958a96fd93e12ea9b3b181006011030dba5 100644 (file)
@@ -494,16 +494,21 @@ func _() {
 func _( /* this */ x /* is */ /* an */ int) {
 }
 
-func _( /* no params */)       {}
+func _( /* no params - extra blank before and after comment */ )       {}
+func _(a, b int /* params - no extra blank after comment */)           {}
+
+func _()       { f( /* no args - extra blank before and after comment */ ) }
+func _()       { f(a, b /* args - no extra blank after comment */) }
 
 func _() {
-       f( /* no args */)
+       f( /* no args - extra blank before and after comment */ )
+       f(a, b /* args - no extra blank after comment */)
 }
 
 func ( /* comment1 */ T /* comment2 */) _()    {}
 
-func _() { /* one-line functions with comments are formatted as multi-line functions */
-}
+func _()       { /* "short-ish one-line functions with comments are formatted as multi-line functions */ }
+func _()       { x := 0; /* comment */ y = x /* comment */ }
 
 func _() {
        _ = 0
index d121dd4be77d706162dad3db38132247a85d3aef..983e2b2c97e25a54bd0b96ba1d7e7a05985ebeb0 100644 (file)
@@ -500,15 +500,21 @@ func _() {
 func _(/* this */x/* is *//* an */ int) {
 }
 
-func _(/* no params */) {}
+func _(/* no params - extra blank before and after comment */) {}
+func _(a, b int /* params - no extra blank after comment */) {}
+
+func _() { f(/* no args - extra blank before and after comment */) }
+func _() { f(a, b /* args - no extra blank after comment */) }
 
 func _() {
-       f(/* no args */)
+       f(/* no args - extra blank before and after comment */)
+       f(a, b /* args - no extra blank after comment */)
 }
 
 func (/* comment1 */ T /* comment2 */) _() {}
 
-func _() { /* one-line functions with comments are formatted as multi-line functions */ }
+func _() { /* "short-ish one-line functions with comments are formatted as multi-line functions */ }
+func _() { x := 0; /* comment */ y = x /* comment */ }
 
 func _() {
        _ = 0
index d3b50bf3e05a44bc1593e06009bc04add3655e32..7676a26c1259eec0a56ca1a3c2930bc593f86de6 100644 (file)
@@ -77,3 +77,29 @@ func main() {
                println("test")
        }
 }
+
+func issue5623() {
+L:
+       _ = yyyyyyyyyyyyyyyy                    // comment - should be aligned
+       _ = xxxxxxxxxxxxxxxxxxxxxxxxxxxx        /* comment */
+
+       _ = yyyyyyyyyyyyyyyy                    /* comment - should be aligned */
+       _ = xxxxxxxxxxxxxxxxxxxxxxxxxxxx        // comment
+
+LLLLLLL:
+       _ = yyyyyyyyyyyyyyyy                    // comment - should be aligned
+       _ = xxxxxxxxxxxxxxxxxxxxxxxxxxxx        // comment
+
+LL:
+LLLLL:
+       _ = xxxxxxxxxxxxxxxxxxxxxxxxxxxx        /* comment */
+       _ = yyyyyyyyyyyyyyyy                    /* comment - should be aligned */
+
+       _ = xxxxxxxxxxxxxxxxxxxxxxxxxxxx        // comment
+       _ = yyyyyyyyyyyyyyyy                    // comment - should be aligned
+
+       // test case from issue
+label:
+       mask := uint64(1)<<c - 1                // Allocation mask
+       used := atomic.LoadUint64(&h.used)      // Current allocations
+}
index 6f8c85c94aad4bdfa2c48e557b7cf14b3d92146f..4a055c82772886a0b2ea165d7b2abc987e458235 100644 (file)
@@ -76,4 +76,30 @@ prints test 5 times
    for i := 0; i < 5; i++ {
       println("test")
    }
-}
\ No newline at end of file
+}
+
+func issue5623() {
+L:
+   _ = yyyyyyyyyyyyyyyy // comment - should be aligned
+   _ = xxxxxxxxxxxxxxxxxxxxxxxxxxxx /* comment */
+
+   _ = yyyyyyyyyyyyyyyy /* comment - should be aligned */
+   _ = xxxxxxxxxxxxxxxxxxxxxxxxxxxx // comment
+
+LLLLLLL:
+   _ = yyyyyyyyyyyyyyyy // comment - should be aligned
+   _ = xxxxxxxxxxxxxxxxxxxxxxxxxxxx // comment
+
+LL:
+LLLLL:
+   _ = xxxxxxxxxxxxxxxxxxxxxxxxxxxx /* comment */
+   _ = yyyyyyyyyyyyyyyy /* comment - should be aligned */
+
+   _ = xxxxxxxxxxxxxxxxxxxxxxxxxxxx // comment
+   _ = yyyyyyyyyyyyyyyy // comment - should be aligned
+
+// test case from issue
+label:
+   mask := uint64(1)<<c - 1 // Allocation mask
+   used := atomic.LoadUint64(&h.used) // Current allocations
+}
index 0331615e5195bc99e7dd50d2fd6d9f9acab90157..a27f21fc8cef86f9ee788762d1f21cc52949ea19 100644 (file)
@@ -397,6 +397,21 @@ func _() {
        }
 }
 
+// use the formatted output rather than the input to decide when to align
+// (was issue 4505)
+const (
+       short           = 2 * (1 + 2)
+       aMuchLongerName = 3
+)
+
+var (
+       short           = X{}
+       aMuchLongerName = X{}
+
+       x1      = X{}   // foo
+       x2      = X{}   // foo
+)
+
 func _() {
        type (
                xxxxxx  int
@@ -723,7 +738,8 @@ func _() int {
 }
 
 // making function declarations safe for new semicolon rules
-func _() { /* multi-line func because of comment */
+func _()       { /* single-line function because of "short-ish" comment */ }
+func _() { /* multi-line function because of "long-ish" comment - much more comment text is following here */ /* and more */
 }
 
 func _() {
index dbdbdfe7422db109a99c991a19e25ea2db256eba..d9951d3865fe4ccb7e42cf4855c7def9860303a3 100644 (file)
@@ -409,6 +409,24 @@ func _() {
        }
 }
 
+// use the formatted output rather than the input to decide when to align
+// (was issue 4505)
+const (
+       short = 2 * (
+       1 + 2)
+       aMuchLongerName = 3
+)
+
+var (
+       short = X{
+       }
+       aMuchLongerName = X{}
+
+       x1 = X{} // foo
+       x2 = X{
+       } // foo
+)
+
 func _() {
        type (
                xxxxxx int
@@ -737,7 +755,8 @@ func _() int {
 
 
 // making function declarations safe for new semicolon rules
-func _() { /* multi-line func because of comment */ }
+func _() { /* single-line function because of "short-ish" comment */ }
+func _() { /* multi-line function because of "long-ish" comment - much more comment text is following here */ /* and more */ }
 
 func _() {
 /* multi-line func because block is on multiple lines */ }
index 25588ba3b0c0bb166dd518a2adbdb2c5df729501..cec82ea10efa52bde92a290062f57cfeb0418974 100644 (file)
@@ -148,11 +148,14 @@ func (s *Scanner) interpretLineComment(text []byte) {
                // get filename and line number, if any
                if i := bytes.LastIndex(text, []byte{':'}); i > 0 {
                        if line, err := strconv.Atoi(string(text[i+1:])); err == nil && line > 0 {
-                               // valid //line filename:line comment;
-                               filename := filepath.Clean(string(text[len(prefix):i]))
-                               if !filepath.IsAbs(filename) {
-                                       // make filename relative to current directory
-                                       filename = filepath.Join(s.dir, filename)
+                               // valid //line filename:line comment
+                               filename := string(bytes.TrimSpace(text[len(prefix):i]))
+                               if filename != "" {
+                                       filename = filepath.Clean(filename)
+                                       if !filepath.IsAbs(filename) {
+                                               // make filename relative to current directory
+                                               filename = filepath.Join(s.dir, filename)
+                                       }
                                }
                                // update scanner position
                                s.file.AddLineInfo(s.lineOffset+len(text)+1, filename, line) // +len(text)+1 since comment applies to next line
index e0d0b54f68e6d73d11cf7b05bbc290982613d5b3..fc450d8a6eb0db6dd854f116c3800d5ccfcc7ffa 100644 (file)
@@ -493,9 +493,9 @@ var segments = []segment{
        {"\nline3  //line File1.go:100", filepath.Join("dir", "TestLineComments"), 3}, // bad line comment, ignored
        {"\nline4", filepath.Join("dir", "TestLineComments"), 4},
        {"\n//line File1.go:100\n  line100", filepath.Join("dir", "File1.go"), 100},
+       {"\n//line  \t :42\n  line1", "", 42},
        {"\n//line File2.go:200\n  line200", filepath.Join("dir", "File2.go"), 200},
-       {"\n//line :1\n  line1", "dir", 1},
-       {"\n//line foo:42\n  line42", filepath.Join("dir", "foo"), 42},
+       {"\n//line foo\t:42\n  line42", filepath.Join("dir", "foo"), 42},
        {"\n //line foo:42\n  line44", filepath.Join("dir", "foo"), 44},           // bad line comment, ignored
        {"\n//line foo 42\n  line46", filepath.Join("dir", "foo"), 46},            // bad line comment, ignored
        {"\n//line foo:42 extra text\n  line48", filepath.Join("dir", "foo"), 48}, // bad line comment, ignored
similarity index 96%
rename from libgo/go/hash/crc32/crc32_amd64.go
rename to libgo/go/hash/crc32/crc32_amd64x.go
index b5bc6d3cf07df50da42a27afc27c27e576aaa3db..b7e359930a4ca59e2f3445f01a68bcad2cc984c7 100644 (file)
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// +build amd64 amd64p32
+
 package crc32
 
 // This file contains the code to call the SSE 4.2 version of the Castagnoli
index 3ea02880d45a67536083e7ff4b2c805234a358ac..d65d340073d8291edeed12ad9e4c2fa4e6af7db1 100644 (file)
@@ -90,7 +90,7 @@ var attrTypeMap = map[string]contentType{
        "name":            contentTypePlain,
        "novalidate":      contentTypeUnsafe,
        // Skip handler names from
-       // http://www.w3.org/TR/html5/Overview.html#event-handlers-on-elements-document-objects-and-window-objects
+       // http://www.w3.org/TR/html5/webappapis.html#event-handlers-on-elements,-document-objects,-and-window-objects
        // since we have special handling in attrType.
        "open":        contentTypePlain,
        "optimum":     contentTypePlain,
@@ -160,7 +160,7 @@ func attrType(name string) contentType {
 
        // Heuristics to prevent "javascript:..." injection in custom
        // data attributes and custom attributes like g:tweetUrl.
-       // http://www.w3.org/TR/html5/elements.html#embedding-custom-non-visible-data-with-the-data-attributes:
+       // http://www.w3.org/TR/html5/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes
        // "Custom data attributes are intended to store custom data
        //  private to the page or application, for which there are no
        //  more appropriate attributes or elements."
index 41b1116a661cb0d81022722b3a8553fbbec9576c..3715ed5c93805f8dbb8222be2e5142154a3872a5 100644 (file)
@@ -16,7 +16,8 @@ type (
        //   2. The CSS3 rule production, such as `a[href=~"https:"].foo#bar`.
        //   3. CSS3 declaration productions, such as `color: red; margin: 2px`.
        //   4. The CSS3 value production, such as `rgba(0, 0, 255, 127)`.
-       // See http://www.w3.org/TR/css3-syntax/#style
+       // See http://www.w3.org/TR/css3-syntax/#parsing and
+       // https://web.archive.org/web/20090211114933/http://w3.org/TR/css3-syntax#style
        CSS string
 
        // HTML encapsulates a known safe HTML document fragment.
index eb47e2be3c79e3d932bf694d6d3d74588a6c1580..59e794d6861f058cd6fa4a98e40c23b6c9657356 100644 (file)
@@ -13,7 +13,7 @@ import (
 //
 // The zero value of type context is the start context for a template that
 // produces an HTML fragment as defined at
-// http://www.w3.org/TR/html5/the-end.html#parsing-html-fragments
+// http://www.w3.org/TR/html5/syntax.html#the-end
 // where the context element is null.
 type context struct {
        state   state
@@ -96,7 +96,7 @@ const (
        // stateHTMLCmt occurs inside an <!-- HTML comment -->.
        stateHTMLCmt
        // stateRCDATA occurs inside an RCDATA element (<textarea> or <title>)
-       // as described at http://dev.w3.org/html5/spec/syntax.html#elements-0
+       // as described at http://www.w3.org/TR/html5/syntax.html#elements-0
        stateRCDATA
        // stateAttr occurs inside an HTML attribute whose content is text.
        stateAttr
index 9ae9749db0c408220e83ee45cfd70e24fd872dd9..4e379828d4c33b5cfd86083a4e5c2a85ddccaad5 100644 (file)
@@ -40,10 +40,14 @@ func escapeTemplates(tmpl *Template, names ...string) error {
                        }
                        return err
                }
-               tmpl.escaped = true
-               tmpl.Tree = tmpl.text.Tree
        }
        e.commit()
+       for _, name := range names {
+               if t := tmpl.set[name]; t != nil {
+                       t.escaped = true
+                       t.Tree = t.text.Tree
+               }
+       }
        return nil
 }
 
@@ -207,6 +211,18 @@ func (e *escaper) escapeAction(c context, n *parse.ActionNode) context {
        return c
 }
 
+// allIdents returns the names of the identifiers under the Ident field of the node,
+// which might be a singleton (Identifier) or a slice (Field).
+func allIdents(node parse.Node) []string {
+       switch node := node.(type) {
+       case *parse.IdentifierNode:
+               return []string{node.Ident}
+       case *parse.FieldNode:
+               return node.Ident
+       }
+       panic("unidentified node type in allIdents")
+}
+
 // ensurePipelineContains ensures that the pipeline has commands with
 // the identifiers in s in order.
 // If the pipeline already has some of the sanitizers, do not interfere.
@@ -229,27 +245,31 @@ func ensurePipelineContains(p *parse.PipeNode, s []string) {
                idents = p.Cmds[i+1:]
        }
        dups := 0
-       for _, id := range idents {
-               if escFnsEq(s[dups], (id.Args[0].(*parse.IdentifierNode)).Ident) {
-                       dups++
-                       if dups == len(s) {
-                               return
+       for _, idNode := range idents {
+               for _, ident := range allIdents(idNode.Args[0]) {
+                       if escFnsEq(s[dups], ident) {
+                               dups++
+                               if dups == len(s) {
+                                       return
+                               }
                        }
                }
        }
        newCmds := make([]*parse.CommandNode, n-len(idents), n+len(s)-dups)
        copy(newCmds, p.Cmds)
        // Merge existing identifier commands with the sanitizers needed.
-       for _, id := range idents {
-               pos := id.Args[0].Position()
-               i := indexOfStr((id.Args[0].(*parse.IdentifierNode)).Ident, s, escFnsEq)
-               if i != -1 {
-                       for _, name := range s[:i] {
-                               newCmds = appendCmd(newCmds, newIdentCmd(name, pos))
+       for _, idNode := range idents {
+               pos := idNode.Args[0].Position()
+               for _, ident := range allIdents(idNode.Args[0]) {
+                       i := indexOfStr(ident, s, escFnsEq)
+                       if i != -1 {
+                               for _, name := range s[:i] {
+                                       newCmds = appendCmd(newCmds, newIdentCmd(name, pos))
+                               }
+                               s = s[i+1:]
                        }
-                       s = s[i+1:]
                }
-               newCmds = appendCmd(newCmds, id)
+               newCmds = appendCmd(newCmds, idNode)
        }
        // Create any remaining sanitizers.
        for _, name := range s {
@@ -664,7 +684,7 @@ func contextAfterText(c context, s []byte) (context, int) {
                i = len(s)
        }
        if c.delim == delimSpaceOrTagEnd {
-               // http://www.w3.org/TR/html5/tokenization.html#attribute-value-unquoted-state
+               // http://www.w3.org/TR/html5/syntax.html#attribute-value-(unquoted)-state
                // lists the runes below as error characters.
                // Error out because HTML parsers may differ on whether
                // "<a id= onclick=f("     ends inside id's or onclick's value,
index 58383a6cd4e291454d659776b8fca56d8778face..3ccf93ece01877a2563f47702ad83f7bb919eefc 100644 (file)
@@ -1649,6 +1649,38 @@ func TestEmptyTemplate(t *testing.T) {
        }
 }
 
+type Issue7379 int
+
+func (Issue7379) SomeMethod(x int) string {
+       return fmt.Sprintf("<%d>", x)
+}
+
+// This is a test for issue 7379: type assertion error caused panic, and then
+// the code to handle the panic breaks escaping. It's hard to see the second
+// problem once the first is fixed, but its fix is trivial so we let that go. See
+// the discussion for issue 7379.
+func TestPipeToMethodIsEscaped(t *testing.T) {
+       tmpl := Must(New("x").Parse("<html>{{0 | .SomeMethod}}</html>\n"))
+       tryExec := func() string {
+               defer func() {
+                       panicValue := recover()
+                       if panicValue != nil {
+                               t.Errorf("panicked: %v\n", panicValue)
+                       }
+               }()
+               var b bytes.Buffer
+               tmpl.Execute(&b, Issue7379(0))
+               return b.String()
+       }
+       for i := 0; i < 3; i++ {
+               str := tryExec()
+               const expect = "<html>&lt;0&gt;</html>\n"
+               if str != expect {
+                       t.Errorf("expected %q got %q", expect, str)
+               }
+       }
+}
+
 func BenchmarkEscapedExecute(b *testing.B) {
        tmpl := Must(New("t").Parse(`<a onclick="alert('{{.}}')">{{.}}</a>`))
        var buf bytes.Buffer
index f25f1074c7beb5f8cca249a4c9688b5be5f342fc..9c069efd1d9a28393886eac1e2c284297643d0e5 100644 (file)
@@ -50,12 +50,12 @@ func htmlEscaper(args ...interface{}) string {
 // htmlReplacementTable contains the runes that need to be escaped
 // inside a quoted attribute value or in a text node.
 var htmlReplacementTable = []string{
-       // http://www.w3.org/TR/html5/tokenization.html#attribute-value-unquoted-state: "
+       // http://www.w3.org/TR/html5/syntax.html#attribute-value-(unquoted)-state
        // U+0000 NULL Parse error. Append a U+FFFD REPLACEMENT
        // CHARACTER character to the current attribute's value.
        // "
        // and similarly
-       // http://www.w3.org/TR/html5/tokenization.html#before-attribute-value-state
+       // http://www.w3.org/TR/html5/syntax.html#before-attribute-value-state
        0:    "\uFFFD",
        '"':  "&#34;",
        '&':  "&amp;",
index d594e0ad7116b1fb8d97a4757edbbe6662396213..999a61ed078a30f74001c49ddb7727b5bd48c12c 100644 (file)
@@ -99,7 +99,7 @@ func nextJSCtx(s []byte, preceding jsCtx) jsCtx {
        return jsCtxDivOp
 }
 
-// regexPrecederKeywords is a set of reserved JS keywords that can precede a
+// regexpPrecederKeywords is a set of reserved JS keywords that can precede a
 // regular expression in JS source.
 var regexpPrecederKeywords = map[string]bool{
        "break":      true,
index 11cc34a50a38eb80297d54da58808bbe39c07625..d389658979aa4c045fa7a291c999340dd9a2aad3 100644 (file)
@@ -62,6 +62,10 @@ func (t *Template) escape() error {
 
 // Execute applies a parsed template to the specified data object,
 // writing the output to wr.
+// If an error occurs executing the template or writing its output,
+// execution stops, but partial results may already have been written to
+// the output writer.
+// A template may be executed safely in parallel.
 func (t *Template) Execute(wr io.Writer, data interface{}) error {
        if err := t.escape(); err != nil {
                return err
@@ -71,6 +75,10 @@ func (t *Template) Execute(wr io.Writer, data interface{}) error {
 
 // ExecuteTemplate applies the template associated with t that has the given
 // name to the specified data object and writes the output to wr.
+// If an error occurs executing the template or writing its output,
+// execution stops, but partial results may already have been written to
+// the output writer.
+// A template may be executed safely in parallel.
 func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{}) error {
        tmpl, err := t.lookupAndEscapeTemplate(name)
        if err != nil {
index 9b731fdc4fa590eddbacf00cc078037870f82d23..f53d873a53876d880f151e53d899918ae637af89 100644 (file)
@@ -37,6 +37,9 @@ func (d *decoder) ensureNBits(n int) error {
        for d.b.n < n {
                c, err := d.r.ReadByte()
                if err != nil {
+                       if err == io.EOF {
+                               return FormatError("short Huffman data")
+                       }
                        return err
                }
                d.b.a = d.b.a<<8 | uint32(c)
@@ -50,6 +53,9 @@ func (d *decoder) ensureNBits(n int) error {
                if c == 0xff {
                        c, err = d.r.ReadByte()
                        if err != nil {
+                               if err == io.EOF {
+                                       return FormatError("short Huffman data")
+                               }
                                return err
                        }
                        if c != 0x00 {
index e951e038c067030f209086813940ab0da366871e..926bb043448d2d9927aa099a8cee37aca8933cbd 100644 (file)
@@ -28,6 +28,7 @@ func TestDecodeProgressive(t *testing.T) {
                "../testdata/video-001.q50.444",
                "../testdata/video-005.gray.q50",
                "../testdata/video-005.gray.q50.2x2",
+               "../testdata/video-001.separate.dc.progression",
        }
        for _, tc := range testCases {
                m0, err := decodeFile(tc + ".jpeg")
@@ -44,6 +45,12 @@ func TestDecodeProgressive(t *testing.T) {
                        t.Errorf("%s: bounds differ: %v and %v", tc, m0.Bounds(), m1.Bounds())
                        continue
                }
+               // All of the video-*.jpeg files are 150x103.
+               if m0.Bounds() != image.Rect(0, 0, 150, 103) {
+                       t.Errorf("%s: bad bounds: %v", tc, m0.Bounds())
+                       continue
+               }
+
                switch m0 := m0.(type) {
                case *image.YCbCr:
                        m1 := m1.(*image.YCbCr)
@@ -84,18 +91,15 @@ func decodeFile(filename string) (image.Image, error) {
 
 // check checks that the two pix data are equal, within the given bounds.
 func check(bounds image.Rectangle, pix0, pix1 []byte, stride0, stride1 int) error {
-       if len(pix0) != len(pix1) {
-               return fmt.Errorf("len(pix) %d and %d differ", len(pix0), len(pix1))
-       }
-       if stride0 != stride1 {
-               return fmt.Errorf("strides %d and %d differ", stride0, stride1)
+       if stride0 <= 0 || stride0%8 != 0 {
+               return fmt.Errorf("bad stride %d", stride0)
        }
-       if stride0%8 != 0 {
-               return fmt.Errorf("stride %d is not a multiple of 8", stride0)
+       if stride1 <= 0 || stride1%8 != 0 {
+               return fmt.Errorf("bad stride %d", stride1)
        }
        // Compare the two pix data, one 8x8 block at a time.
-       for y := 0; y < len(pix0)/stride0; y += 8 {
-               for x := 0; x < stride0; x += 8 {
+       for y := 0; y < len(pix0)/stride0 && y < len(pix1)/stride1; y += 8 {
+               for x := 0; x < stride0 && x < stride1; x += 8 {
                        if x >= bounds.Max.X || y >= bounds.Max.Y {
                                // We don't care if the two pix data differ if the 8x8 block is
                                // entirely outside of the image's bounds. For example, this can
@@ -108,8 +112,9 @@ func check(bounds image.Rectangle, pix0, pix1 []byte, stride0, stride1 int) erro
 
                        for j := 0; j < 8; j++ {
                                for i := 0; i < 8; i++ {
-                                       index := (y+j)*stride0 + (x + i)
-                                       if pix0[index] != pix1[index] {
+                                       index0 := (y+j)*stride0 + (x + i)
+                                       index1 := (y+j)*stride1 + (x + i)
+                                       if pix0[index0] != pix1[index1] {
                                                return fmt.Errorf("blocks at (%d, %d) differ:\n%sand\n%s", x, y,
                                                        pixString(pix0, stride0, x, y),
                                                        pixString(pix1, stride1, x, y),
index a69ed17489c6c8bce0d74b2390edbf97cb9a0ff4..559235d51272a3f0f93bc1cc1736ad6e9ebd9dfc 100644 (file)
@@ -141,25 +141,30 @@ func (d *decoder) processSOS(n int) error {
                                for j := 0; j < d.comp[compIndex].h*d.comp[compIndex].v; j++ {
                                        // The blocks are traversed one MCU at a time. For 4:2:0 chroma
                                        // subsampling, there are four Y 8x8 blocks in every 16x16 MCU.
+                                       //
                                        // For a baseline 32x16 pixel image, the Y blocks visiting order is:
                                        //      0 1 4 5
                                        //      2 3 6 7
                                        //
-                                       // For progressive images, the DC data blocks (zigStart == 0) are traversed
-                                       // as above, but AC data blocks are traversed left to right, top to bottom:
+                                       // For progressive images, the interleaved scans (those with nComp > 1)
+                                       // are traversed as above, but non-interleaved scans are traversed left
+                                       // to right, top to bottom:
                                        //      0 1 2 3
                                        //      4 5 6 7
+                                       // Only DC scans (zigStart == 0) can be interleaved. AC scans must have
+                                       // only one component.
                                        //
-                                       // To further complicate matters, there is no AC data for any blocks that
-                                       // are inside the image at the MCU level but outside the image at the pixel
-                                       // level. For example, a 24x16 pixel 4:2:0 progressive image consists of
-                                       // two 16x16 MCUs. The earlier scans will process 8 Y blocks:
+                                       // To further complicate matters, for non-interleaved scans, there is no
+                                       // data for any blocks that are inside the image at the MCU level but
+                                       // outside the image at the pixel level. For example, a 24x16 pixel 4:2:0
+                                       // progressive image consists of two 16x16 MCUs. The interleaved scans
+                                       // will process 8 Y blocks:
                                        //      0 1 4 5
                                        //      2 3 6 7
-                                       // The later scans will process only 6 Y blocks:
+                                       // The non-interleaved scans will process only 6 Y blocks:
                                        //      0 1 2
                                        //      3 4 5
-                                       if zigStart == 0 {
+                                       if nComp != 1 {
                                                mx0, my0 = d.comp[compIndex].h*mx, d.comp[compIndex].v*my
                                                if h0 == 1 {
                                                        my0 += j
index a6bf86ede6505036ee3800f1484d6fdbb9f38041..dfe2991024dce40327482a644c8a1070345c6c22 100644 (file)
@@ -505,8 +505,14 @@ func (d *decoder) decode() (image.Image, error) {
        }
 
        // Check for EOF, to verify the zlib checksum.
-       n, err := r.Read(pr[:1])
-       if err != io.EOF {
+       n := 0
+       for i := 0; n == 0 && err == nil; i++ {
+               if i == 100 {
+                       return nil, io.ErrNoProgress
+               }
+               n, err = r.Read(pr[:1])
+       }
+       if err != nil && err != io.EOF {
                return nil, FormatError(err.Error())
        }
        if n != 0 || d.idatLength != 0 {
diff --git a/libgo/go/image/testdata/video-001.separate.dc.progression.jpeg b/libgo/go/image/testdata/video-001.separate.dc.progression.jpeg
new file mode 100644 (file)
index 0000000..107f0fa
Binary files /dev/null and b/libgo/go/image/testdata/video-001.separate.dc.progression.jpeg differ
diff --git a/libgo/go/image/testdata/video-001.separate.dc.progression.progressive.jpeg b/libgo/go/image/testdata/video-001.separate.dc.progression.progressive.jpeg
new file mode 100644 (file)
index 0000000..a1d493e
Binary files /dev/null and b/libgo/go/image/testdata/video-001.separate.dc.progression.progressive.jpeg differ
diff --git a/libgo/go/image/testdata/video-005.gray.gif b/libgo/go/image/testdata/video-005.gray.gif
new file mode 100644 (file)
index 0000000..23350d6
Binary files /dev/null and b/libgo/go/image/testdata/video-005.gray.gif differ
index f7073ffc06841e8d353a77ec7e481eca80732406..022fdb676453949e197e7350824cb179ea114fdc 100644 (file)
@@ -74,6 +74,7 @@ type Reader interface {
 // It returns the number of bytes written from p (0 <= n <= len(p))
 // and any error encountered that caused the write to stop early.
 // Write must return a non-nil error if it returns n < len(p).
+// Write must not modify the slice data, even temporarily.
 type Writer interface {
        Write(p []byte) (n int, err error)
 }
index ab8dd5d3b40534e2354846b814d42d3a173ad004..e26cc53e9eeb7cc8477a00247b177ffd9cea3a36 100644 (file)
@@ -29,7 +29,9 @@ func (mr *multiReader) Read(p []byte) (n int, err error) {
 // inputs have returned EOF, Read will return EOF.  If any of the readers
 // return a non-nil, non-EOF error, Read will return that error.
 func MultiReader(readers ...Reader) Reader {
-       return &multiReader{readers}
+       r := make([]Reader, len(readers))
+       copy(r, readers)
+       return &multiReader{r}
 }
 
 type multiWriter struct {
@@ -53,5 +55,7 @@ func (t *multiWriter) Write(p []byte) (n int, err error) {
 // MultiWriter creates a writer that duplicates its writes to all the
 // provided writers, similar to the Unix tee(1) command.
 func MultiWriter(writers ...Writer) Writer {
-       return &multiWriter{writers}
+       w := make([]Writer, len(writers))
+       copy(w, writers)
+       return &multiWriter{w}
 }
index eb717f7bc21c7405bf1716aa100b06a21d9f22b1..56c6769a9eb598cc8e1671c88848eb5eb2f85245 100644 (file)
@@ -9,6 +9,7 @@ import (
        "crypto/sha1"
        "fmt"
        . "io"
+       "io/ioutil"
        "strings"
        "testing"
 )
@@ -86,3 +87,29 @@ func TestMultiWriter(t *testing.T) {
                t.Errorf("expected %q; got %q", sourceString, sink.String())
        }
 }
+
+// Test that MultiReader copies the input slice and is insulated from future modification.
+func TestMultiReaderCopy(t *testing.T) {
+       slice := []Reader{strings.NewReader("hello world")}
+       r := MultiReader(slice...)
+       slice[0] = nil
+       data, err := ioutil.ReadAll(r)
+       if err != nil || string(data) != "hello world" {
+               t.Errorf("ReadAll() = %q, %v, want %q, nil", data, err, "hello world")
+       }
+}
+
+// Test that MultiWriter copies the input slice and is insulated from future modification.
+func TestMultiWriterCopy(t *testing.T) {
+       var buf bytes.Buffer
+       slice := []Writer{&buf}
+       w := MultiWriter(slice...)
+       slice[0] = nil
+       n, err := w.Write([]byte("hello world"))
+       if err != nil || n != 11 {
+               t.Errorf("Write(`hello world`) = %d, %v, want 11, nil", n, err)
+       }
+       if buf.String() != "hello world" {
+               t.Errorf("buf.String() = %q, want %q", buf.String(), "hello world")
+       }
+}
index 0cbfa9011b8e0fa0dd9648634797bec0816bd6c1..5e09599162693d51a7d80702b98250ca17b39598 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.
 
-// +build !windows,!plan9
+// +build !windows,!nacl,!plan9
 
 // Package syslog provides a simple interface to the system log
 // service. It can send messages to the syslog daemon using UNIX
@@ -115,9 +115,10 @@ func New(priority Priority, tag string) (w *Writer, err error) {
 }
 
 // Dial establishes a connection to a log daemon by connecting to
-// address raddr on the network net.  Each write to the returned
+// address raddr on the specified network.  Each write to the returned
 // writer sends a log message with the given facility, severity and
 // tag.
+// If network is empty, Dial will connect to the local syslog server.
 func Dial(network, raddr string, priority Priority, tag string) (*Writer, error) {
        if priority < 0 || priority > LOG_LOCAL7|LOG_DEBUG {
                return nil, errors.New("log/syslog: invalid priority")
index 760a5c7d1e99169e399db5da6af4c8a3732816be..24a460f6d9eaaa0b33348e4a1a1c51e946a9d09a 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.
 
-// +build !windows,!plan9
+// +build !windows,!nacl,!plan9
 
 package syslog
 
index 28a294af96387eb96a2546522b54df304f62723d..f6d2f1b7a399ef89a1958b985dd8be6d9fd1e38c 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.
 
-// +build !windows,!plan9
+// +build !windows,!nacl,!plan9
 
 package syslog
 
index 4591590d409817e6bf8b05af1be5674b6cd06c1a..269949d616070b5e45f5238a612e9e9b620b8db7 100644 (file)
@@ -576,21 +576,22 @@ func (x *Int) BitLen() int {
 }
 
 // Exp sets z = x**y mod |m| (i.e. the sign of m is ignored), and returns z.
-// If y <= 0, the result is 1; if m == nil or m == 0, z = x**y.
+// If y <= 0, the result is 1 mod |m|; if m == nil or m == 0, z = x**y.
 // See Knuth, volume 2, section 4.6.3.
 func (z *Int) Exp(x, y, m *Int) *Int {
-       if y.neg || len(y.abs) == 0 {
-               return z.SetInt64(1)
+       var yWords nat
+       if !y.neg {
+               yWords = y.abs
        }
-       // y > 0
+       // y >= 0
 
        var mWords nat
        if m != nil {
                mWords = m.abs // m.abs may be nil for m == 0
        }
 
-       z.abs = z.abs.expNN(x.abs, y.abs, mWords)
-       z.neg = len(z.abs) > 0 && x.neg && y.abs[0]&1 == 1 // 0 has no sign
+       z.abs = z.abs.expNN(x.abs, yWords, mWords)
+       z.neg = len(z.abs) > 0 && x.neg && len(yWords) > 0 && yWords[0]&1 == 1 // 0 has no sign
        return z
 }
 
index 3dd9c5b712b72d200d71e5e60846f37cb67a0f12..299dc72fb1a2e4cdeca0448454df853ec8c01aa3 100644 (file)
@@ -768,6 +768,19 @@ var expTests = []struct {
        x, y, m string
        out     string
 }{
+       // y <= 0
+       {"0", "0", "", "1"},
+       {"1", "0", "", "1"},
+       {"-10", "0", "", "1"},
+       {"1234", "-1", "", "1"},
+
+       // m == 1
+       {"0", "0", "1", "0"},
+       {"1", "0", "1", "0"},
+       {"-10", "0", "1", "0"},
+       {"1234", "-1", "1", "0"},
+
+       // misc
        {"5", "-7", "", "1"},
        {"-5", "-7", "", "1"},
        {"5", "0", "", "1"},
index 6874900d0bdecf0d7a69c8a3a78e573bf0012b61..16a87f5c537480efaeab5ecfe2a36ba26e9af2b4 100644 (file)
@@ -1233,10 +1233,15 @@ func (z nat) expNN(x, y, m nat) nat {
                z = nil
        }
 
+       // x**y mod 1 == 0
+       if len(m) == 1 && m[0] == 1 {
+               return z.setWord(0)
+       }
+       // m == 0 || m > 1
+
+       // x**0 == 1
        if len(y) == 0 {
-               z = z.make(1)
-               z[0] = 1
-               return z
+               return z.setWord(1)
        }
        // y > 0
 
index 1d4dfe80d3d66479d0f013ed6015b4d1fff63b32..a2ae53385c91512a7ace41dcb511e48d6ac43b5b 100644 (file)
@@ -437,20 +437,11 @@ func BenchmarkStringPiParallel(b *testing.B) {
        if x.decimalString() != pi {
                panic("benchmark incorrect: conversion failed")
        }
-       n := runtime.GOMAXPROCS(0)
-       m := b.N / n // n*m <= b.N due to flooring, but the error is neglibible (n is not very large)
-       c := make(chan int, n)
-       for i := 0; i < n; i++ {
-               go func() {
-                       for j := 0; j < m; j++ {
-                               x.decimalString()
-                       }
-                       c <- 0
-               }()
-       }
-       for i := 0; i < n; i++ {
-               <-c
-       }
+       b.RunParallel(func(pb *testing.PB) {
+               for pb.Next() {
+                       x.decimalString()
+               }
+       })
 }
 
 func BenchmarkScan10Base2(b *testing.B)     { ScanHelper(b, 2, 10, 10) }
@@ -723,6 +714,12 @@ var expNNTests = []struct {
        x, y, m string
        out     string
 }{
+       {"0", "0", "0", "1"},
+       {"0", "0", "1", "0"},
+       {"1", "1", "1", "0"},
+       {"2", "1", "1", "0"},
+       {"2", "2", "1", "0"},
+       {"10", "100000000000", "1", "0"},
        {"0x8000000000000000", "2", "", "0x40000000000000000000000000000000"},
        {"0x8000000000000000", "2", "6719", "4944"},
        {"0x8000000000000000", "3", "6719", "5447"},
@@ -750,7 +747,7 @@ func TestExpNN(t *testing.T) {
 
                z := nat(nil).expNN(x, y, m)
                if z.cmp(out) != 0 {
-                       t.Errorf("#%d got %v want %v", i, z, out)
+                       t.Errorf("#%d got %s want %s", i, z.decimalString(), out.decimalString())
                }
        }
 }
index 3cdb1d807f5dc4406eab1b9a70e4013a7396f45f..f0973b3902f19a4f89c3bc6a55f83272d0f6ec14 100644 (file)
@@ -47,7 +47,7 @@ func (z *Rat) SetFloat64(f float64) *Rat {
 
        shift := 52 - exp
 
-       // Optimisation (?): partially pre-normalise.
+       // Optimization (?): partially pre-normalise.
        for mantissa&1 == 0 && shift > 0 {
                mantissa >>= 1
                shift--
@@ -477,7 +477,7 @@ func (z *Rat) SetString(s string) (*Rat, bool) {
        return z, true
 }
 
-// String returns a string representation of z in the form "a/b" (even if b == 1).
+// String returns a string representation of x in the form "a/b" (even if b == 1).
 func (x *Rat) String() string {
        s := "/1"
        if len(x.b.abs) != 0 {
@@ -486,7 +486,7 @@ func (x *Rat) String() string {
        return x.a.String() + s
 }
 
-// RatString returns a string representation of z in the form "a/b" if b != 1,
+// RatString returns a string representation of x in the form "a/b" if b != 1,
 // and in the form "a" if b == 1.
 func (x *Rat) RatString() string {
        if x.IsInt() {
@@ -495,7 +495,7 @@ func (x *Rat) RatString() string {
        return x.String()
 }
 
-// FloatString returns a string representation of z in decimal form with prec
+// FloatString returns a string representation of x in decimal form with prec
 // digits of precision after the decimal point and the last digit rounded.
 func (x *Rat) FloatString(prec int) string {
        if x.IsInt() {
index 610ca8cebb2fca06ecb7755a84006c6b0314eb42..f285646af7a8b9318b9decab32f93bde7dd6081b 100644 (file)
@@ -656,6 +656,19 @@ func TestPolar(t *testing.T) {
        }
 }
 func TestPow(t *testing.T) {
+       // Special cases for Pow(0, c).
+       var zero = complex(0, 0)
+       zeroPowers := [][2]complex128{
+               {0, 1 + 0i},
+               {1.5, 0 + 0i},
+               {-1.5, complex(math.Inf(0), 0)},
+               {-1.5 + 1.5i, Inf()},
+       }
+       for _, zp := range zeroPowers {
+               if f := Pow(zero, zp[0]); f != zp[1] {
+                       t.Errorf("Pow(%g, %g) = %g, want %g", zero, zp[0], f, zp[1])
+               }
+       }
        var a = complex(3.0, 3.0)
        for i := 0; i < len(vc); i++ {
                if f := Pow(a, vc[i]); !cSoclose(pow[i], f, 4e-15) {
index 4dbc58398b4c8fcc0eda62e2e5282b292f40b375..1630b879b88b0dbc97c4d5ef16a1815f40c66953 100644 (file)
@@ -43,7 +43,25 @@ import "math"
 //    IEEE      -10,+10     30000       9.4e-15     1.5e-15
 
 // Pow returns x**y, the base-x exponential of y.
+// For generalized compatibility with math.Pow:
+//     Pow(0, ±0) returns 1+0i
+//     Pow(0, c) for real(c)<0 returns Inf+0i if imag(c) is zero, otherwise Inf+Inf i.
 func Pow(x, y complex128) complex128 {
+       if x == 0 { // Guaranteed also true for x == -0.
+               r, i := real(y), imag(y)
+               switch {
+               case r == 0:
+                       return 1
+               case r < 0:
+                       if i == 0 {
+                               return complex(math.Inf(1), 0)
+                       }
+                       return Inf()
+               case r > 0:
+                       return 0
+               }
+               panic("not reached")
+       }
        modulus := Abs(x)
        if modulus == 0 {
                return complex(0, 0)
index 179b5396abcc4bca5ad635ef0c098b7f25f04284..4ef6807addc9e1feed74a8c5738d6a5bc1e4f608 100644 (file)
@@ -54,6 +54,7 @@ import "math"
 //    IEEE      -10,+10   1,000,000     2.9e-16     6.1e-17
 
 // Sqrt returns the square root of x.
+// The result r is chosen so that real(r) ≥ 0 and imag(r) has the same sign as imag(x).
 func Sqrt(x complex128) complex128 {
        if imag(x) == 0 {
                if real(x) == 0 {
index d3ea84017812613645c55689fca44db699d160b6..3ffb5c4e5c64d2062e448e028146028aed940693 100644 (file)
@@ -60,6 +60,9 @@ func (r *Rand) Int63n(n int64) int64 {
        if n <= 0 {
                panic("invalid argument to Int63n")
        }
+       if n&(n-1) == 0 { // n is power of two, can mask
+               return r.Int63() & (n - 1)
+       }
        max := int64((1 << 63) - 1 - (1<<63)%uint64(n))
        v := r.Int63()
        for v > max {
@@ -74,6 +77,9 @@ func (r *Rand) Int31n(n int32) int32 {
        if n <= 0 {
                panic("invalid argument to Int31n")
        }
+       if n&(n-1) == 0 { // n is power of two, can mask
+               return r.Int31() & (n - 1)
+       }
        max := int32((1 << 31) - 1 - (1<<31)%uint32(n))
        v := r.Int31()
        for v > max {
@@ -95,10 +101,46 @@ func (r *Rand) Intn(n int) int {
 }
 
 // Float64 returns, as a float64, a pseudo-random number in [0.0,1.0).
-func (r *Rand) Float64() float64 { return float64(r.Int63n(1<<53)) / (1 << 53) }
+func (r *Rand) Float64() float64 {
+       // A clearer, simpler implementation would be:
+       //      return float64(r.Int63n(1<<53)) / (1<<53)
+       // However, Go 1 shipped with
+       //      return float64(r.Int63()) / (1 << 63)
+       // and we want to preserve that value stream.
+       //
+       // There is one bug in the value stream: r.Int63() may be so close
+       // to 1<<63 that the division rounds up to 1.0, and we've guaranteed
+       // that the result is always less than 1.0. To fix that, we treat the
+       // range as cyclic and map 1 back to 0. This is justified by observing
+       // that while some of the values rounded down to 0, nothing was
+       // rounding up to 0, so 0 was underrepresented in the results.
+       // Mapping 1 back to zero restores some balance.
+       // (The balance is not perfect because the implementation
+       // returns denormalized numbers for very small r.Int63(),
+       // and those steal from what would normally be 0 results.)
+       // The remapping only happens 1/2⁵³ of the time, so most clients
+       // will not observe it anyway.
+       f := float64(r.Int63()) / (1 << 63)
+       if f == 1 {
+               f = 0
+       }
+       return f
+}
 
 // Float32 returns, as a float32, a pseudo-random number in [0.0,1.0).
-func (r *Rand) Float32() float32 { return float32(r.Int31n(1<<24)) / (1 << 24) }
+func (r *Rand) Float32() float32 {
+       // Same rationale as in Float64: we want to preserve the Go 1 value
+       // stream except we want to fix it not to return 1.0
+       // There is a double rounding going on here, but the argument for
+       // mapping 1 to 0 still applies: 0 was underrepresented before,
+       // so mapping 1 to 0 doesn't cause too many 0s.
+       // This only happens 1/2²⁴ of the time (plus the 1/2⁵³ of the time in Float64).
+       f := float32(r.Float64())
+       if f == 1 {
+               f = 0
+       }
+       return f
+}
 
 // Perm returns, as a slice of n ints, a pseudo-random permutation of the integers [0,n).
 func (r *Rand) Perm(n int) []int {
index c174c613f40c56cb6012f04b16dae82da3198a55..ab0dc49b41128a46b8976b77bc4be1c10ec97dc7 100644 (file)
@@ -376,6 +376,13 @@ func BenchmarkFloat32(b *testing.B) {
        }
 }
 
+func BenchmarkFloat64(b *testing.B) {
+       r := New(NewSource(1))
+       for n := b.N; n > 0; n-- {
+               r.Float64()
+       }
+}
+
 func BenchmarkPerm3(b *testing.B) {
        r := New(NewSource(1))
        for n := b.N; n > 0; n-- {
diff --git a/libgo/go/math/rand/regress_test.go b/libgo/go/math/rand/regress_test.go
new file mode 100644 (file)
index 0000000..2b012af
--- /dev/null
@@ -0,0 +1,355 @@
+// Copyright 2014 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.
+
+// Test that random number sequences generated by a specific seed
+// do not change from version to version.
+//
+// Do NOT make changes to the golden outputs. If bugs need to be fixed
+// in the underlying code, find ways to fix them that do not affect the
+// outputs.
+
+package rand_test
+
+import (
+       "flag"
+       "fmt"
+       . "math/rand"
+       "reflect"
+       "testing"
+)
+
+var printgolden = flag.Bool("printgolden", false, "print golden results for regression test")
+
+func TestRegress(t *testing.T) {
+       var int32s = []int32{1, 10, 32, 1 << 20, 1<<20 + 1, 1000000000, 1 << 30, 1<<31 - 2, 1<<31 - 1}
+       var int64s = []int64{1, 10, 32, 1 << 20, 1<<20 + 1, 1000000000, 1 << 30, 1<<31 - 2, 1<<31 - 1, 1000000000000000000, 1 << 60, 1<<63 - 2, 1<<63 - 1}
+       var permSizes = []int{0, 1, 5, 8, 9, 10, 16}
+       r := New(NewSource(0))
+
+       rv := reflect.ValueOf(r)
+       n := rv.NumMethod()
+       p := 0
+       if *printgolden {
+               fmt.Printf("var regressGolden = []interface{}{\n")
+       }
+       for i := 0; i < n; i++ {
+               m := rv.Type().Method(i)
+               mv := rv.Method(i)
+               mt := mv.Type()
+               if mt.NumOut() == 0 {
+                       continue
+               }
+               if mt.NumOut() != 1 {
+                       t.Fatalf("unexpected result count for r.%s", m.Name)
+               }
+               r.Seed(0)
+               for repeat := 0; repeat < 20; repeat++ {
+                       var args []reflect.Value
+                       var argstr string
+                       if mt.NumIn() == 1 {
+                               var x interface{}
+                               switch mt.In(0).Kind() {
+                               default:
+                                       t.Fatalf("unexpected argument type for r.%s", m.Name)
+
+                               case reflect.Int:
+                                       if m.Name == "Perm" {
+                                               x = permSizes[repeat%len(permSizes)]
+                                               break
+                                       }
+                                       big := int64s[repeat%len(int64s)]
+                                       if int64(int(big)) != big {
+                                               r.Int63n(big) // what would happen on 64-bit machine, to keep stream in sync
+                                               if *printgolden {
+                                                       fmt.Printf("\tskipped, // must run printgolden on 64-bit machine\n")
+                                               }
+                                               p++
+                                               continue
+                                       }
+                                       x = int(big)
+
+                               case reflect.Int32:
+                                       x = int32s[repeat%len(int32s)]
+
+                               case reflect.Int64:
+                                       x = int64s[repeat%len(int64s)]
+                               }
+                               argstr = fmt.Sprint(x)
+                               args = append(args, reflect.ValueOf(x))
+                       }
+                       out := mv.Call(args)[0].Interface()
+                       if m.Name == "Int" || m.Name == "Intn" {
+                               out = int64(out.(int))
+                       }
+                       if *printgolden {
+                               var val string
+                               big := int64(1 << 60)
+                               if int64(int(big)) != big && (m.Name == "Int" || m.Name == "Intn") {
+                                       // 32-bit machine cannot print 64-bit results
+                                       val = "truncated"
+                               } else if reflect.TypeOf(out).Kind() == reflect.Slice {
+                                       val = fmt.Sprintf("%#v", out)
+                               } else {
+                                       val = fmt.Sprintf("%T(%v)", out, out)
+                               }
+                               fmt.Printf("\t%s, // %s(%s)\n", val, m.Name, argstr)
+                       } else {
+                               want := regressGolden[p]
+                               if m.Name == "Int" {
+                                       want = int64(int(uint(want.(int64)) << 1 >> 1))
+                               }
+                               if !reflect.DeepEqual(out, want) {
+                                       t.Errorf("r.%s(%s) = %v, want %v", m.Name, argstr, out, want)
+                               }
+                       }
+                       p++
+               }
+       }
+       if *printgolden {
+               fmt.Printf("}\n")
+       }
+}
+
+var regressGolden = []interface{}{
+       float64(4.668112973579268),    // ExpFloat64()
+       float64(0.1601593871172866),   // ExpFloat64()
+       float64(3.0465834105636),      // ExpFloat64()
+       float64(0.06385839451671879),  // ExpFloat64()
+       float64(1.8578917487258961),   // ExpFloat64()
+       float64(0.784676123472182),    // ExpFloat64()
+       float64(0.11225477361256932),  // ExpFloat64()
+       float64(0.20173283329802255),  // ExpFloat64()
+       float64(0.3468619496201105),   // ExpFloat64()
+       float64(0.35601103454384536),  // ExpFloat64()
+       float64(0.888376329507869),    // ExpFloat64()
+       float64(1.4081362450365698),   // ExpFloat64()
+       float64(1.0077753823151994),   // ExpFloat64()
+       float64(0.23594100766227588),  // ExpFloat64()
+       float64(2.777245612300007),    // ExpFloat64()
+       float64(0.5202997830662377),   // ExpFloat64()
+       float64(1.2842705247770294),   // ExpFloat64()
+       float64(0.030307408362776206), // ExpFloat64()
+       float64(2.204156824853721),    // ExpFloat64()
+       float64(2.09891923895058),     // ExpFloat64()
+       float32(0.94519615),           // Float32()
+       float32(0.24496509),           // Float32()
+       float32(0.65595627),           // Float32()
+       float32(0.05434384),           // Float32()
+       float32(0.3675872),            // Float32()
+       float32(0.28948045),           // Float32()
+       float32(0.1924386),            // Float32()
+       float32(0.65533215),           // Float32()
+       float32(0.8971697),            // Float32()
+       float32(0.16735445),           // Float32()
+       float32(0.28858566),           // Float32()
+       float32(0.9026048),            // Float32()
+       float32(0.84978026),           // Float32()
+       float32(0.2730468),            // Float32()
+       float32(0.6090802),            // Float32()
+       float32(0.253656),             // Float32()
+       float32(0.7746542),            // Float32()
+       float32(0.017480763),          // Float32()
+       float32(0.78707397),           // Float32()
+       float32(0.7993937),            // Float32()
+       float64(0.9451961492941164),   // Float64()
+       float64(0.24496508529377975),  // Float64()
+       float64(0.6559562651954052),   // Float64()
+       float64(0.05434383959970039),  // Float64()
+       float64(0.36758720663245853),  // Float64()
+       float64(0.2894804331565928),   // Float64()
+       float64(0.19243860967493215),  // Float64()
+       float64(0.6553321508148324),   // Float64()
+       float64(0.897169713149801),    // Float64()
+       float64(0.16735444255905835),  // Float64()
+       float64(0.2885856518054551),   // Float64()
+       float64(0.9026048462705047),   // Float64()
+       float64(0.8497802817628735),   // Float64()
+       float64(0.2730468047134829),   // Float64()
+       float64(0.6090801919903561),   // Float64()
+       float64(0.25365600644283687),  // Float64()
+       float64(0.7746542391859803),   // Float64()
+       float64(0.017480762156647272), // Float64()
+       float64(0.7870739563039942),   // Float64()
+       float64(0.7993936979594545),   // Float64()
+       int64(8717895732742165505),    // Int()
+       int64(2259404117704393152),    // Int()
+       int64(6050128673802995827),    // Int()
+       int64(501233450539197794),     // Int()
+       int64(3390393562759376202),    // Int()
+       int64(2669985732393126063),    // Int()
+       int64(1774932891286980153),    // Int()
+       int64(6044372234677422456),    // Int()
+       int64(8274930044578894929),    // Int()
+       int64(1543572285742637646),    // Int()
+       int64(2661732831099943416),    // Int()
+       int64(8325060299420976708),    // Int()
+       int64(7837839688282259259),    // Int()
+       int64(2518412263346885298),    // Int()
+       int64(5617773211005988520),    // Int()
+       int64(2339563716805116249),    // Int()
+       int64(7144924247938981575),    // Int()
+       int64(161231572858529631),     // Int()
+       int64(7259475919510918339),    // Int()
+       int64(7373105480197164748),    // Int()
+       int32(2029793274),             // Int31()
+       int32(526058514),              // Int31()
+       int32(1408655353),             // Int31()
+       int32(116702506),              // Int31()
+       int32(789387515),              // Int31()
+       int32(621654496),              // Int31()
+       int32(413258767),              // Int31()
+       int32(1407315077),             // Int31()
+       int32(1926657288),             // Int31()
+       int32(359390928),              // Int31()
+       int32(619732968),              // Int31()
+       int32(1938329147),             // Int31()
+       int32(1824889259),             // Int31()
+       int32(586363548),              // Int31()
+       int32(1307989752),             // Int31()
+       int32(544722126),              // Int31()
+       int32(1663557311),             // Int31()
+       int32(37539650),               // Int31()
+       int32(1690228450),             // Int31()
+       int32(1716684894),             // Int31()
+       int32(0),                      // Int31n(1)
+       int32(4),                      // Int31n(10)
+       int32(25),                     // Int31n(32)
+       int32(310570),                 // Int31n(1048576)
+       int32(857611),                 // Int31n(1048577)
+       int32(621654496),              // Int31n(1000000000)
+       int32(413258767),              // Int31n(1073741824)
+       int32(1407315077),             // Int31n(2147483646)
+       int32(1926657288),             // Int31n(2147483647)
+       int32(0),                      // Int31n(1)
+       int32(8),                      // Int31n(10)
+       int32(27),                     // Int31n(32)
+       int32(367019),                 // Int31n(1048576)
+       int32(209005),                 // Int31n(1048577)
+       int32(307989752),              // Int31n(1000000000)
+       int32(544722126),              // Int31n(1073741824)
+       int32(1663557311),             // Int31n(2147483646)
+       int32(37539650),               // Int31n(2147483647)
+       int32(0),                      // Int31n(1)
+       int32(4),                      // Int31n(10)
+       int64(8717895732742165505),    // Int63()
+       int64(2259404117704393152),    // Int63()
+       int64(6050128673802995827),    // Int63()
+       int64(501233450539197794),     // Int63()
+       int64(3390393562759376202),    // Int63()
+       int64(2669985732393126063),    // Int63()
+       int64(1774932891286980153),    // Int63()
+       int64(6044372234677422456),    // Int63()
+       int64(8274930044578894929),    // Int63()
+       int64(1543572285742637646),    // Int63()
+       int64(2661732831099943416),    // Int63()
+       int64(8325060299420976708),    // Int63()
+       int64(7837839688282259259),    // Int63()
+       int64(2518412263346885298),    // Int63()
+       int64(5617773211005988520),    // Int63()
+       int64(2339563716805116249),    // Int63()
+       int64(7144924247938981575),    // Int63()
+       int64(161231572858529631),     // Int63()
+       int64(7259475919510918339),    // Int63()
+       int64(7373105480197164748),    // Int63()
+       int64(0),                      // Int63n(1)
+       int64(2),                      // Int63n(10)
+       int64(19),                     // Int63n(32)
+       int64(959842),                 // Int63n(1048576)
+       int64(688912),                 // Int63n(1048577)
+       int64(393126063),              // Int63n(1000000000)
+       int64(89212473),               // Int63n(1073741824)
+       int64(834026388),              // Int63n(2147483646)
+       int64(1577188963),             // Int63n(2147483647)
+       int64(543572285742637646),     // Int63n(1000000000000000000)
+       int64(355889821886249464),     // Int63n(1152921504606846976)
+       int64(8325060299420976708),    // Int63n(9223372036854775806)
+       int64(7837839688282259259),    // Int63n(9223372036854775807)
+       int64(0),                      // Int63n(1)
+       int64(0),                      // Int63n(10)
+       int64(25),                     // Int63n(32)
+       int64(679623),                 // Int63n(1048576)
+       int64(882178),                 // Int63n(1048577)
+       int64(510918339),              // Int63n(1000000000)
+       int64(782454476),              // Int63n(1073741824)
+       int64(0),                      // Intn(1)
+       int64(4),                      // Intn(10)
+       int64(25),                     // Intn(32)
+       int64(310570),                 // Intn(1048576)
+       int64(857611),                 // Intn(1048577)
+       int64(621654496),              // Intn(1000000000)
+       int64(413258767),              // Intn(1073741824)
+       int64(1407315077),             // Intn(2147483646)
+       int64(1926657288),             // Intn(2147483647)
+       int64(543572285742637646),     // Intn(1000000000000000000)
+       int64(355889821886249464),     // Intn(1152921504606846976)
+       int64(8325060299420976708),    // Intn(9223372036854775806)
+       int64(7837839688282259259),    // Intn(9223372036854775807)
+       int64(0),                      // Intn(1)
+       int64(2),                      // Intn(10)
+       int64(14),                     // Intn(32)
+       int64(515775),                 // Intn(1048576)
+       int64(839455),                 // Intn(1048577)
+       int64(690228450),              // Intn(1000000000)
+       int64(642943070),              // Intn(1073741824)
+       float64(-0.28158587086436215), // NormFloat64()
+       float64(0.570933095808067),    // NormFloat64()
+       float64(-1.6920196326157044),  // NormFloat64()
+       float64(0.1996229111693099),   // NormFloat64()
+       float64(1.9195199291234621),   // NormFloat64()
+       float64(0.8954838794918353),   // NormFloat64()
+       float64(0.41457072128813166),  // NormFloat64()
+       float64(-0.48700161491544713), // NormFloat64()
+       float64(-0.1684059662402393),  // NormFloat64()
+       float64(0.37056410998929545),  // NormFloat64()
+       float64(1.0156889027029008),   // NormFloat64()
+       float64(-0.5174422210625114),  // NormFloat64()
+       float64(-0.5565834214413804),  // NormFloat64()
+       float64(0.778320596648391),    // NormFloat64()
+       float64(-1.8970718197702225),  // NormFloat64()
+       float64(0.5229525761688676),   // NormFloat64()
+       float64(-1.5515595563231523),  // NormFloat64()
+       float64(0.0182029289376123),   // NormFloat64()
+       float64(-0.6820951356608795),  // NormFloat64()
+       float64(-0.5987943422687668),  // NormFloat64()
+       []int{},                                                     // Perm(0)
+       []int{0},                                                    // Perm(1)
+       []int{0, 4, 1, 3, 2},                                        // Perm(5)
+       []int{3, 1, 0, 4, 7, 5, 2, 6},                               // Perm(8)
+       []int{5, 0, 3, 6, 7, 4, 2, 1, 8},                            // Perm(9)
+       []int{4, 5, 0, 2, 6, 9, 3, 1, 8, 7},                         // Perm(10)
+       []int{14, 2, 0, 8, 3, 5, 13, 12, 1, 4, 6, 7, 11, 9, 15, 10}, // Perm(16)
+       []int{},                                                     // Perm(0)
+       []int{0},                                                    // Perm(1)
+       []int{3, 0, 1, 2, 4},                                        // Perm(5)
+       []int{5, 1, 2, 0, 4, 7, 3, 6},                               // Perm(8)
+       []int{4, 0, 6, 8, 1, 5, 2, 7, 3},                            // Perm(9)
+       []int{8, 6, 1, 7, 5, 4, 3, 2, 9, 0},                         // Perm(10)
+       []int{0, 3, 13, 2, 15, 4, 10, 1, 8, 14, 7, 6, 12, 9, 5, 11}, // Perm(16)
+       []int{},                             // Perm(0)
+       []int{0},                            // Perm(1)
+       []int{0, 4, 2, 1, 3},                // Perm(5)
+       []int{2, 1, 7, 0, 6, 3, 4, 5},       // Perm(8)
+       []int{8, 7, 5, 3, 4, 6, 0, 1, 2},    // Perm(9)
+       []int{1, 0, 2, 5, 7, 6, 9, 8, 3, 4}, // Perm(10)
+       uint32(4059586549),                  // Uint32()
+       uint32(1052117029),                  // Uint32()
+       uint32(2817310706),                  // Uint32()
+       uint32(233405013),                   // Uint32()
+       uint32(1578775030),                  // Uint32()
+       uint32(1243308993),                  // Uint32()
+       uint32(826517535),                   // Uint32()
+       uint32(2814630155),                  // Uint32()
+       uint32(3853314576),                  // Uint32()
+       uint32(718781857),                   // Uint32()
+       uint32(1239465936),                  // Uint32()
+       uint32(3876658295),                  // Uint32()
+       uint32(3649778518),                  // Uint32()
+       uint32(1172727096),                  // Uint32()
+       uint32(2615979505),                  // Uint32()
+       uint32(1089444252),                  // Uint32()
+       uint32(3327114623),                  // Uint32()
+       uint32(75079301),                    // Uint32()
+       uint32(3380456901),                  // Uint32()
+       uint32(3433369789),                  // Uint32()
+}
index 608f759da8f315795310291f25fbc9a92c2c5a29..ad63f9bb98e9a4197070f99b82d512b9f3812eb0 100644 (file)
@@ -8,6 +8,7 @@ import (
        "bytes"
        "errors"
        "fmt"
+       "sort"
        "strings"
        "unicode"
 )
@@ -31,7 +32,14 @@ func FormatMediaType(t string, param map[string]string) string {
        b.WriteByte('/')
        b.WriteString(strings.ToLower(sub))
 
-       for attribute, value := range param {
+       attrs := make([]string, 0, len(param))
+       for a := range param {
+               attrs = append(attrs, a)
+       }
+       sort.Strings(attrs)
+
+       for _, attribute := range attrs {
+               value := param[attribute]
                b.WriteByte(';')
                b.WriteByte(' ')
                if !isToken(attribute) {
index 29511445bcffcdf323c5e3e8d7c40e04f0e27391..026bfa4d73468117aeca38445aedaf31122b960d 100644 (file)
@@ -293,6 +293,7 @@ var formatTests = []formatTest{
        {"foo/BAR", map[string]string{"": "empty attribute"}, ""},
        {"foo/BAR", map[string]string{"bad attribute": "baz"}, ""},
        {"foo/BAR", map[string]string{"nonascii": "not an ascii character: ä"}, ""},
+       {"foo/bar", map[string]string{"a": "av", "b": "bv", "c": "cv"}, "foo/bar; a=av; b=bv; c=cv"},
 }
 
 func TestFormatMediaType(t *testing.T) {
index 2b4f5b433ec51c3b6643349e1268dd7db0f8d0f3..7382efab96723a66a87dadaf73e9a199fa4461c2 100644 (file)
@@ -81,12 +81,16 @@ func (p *Part) parseContentDisposition() {
        }
 }
 
-// NewReader creates a new multipart Reader reading from reader using the
+// NewReader creates a new multipart Reader reading from r using the
 // given MIME boundary.
-func NewReader(reader io.Reader, boundary string) *Reader {
+//
+// The boundary is usually obtained from the "boundary" parameter of
+// the message's "Content-Type" header. Use mime.ParseMediaType to
+// parse such headers.
+func NewReader(r io.Reader, boundary string) *Reader {
        b := []byte("\r\n--" + boundary + "--")
        return &Reader{
-               bufReader: bufio.NewReader(reader),
+               bufReader: bufio.NewReader(r),
 
                nl:               b[:2],
                nlDashBoundary:   b[:len(b)-2],
index 8a95f7f037b65291215f6d63f6e6f27dc898ea31..c4de3eb756634e4b10ab683409f24cff0cc317b1 100644 (file)
@@ -131,7 +131,7 @@ func TestQPExhaustive(t *testing.T) {
                                        return
                                }
                                if strings.HasSuffix(errStr, "0x0a") || strings.HasSuffix(errStr, "0x0d") {
-                                       // bunch of cases; since whitespace at the end of of a line before \n is removed.
+                                       // bunch of cases; since whitespace at the end of a line before \n is removed.
                                        return
                                }
                        }
index d949ba3f3e5ece6473337cdb51935a7bb32d39c6..1d394315a49b0cafd80d0177340a6f6c1d983e55 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.
 
-// +build darwin dragonfly freebsd linux netbsd openbsd solaris
+// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
 
 package mime
 
index 3852fc22987ae3bdebf9d1b13bebecab884a2f6a..ce46f2e8c3aec98c0808654c327796c5986ee738 100644 (file)
@@ -3,7 +3,7 @@
 // license that can be found in the LICENSE file.
 
 // +build !netgo
-// +build darwin dragonfly freebsd
+// +build darwin dragonfly freebsd solaris
 
 package net
 
index 7250dcb85ad766d9f8897d776041640bfe6bdb76..37bb4e2c0710ee7022a6168ba708d43a0dec8b0f 100644 (file)
@@ -16,11 +16,11 @@ import (
 
 var connTests = []struct {
        net  string
-       addr func() string
+       addr string
 }{
-       {"tcp", func() string { return "127.0.0.1:0" }},
-       {"unix", testUnixAddr},
-       {"unixpacket", testUnixAddr},
+       {"tcp", "127.0.0.1:0"},
+       {"unix", testUnixAddr()},
+       {"unixpacket", testUnixAddr()},
 }
 
 // someTimeout is used just to test that net.Conn implementations
@@ -31,18 +31,21 @@ const someTimeout = 10 * time.Second
 func TestConnAndListener(t *testing.T) {
        for _, tt := range connTests {
                switch tt.net {
-               case "unix", "unixpacket":
+               case "unix":
                        switch runtime.GOOS {
-                       case "plan9", "windows":
+                       case "nacl", "plan9", "windows":
                                continue
                        }
-                       if tt.net == "unixpacket" && runtime.GOOS != "linux" {
+               case "unixpacket":
+                       switch runtime.GOOS {
+                       case "darwin", "nacl", "openbsd", "plan9", "windows":
+                               continue
+                       case "freebsd": // FreeBSD 8 doesn't support unixpacket
                                continue
                        }
                }
 
-               addr := tt.addr()
-               ln, err := Listen(tt.net, addr)
+               ln, err := Listen(tt.net, tt.addr)
                if err != nil {
                        t.Fatalf("Listen failed: %v", err)
                }
@@ -52,7 +55,7 @@ func TestConnAndListener(t *testing.T) {
                        case "unix", "unixpacket":
                                os.Remove(addr)
                        }
-               }(ln, tt.net, addr)
+               }(ln, tt.net, tt.addr)
                if ln.Addr().Network() != tt.net {
                        t.Fatalf("got %v; expected %v", ln.Addr().Network(), tt.net)
                }
index 70b66e70d1564f4ee2adb70c3df9a2c709a0d922..93569c253cdafefbd8f567a7c2ec5e58c6077e4a 100644 (file)
@@ -44,6 +44,12 @@ type Dialer struct {
        // destination is a host name that has multiple address family
        // DNS records.
        DualStack bool
+
+       // KeepAlive specifies the keep-alive period for an active
+       // network connection.
+       // If zero, keep-alives are not enabled. Network protocols
+       // that do not support keep-alives ignore this field.
+       KeepAlive time.Duration
 }
 
 // Return either now+Timeout or Deadline, whichever comes first.
@@ -162,9 +168,19 @@ func (d *Dialer) Dial(network, address string) (Conn, error) {
                        return dialMulti(network, address, d.LocalAddr, ras, deadline)
                }
        }
-       return dial(network, ra.toAddr(), dialer, d.deadline())
+       c, err := dial(network, ra.toAddr(), dialer, d.deadline())
+       if d.KeepAlive > 0 && err == nil {
+               if tc, ok := c.(*TCPConn); ok {
+                       tc.SetKeepAlive(true)
+                       tc.SetKeepAlivePeriod(d.KeepAlive)
+                       testHookSetKeepAlive()
+               }
+       }
+       return c, err
 }
 
+var testHookSetKeepAlive = func() {} // changed by dial_test.go
+
 // dialMulti attempts to establish connections to each destination of
 // the list of addresses. It will return the first established
 // connection and close the other connections. Otherwise it returns
index 08a0567ca762321096073f5878197b7eec3f9bf4..f9260fd281baa0870c6479edc1b6c549677d3067 100644 (file)
@@ -425,60 +425,6 @@ func numFD() int {
        panic("numFDs not implemented on " + runtime.GOOS)
 }
 
-// Assert that a failed Dial attempt does not leak
-// runtime.PollDesc structures
-func TestDialFailPDLeak(t *testing.T) {
-       if testing.Short() {
-               t.Skip("skipping test in short mode")
-       }
-       if runtime.GOOS == "windows" && runtime.GOARCH == "386" {
-               // Just skip the test because it takes too long.
-               t.Skipf("skipping test on %q/%q", runtime.GOOS, runtime.GOARCH)
-       }
-
-       maxprocs := runtime.GOMAXPROCS(0)
-       loops := 10 + maxprocs
-       // 500 is enough to turn over the chunk of pollcache.
-       // See allocPollDesc in runtime/netpoll.goc.
-       const count = 500
-       var old runtime.MemStats // used by sysdelta
-       runtime.ReadMemStats(&old)
-       sysdelta := func() uint64 {
-               var new runtime.MemStats
-               runtime.ReadMemStats(&new)
-               delta := old.Sys - new.Sys
-               old = new
-               return delta
-       }
-       d := &Dialer{Timeout: time.Nanosecond} // don't bother TCP with handshaking
-       failcount := 0
-       for i := 0; i < loops; i++ {
-               var wg sync.WaitGroup
-               for i := 0; i < count; i++ {
-                       wg.Add(1)
-                       go func() {
-                               defer wg.Done()
-                               if c, err := d.Dial("tcp", "127.0.0.1:1"); err == nil {
-                                       t.Error("dial should not succeed")
-                                       c.Close()
-                               }
-                       }()
-               }
-               wg.Wait()
-               if t.Failed() {
-                       t.FailNow()
-               }
-               if delta := sysdelta(); delta > 0 {
-                       failcount++
-               }
-               // there are always some allocations on the first loop
-               if failcount > maxprocs+2 {
-                       t.Error("detected possible memory leak in runtime")
-                       t.FailNow()
-               }
-       }
-}
-
 func TestDialer(t *testing.T) {
        ln, err := Listen("tcp4", "127.0.0.1:0")
        if err != nil {
@@ -555,3 +501,36 @@ func TestDialDualStackLocalhost(t *testing.T) {
                }
        }
 }
+
+func TestDialerKeepAlive(t *testing.T) {
+       ln := newLocalListener(t)
+       defer ln.Close()
+       defer func() {
+               testHookSetKeepAlive = func() {}
+       }()
+       go func() {
+               for {
+                       c, err := ln.Accept()
+                       if err != nil {
+                               return
+                       }
+                       c.Close()
+               }
+       }()
+       for _, keepAlive := range []bool{false, true} {
+               got := false
+               testHookSetKeepAlive = func() { got = true }
+               var d Dialer
+               if keepAlive {
+                       d.KeepAlive = 30 * time.Second
+               }
+               c, err := d.Dial("tcp", ln.Addr().String())
+               if err != nil {
+                       t.Fatal(err)
+               }
+               c.Close()
+               if got != keepAlive {
+                       t.Errorf("Dialer.KeepAlive = %v: SetKeepAlive called = %v, want %v", d.KeepAlive, got, !got)
+               }
+       }
+}
index 01db4372945c230f7df3a4f6d72bb7bae353e321..9bffa11f9165df0a338cdd300fbd9c304225575a 100644 (file)
@@ -191,10 +191,10 @@ func (addrs byPriorityWeight) shuffleByWeight() {
        }
        for sum > 0 && len(addrs) > 1 {
                s := 0
-               n := rand.Intn(sum + 1)
+               n := rand.Intn(sum)
                for i := range addrs {
                        s += int(addrs[i].Weight)
-                       if s >= n {
+                       if s > n {
                                if i > 0 {
                                        t := addrs[i]
                                        copy(addrs[1:i+1], addrs[0:i])
diff --git a/libgo/go/net/dnsclient_test.go b/libgo/go/net/dnsclient_test.go
new file mode 100644 (file)
index 0000000..435eb35
--- /dev/null
@@ -0,0 +1,69 @@
+// Copyright 2014 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 net
+
+import (
+       "math/rand"
+       "testing"
+)
+
+func checkDistribution(t *testing.T, data []*SRV, margin float64) {
+       sum := 0
+       for _, srv := range data {
+               sum += int(srv.Weight)
+       }
+
+       results := make(map[string]int)
+
+       count := 1000
+       for j := 0; j < count; j++ {
+               d := make([]*SRV, len(data))
+               copy(d, data)
+               byPriorityWeight(d).shuffleByWeight()
+               key := d[0].Target
+               results[key] = results[key] + 1
+       }
+
+       actual := results[data[0].Target]
+       expected := float64(count) * float64(data[0].Weight) / float64(sum)
+       diff := float64(actual) - expected
+       t.Logf("actual: %v diff: %v e: %v m: %v", actual, diff, expected, margin)
+       if diff < 0 {
+               diff = -diff
+       }
+       if diff > (expected * margin) {
+               t.Errorf("missed target weight: expected %v, %v", expected, actual)
+       }
+}
+
+func testUniformity(t *testing.T, size int, margin float64) {
+       rand.Seed(1)
+       data := make([]*SRV, size)
+       for i := 0; i < size; i++ {
+               data[i] = &SRV{Target: string('a' + i), Weight: 1}
+       }
+       checkDistribution(t, data, margin)
+}
+
+func TestUniformity(t *testing.T) {
+       testUniformity(t, 2, 0.05)
+       testUniformity(t, 3, 0.10)
+       testUniformity(t, 10, 0.20)
+       testWeighting(t, 0.05)
+}
+
+func testWeighting(t *testing.T, margin float64) {
+       rand.Seed(1)
+       data := []*SRV{
+               {Target: "a", Weight: 60},
+               {Target: "b", Weight: 30},
+               {Target: "c", Weight: 10},
+       }
+       checkDistribution(t, data, margin)
+}
+
+func TestWeighting(t *testing.T) {
+       testWeighting(t, 0.05)
+}
index a30c9a73d7e0e920f7a8a7ee33e79ff44b39b0a4..3713efd0e3c9f27b3d95596d2ed3f00c0b82649c 100644 (file)
@@ -2,13 +2,12 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build darwin dragonfly freebsd linux netbsd openbsd
+// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
 
 // DNS client: see RFC 1035.
 // Has to be linked into package net for Dial.
 
 // TODO(rsc):
-//     Check periodically whether /etc/resolv.conf has changed.
 //     Could potentially handle many outstanding lookups faster.
 //     Could have a small cache.
 //     Random UDP source port (net.Dial should do that for us).
@@ -19,6 +18,7 @@ package net
 import (
        "io"
        "math/rand"
+       "os"
        "sync"
        "time"
 )
@@ -156,33 +156,90 @@ func convertRR_AAAA(records []dnsRR) []IP {
        return addrs
 }
 
-var cfg *dnsConfig
-var dnserr error
+var cfg struct {
+       ch        chan struct{}
+       mu        sync.RWMutex // protects dnsConfig and dnserr
+       dnsConfig *dnsConfig
+       dnserr    error
+}
+var onceLoadConfig sync.Once
 
 // Assume dns config file is /etc/resolv.conf here
-func loadConfig() { cfg, dnserr = dnsReadConfig("/etc/resolv.conf") }
+func loadDefaultConfig() {
+       loadConfig("/etc/resolv.conf", 5*time.Second, nil)
+}
 
-var onceLoadConfig sync.Once
+func loadConfig(resolvConfPath string, reloadTime time.Duration, quit <-chan chan struct{}) {
+       var mtime time.Time
+       cfg.ch = make(chan struct{}, 1)
+       if fi, err := os.Stat(resolvConfPath); err != nil {
+               cfg.dnserr = err
+       } else {
+               mtime = fi.ModTime()
+               cfg.dnsConfig, cfg.dnserr = dnsReadConfig(resolvConfPath)
+       }
+       go func() {
+               for {
+                       time.Sleep(reloadTime)
+                       select {
+                       case qresp := <-quit:
+                               qresp <- struct{}{}
+                               return
+                       case <-cfg.ch:
+                       }
+
+                       // In case of error, we keep the previous config
+                       fi, err := os.Stat(resolvConfPath)
+                       if err != nil {
+                               continue
+                       }
+                       // If the resolv.conf mtime didn't change, do not reload
+                       m := fi.ModTime()
+                       if m.Equal(mtime) {
+                               continue
+                       }
+                       mtime = m
+                       // In case of error, we keep the previous config
+                       ncfg, err := dnsReadConfig(resolvConfPath)
+                       if err != nil || len(ncfg.servers) == 0 {
+                               continue
+                       }
+                       cfg.mu.Lock()
+                       cfg.dnsConfig = ncfg
+                       cfg.dnserr = nil
+                       cfg.mu.Unlock()
+               }
+       }()
+}
 
 func lookup(name string, qtype uint16) (cname string, addrs []dnsRR, err error) {
        if !isDomainName(name) {
                return name, nil, &DNSError{Err: "invalid domain name", Name: name}
        }
-       onceLoadConfig.Do(loadConfig)
-       if dnserr != nil || cfg == nil {
-               err = dnserr
+       onceLoadConfig.Do(loadDefaultConfig)
+
+       select {
+       case cfg.ch <- struct{}{}:
+       default:
+       }
+
+       cfg.mu.RLock()
+       defer cfg.mu.RUnlock()
+
+       if cfg.dnserr != nil || cfg.dnsConfig == nil {
+               err = cfg.dnserr
                return
        }
        // If name is rooted (trailing dot) or has enough dots,
        // try it by itself first.
        rooted := len(name) > 0 && name[len(name)-1] == '.'
-       if rooted || count(name, '.') >= cfg.ndots {
+       if rooted || count(name, '.') >= cfg.dnsConfig.ndots {
                rname := name
                if !rooted {
                        rname += "."
                }
                // Can try as ordinary name.
-               cname, addrs, err = tryOneName(cfg, rname, qtype)
+               cname, addrs, err = tryOneName(cfg.dnsConfig, rname, qtype)
                if err == nil {
                        return
                }
@@ -192,12 +249,12 @@ func lookup(name string, qtype uint16) (cname string, addrs []dnsRR, err error)
        }
 
        // Otherwise, try suffixes.
-       for i := 0; i < len(cfg.search); i++ {
-               rname := name + "." + cfg.search[i]
+       for i := 0; i < len(cfg.dnsConfig.search); i++ {
+               rname := name + "." + cfg.dnsConfig.search[i]
                if rname[len(rname)-1] != '.' {
                        rname += "."
                }
-               cname, addrs, err = tryOneName(cfg, rname, qtype)
+               cname, addrs, err = tryOneName(cfg.dnsConfig, rname, qtype)
                if err == nil {
                        return
                }
@@ -208,7 +265,7 @@ func lookup(name string, qtype uint16) (cname string, addrs []dnsRR, err error)
        if !rooted {
                rname += "."
        }
-       cname, addrs, err = tryOneName(cfg, rname, qtype)
+       cname, addrs, err = tryOneName(cfg.dnsConfig, rname, qtype)
        if err == nil {
                return
        }
@@ -233,11 +290,6 @@ func goLookupHost(name string) (addrs []string, err error) {
        if len(addrs) > 0 {
                return
        }
-       onceLoadConfig.Do(loadConfig)
-       if dnserr != nil || cfg == nil {
-               err = dnserr
-               return
-       }
        ips, err := goLookupIP(name)
        if err != nil {
                return
@@ -268,11 +320,6 @@ func goLookupIP(name string) (addrs []IP, err error) {
                        return
                }
        }
-       onceLoadConfig.Do(loadConfig)
-       if dnserr != nil || cfg == nil {
-               err = dnserr
-               return
-       }
        var records []dnsRR
        var cname string
        var err4, err6 error
@@ -308,11 +355,6 @@ func goLookupIP(name string) (addrs []IP, err error) {
 // depending on our lookup code, so that Go and C get the same
 // answers.
 func goLookupCNAME(name string) (cname string, err error) {
-       onceLoadConfig.Do(loadConfig)
-       if dnserr != nil || cfg == nil {
-               err = dnserr
-               return
-       }
        _, rr, err := lookup(name, dnsTypeCNAME)
        if err != nil {
                return
index 47dcb563bc54e8a91ee7dbd199a3ebbd8c597650..2350142d61052e06f40644baa3200016df3bb27a 100644 (file)
@@ -2,12 +2,18 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build darwin dragonfly freebsd linux netbsd openbsd
+// +build darwin dragonfly freebsd linux netbsd openbsd solaris
 
 package net
 
 import (
+       "io"
+       "io/ioutil"
+       "os"
+       "path"
+       "reflect"
        "testing"
+       "time"
 )
 
 func TestTCPLookup(t *testing.T) {
@@ -25,3 +31,129 @@ func TestTCPLookup(t *testing.T) {
                t.Fatalf("exchange failed: %v", err)
        }
 }
+
+type resolvConfTest struct {
+       *testing.T
+       dir     string
+       path    string
+       started bool
+       quitc   chan chan struct{}
+}
+
+func newResolvConfTest(t *testing.T) *resolvConfTest {
+       dir, err := ioutil.TempDir("", "resolvConfTest")
+       if err != nil {
+               t.Fatalf("could not create temp dir: %v", err)
+       }
+
+       // Disable the default loadConfig
+       onceLoadConfig.Do(func() {})
+
+       r := &resolvConfTest{
+               T:     t,
+               dir:   dir,
+               path:  path.Join(dir, "resolv.conf"),
+               quitc: make(chan chan struct{}),
+       }
+
+       return r
+}
+
+func (r *resolvConfTest) Start() {
+       loadConfig(r.path, 100*time.Millisecond, r.quitc)
+       r.started = true
+}
+
+func (r *resolvConfTest) SetConf(s string) {
+       // Make sure the file mtime will be different once we're done here,
+       // even on systems with coarse (1s) mtime resolution.
+       time.Sleep(time.Second)
+
+       f, err := os.OpenFile(r.path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0600)
+       if err != nil {
+               r.Fatalf("failed to create temp file %s: %v", r.path, err)
+       }
+       if _, err := io.WriteString(f, s); err != nil {
+               f.Close()
+               r.Fatalf("failed to write temp file: %v", err)
+       }
+       f.Close()
+
+       if r.started {
+               cfg.ch <- struct{}{} // fill buffer
+               cfg.ch <- struct{}{} // wait for reload to begin
+               cfg.ch <- struct{}{} // wait for reload to complete
+       }
+}
+
+func (r *resolvConfTest) WantServers(want []string) {
+       cfg.mu.RLock()
+       defer cfg.mu.RUnlock()
+       if got := cfg.dnsConfig.servers; !reflect.DeepEqual(got, want) {
+               r.Fatalf("Unexpected dns server loaded, got %v want %v", got, want)
+       }
+}
+
+func (r *resolvConfTest) Close() {
+       resp := make(chan struct{})
+       r.quitc <- resp
+       <-resp
+       if err := os.RemoveAll(r.dir); err != nil {
+               r.Logf("failed to remove temp dir %s: %v", r.dir, err)
+       }
+}
+
+func TestReloadResolvConfFail(t *testing.T) {
+       if testing.Short() || !*testExternal {
+               t.Skip("skipping test to avoid external network")
+       }
+
+       r := newResolvConfTest(t)
+       defer r.Close()
+
+       // resolv.conf.tmp does not exist yet
+       r.Start()
+       if _, err := goLookupIP("golang.org"); err == nil {
+               t.Fatal("goLookupIP(missing) succeeded")
+       }
+
+       r.SetConf("nameserver 8.8.8.8")
+       if _, err := goLookupIP("golang.org"); err != nil {
+               t.Fatalf("goLookupIP(missing; good) failed: %v", err)
+       }
+
+       // Using a bad resolv.conf while we had a good
+       // one before should not update the config
+       r.SetConf("")
+       if _, err := goLookupIP("golang.org"); err != nil {
+               t.Fatalf("goLookupIP(missing; good; bad) failed: %v", err)
+       }
+}
+
+func TestReloadResolvConfChange(t *testing.T) {
+       if testing.Short() || !*testExternal {
+               t.Skip("skipping test to avoid external network")
+       }
+
+       r := newResolvConfTest(t)
+       defer r.Close()
+
+       r.SetConf("nameserver 8.8.8.8")
+       r.Start()
+
+       if _, err := goLookupIP("golang.org"); err != nil {
+               t.Fatalf("goLookupIP(good) failed: %v", err)
+       }
+       r.WantServers([]string{"[8.8.8.8]"})
+
+       // Using a bad resolv.conf when we had a good one
+       // before should not update the config
+       r.SetConf("")
+       if _, err := goLookupIP("golang.org"); err != nil {
+               t.Fatalf("goLookupIP(good; bad) failed: %v", err)
+       }
+
+       // A new good config should get picked up
+       r.SetConf("nameserver 8.8.4.4")
+       r.WantServers([]string{"[8.8.4.4]"})
+}
index 7856ebc80de5ca45bc70c63b389c86b8cc0b1152..af288253e09bacea861f751c779ae823c98035b2 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.
 
-// +build darwin dragonfly freebsd linux netbsd openbsd
+// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
 
 // Read system DNS config from /etc/resolv.conf
 
index 697c69f995952678a532dc1edfed0ed7eb6bf841..37ed4931dbe70ac6ceb6528a312ec09d6692069d 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.
 
-// +build darwin dragonfly freebsd linux netbsd openbsd
+// +build darwin dragonfly freebsd linux netbsd openbsd solaris
 
 package net
 
index 8383084b7a284cbad7429bf16799c7c1f7dd7ef4..c34ec59b996371a6dc63ee919905f68baf3594d6 100644 (file)
@@ -63,7 +63,8 @@ func TestMutexCloseUnblock(t *testing.T) {
        for i := 0; i < 4; i++ {
                go func() {
                        if mu.RWLock(true) {
-                               t.Fatal("broken")
+                               t.Error("broken")
+                               return
                        }
                        c <- true
                }()
@@ -138,36 +139,44 @@ func TestMutexStress(t *testing.T) {
                                switch r.Intn(3) {
                                case 0:
                                        if !mu.Incref() {
-                                               t.Fatal("broken")
+                                               t.Error("broken")
+                                               return
                                        }
                                        if mu.Decref() {
-                                               t.Fatal("broken")
+                                               t.Error("broken")
+                                               return
                                        }
                                case 1:
                                        if !mu.RWLock(true) {
-                                               t.Fatal("broken")
+                                               t.Error("broken")
+                                               return
                                        }
                                        // Ensure that it provides mutual exclusion for readers.
                                        if readState[0] != readState[1] {
-                                               t.Fatal("broken")
+                                               t.Error("broken")
+                                               return
                                        }
                                        readState[0]++
                                        readState[1]++
                                        if mu.RWUnlock(true) {
-                                               t.Fatal("broken")
+                                               t.Error("broken")
+                                               return
                                        }
                                case 2:
                                        if !mu.RWLock(false) {
-                                               t.Fatal("broken")
+                                               t.Error("broken")
+                                               return
                                        }
                                        // Ensure that it provides mutual exclusion for writers.
                                        if writeState[0] != writeState[1] {
-                                               t.Fatal("broken")
+                                               t.Error("broken")
+                                               return
                                        }
                                        writeState[0]++
                                        writeState[1]++
                                        if mu.RWUnlock(false) {
-                                               t.Fatal("broken")
+                                               t.Error("broken")
+                                               return
                                        }
                                }
                        }
index 4309a87c3a4a69e397286b453bc0e1830b9163f0..5fe8effc2954dc3ebd7ebe965dd253f36aaf9c85 100644 (file)
@@ -150,14 +150,14 @@ func (fd *netFD) Write(b []byte) (n int, err error) {
        return fd.data.Write(b)
 }
 
-func (fd *netFD) CloseRead() error {
+func (fd *netFD) closeRead() error {
        if !fd.ok() {
                return syscall.EINVAL
        }
        return syscall.EPLAN9
 }
 
-func (fd *netFD) CloseWrite() error {
+func (fd *netFD) closeWrite() error {
        if !fd.ok() {
                return syscall.EINVAL
        }
diff --git a/libgo/go/net/fd_poll_nacl.go b/libgo/go/net/fd_poll_nacl.go
new file mode 100644 (file)
index 0000000..a3701f8
--- /dev/null
@@ -0,0 +1,94 @@
+// Copyright 2013 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 net
+
+import (
+       "syscall"
+       "time"
+)
+
+type pollDesc struct {
+       fd      *netFD
+       closing bool
+}
+
+func (pd *pollDesc) Init(fd *netFD) error { pd.fd = fd; return nil }
+
+func (pd *pollDesc) Close() {}
+
+func (pd *pollDesc) Lock() {}
+
+func (pd *pollDesc) Unlock() {}
+
+func (pd *pollDesc) Wakeup() {}
+
+func (pd *pollDesc) Evict() bool {
+       pd.closing = true
+       if pd.fd != nil {
+               syscall.StopIO(pd.fd.sysfd)
+       }
+       return false
+}
+
+func (pd *pollDesc) Prepare(mode int) error {
+       if pd.closing {
+               return errClosing
+       }
+       return nil
+}
+
+func (pd *pollDesc) PrepareRead() error { return pd.Prepare('r') }
+
+func (pd *pollDesc) PrepareWrite() error { return pd.Prepare('w') }
+
+func (pd *pollDesc) Wait(mode int) error {
+       if pd.closing {
+               return errClosing
+       }
+       return errTimeout
+}
+
+func (pd *pollDesc) WaitRead() error { return pd.Wait('r') }
+
+func (pd *pollDesc) WaitWrite() error { return pd.Wait('w') }
+
+func (pd *pollDesc) WaitCanceled(mode int) {}
+
+func (pd *pollDesc) WaitCanceledRead() {}
+
+func (pd *pollDesc) WaitCanceledWrite() {}
+
+func (fd *netFD) setDeadline(t time.Time) error {
+       return setDeadlineImpl(fd, t, 'r'+'w')
+}
+
+func (fd *netFD) setReadDeadline(t time.Time) error {
+       return setDeadlineImpl(fd, t, 'r')
+}
+
+func (fd *netFD) setWriteDeadline(t time.Time) error {
+       return setDeadlineImpl(fd, t, 'w')
+}
+
+func setDeadlineImpl(fd *netFD, t time.Time, mode int) error {
+       d := t.UnixNano()
+       if t.IsZero() {
+               d = 0
+       }
+       if err := fd.incref(); err != nil {
+               return err
+       }
+       switch mode {
+       case 'r':
+               syscall.SetReadDeadline(fd.sysfd, d)
+       case 'w':
+               syscall.SetWriteDeadline(fd.sysfd, d)
+       case 'r' + 'w':
+               syscall.SetReadDeadline(fd.sysfd, d)
+               syscall.SetWriteDeadline(fd.sysfd, d)
+       }
+       fd.decref()
+       return nil
+}
index e2b2768864afb537da731c8e6a9f9c0fec2275c1..2bddc836c75a7af4492cbb5a748c74fc6a3ad991 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.
 
-// +build darwin dragonfly freebsd linux netbsd openbsd windows
+// +build darwin dragonfly freebsd linux netbsd openbsd windows solaris
 
 package net
 
@@ -12,6 +12,9 @@ import (
        "time"
 )
 
+// runtimeNano returns the current value of the runtime clock in nanoseconds.
+func runtimeNano() int64
+
 func runtime_pollServerInit()
 func runtime_pollOpen(fd uintptr) (uintptr, int)
 func runtime_pollClose(ctx uintptr)
@@ -128,7 +131,7 @@ func (fd *netFD) setWriteDeadline(t time.Time) error {
 }
 
 func setDeadlineImpl(fd *netFD, t time.Time, mode int) error {
-       d := t.UnixNano()
+       d := runtimeNano() + int64(t.Sub(time.Now()))
        if t.IsZero() {
                d = 0
        }
index a89303e37e9f3093998d0a4c51ea93bb74e12fc2..ca6aac3b42e3afafd72a3b01e8ec6ff06f49b5f3 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.
 
-// +build darwin dragonfly freebsd linux netbsd openbsd
+// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
 
 package net
 
@@ -75,29 +75,47 @@ func (fd *netFD) connect(la, ra syscall.Sockaddr) error {
        if err := fd.pd.PrepareWrite(); err != nil {
                return err
        }
-       for {
-               err := syscall.Connect(fd.sysfd, ra)
-               if err == nil || err == syscall.EISCONN {
-                       break
-               }
-
+       switch err := syscall.Connect(fd.sysfd, ra); err {
+       case syscall.EINPROGRESS, syscall.EALREADY, syscall.EINTR:
+       case nil, syscall.EISCONN:
+               return nil
+       case syscall.EINVAL:
                // On Solaris we can see EINVAL if the socket has
                // already been accepted and closed by the server.
                // Treat this as a successful connection--writes to
                // the socket will see EOF.  For details and a test
                // case in C see http://golang.org/issue/6828.
-               if runtime.GOOS == "solaris" && err == syscall.EINVAL {
-                       break
+               if runtime.GOOS == "solaris" {
+                       return nil
                }
-
-               if err != syscall.EINPROGRESS && err != syscall.EALREADY && err != syscall.EINTR {
+               fallthrough
+       default:
+               return err
+       }
+       for {
+               // Performing multiple connect system calls on a
+               // non-blocking socket under Unix variants does not
+               // necessarily result in earlier errors being
+               // returned. Instead, once runtime-integrated network
+               // poller tells us that the socket is ready, get the
+               // SO_ERROR socket option to see if the connection
+               // succeeded or failed. See issue 7474 for further
+               // details.
+               if err := fd.pd.WaitWrite(); err != nil {
                        return err
                }
-               if err = fd.pd.WaitWrite(); err != nil {
+               nerr, err := syscall.GetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_ERROR)
+               if err != nil {
+                       return err
+               }
+               switch err := syscall.Errno(nerr); err {
+               case syscall.EINPROGRESS, syscall.EALREADY, syscall.EINTR:
+               case syscall.Errno(0), syscall.EISCONN:
+                       return nil
+               default:
                        return err
                }
        }
-       return nil
 }
 
 func (fd *netFD) destroy() {
@@ -190,11 +208,11 @@ func (fd *netFD) shutdown(how int) error {
        return nil
 }
 
-func (fd *netFD) CloseRead() error {
+func (fd *netFD) closeRead() error {
        return fd.shutdown(syscall.SHUT_RD)
 }
 
-func (fd *netFD) CloseWrite() error {
+func (fd *netFD) closeWrite() error {
        return fd.shutdown(syscall.SHUT_WR)
 }
 
@@ -225,7 +243,7 @@ func (fd *netFD) Read(p []byte) (n int, err error) {
        return
 }
 
-func (fd *netFD) ReadFrom(p []byte) (n int, sa syscall.Sockaddr, err error) {
+func (fd *netFD) readFrom(p []byte) (n int, sa syscall.Sockaddr, err error) {
        if err := fd.readLock(); err != nil {
                return 0, nil, err
        }
@@ -252,7 +270,7 @@ func (fd *netFD) ReadFrom(p []byte) (n int, sa syscall.Sockaddr, err error) {
        return
 }
 
-func (fd *netFD) ReadMsg(p []byte, oob []byte) (n, oobn, flags int, sa syscall.Sockaddr, err error) {
+func (fd *netFD) readMsg(p []byte, oob []byte) (n, oobn, flags int, sa syscall.Sockaddr, err error) {
        if err := fd.readLock(); err != nil {
                return 0, 0, 0, nil, err
        }
@@ -323,7 +341,7 @@ func (fd *netFD) Write(p []byte) (nn int, err error) {
        return nn, err
 }
 
-func (fd *netFD) WriteTo(p []byte, sa syscall.Sockaddr) (n int, err error) {
+func (fd *netFD) writeTo(p []byte, sa syscall.Sockaddr) (n int, err error) {
        if err := fd.writeLock(); err != nil {
                return 0, err
        }
@@ -348,7 +366,7 @@ func (fd *netFD) WriteTo(p []byte, sa syscall.Sockaddr) (n int, err error) {
        return
 }
 
-func (fd *netFD) WriteMsg(p []byte, oob []byte, sa syscall.Sockaddr) (n int, oobn int, err error) {
+func (fd *netFD) writeMsg(p []byte, oob []byte, sa syscall.Sockaddr) (n int, oobn int, err error) {
        if err := fd.writeLock(); err != nil {
                return 0, 0, err
        }
@@ -357,7 +375,7 @@ func (fd *netFD) WriteMsg(p []byte, oob []byte, sa syscall.Sockaddr) (n int, oob
                return 0, 0, &OpError{"write", fd.net, fd.raddr, err}
        }
        for {
-               err = syscall.Sendmsg(fd.sysfd, p, oob, sa, 0)
+               n, err = syscall.SendmsgN(fd.sysfd, p, oob, sa, 0)
                if err == syscall.EAGAIN {
                        if err = fd.pd.WaitWrite(); err == nil {
                                continue
@@ -366,7 +384,6 @@ func (fd *netFD) WriteMsg(p []byte, oob []byte, sa syscall.Sockaddr) (n int, oob
                break
        }
        if err == nil {
-               n = len(p)
                oobn = len(oob)
        } else {
                err = &OpError{"write", fd.net, fd.raddr, err}
@@ -465,7 +482,6 @@ func dupCloseOnExecOld(fd int) (newfd int, err error) {
 func (fd *netFD) dup() (f *os.File, err error) {
        ns, err := dupCloseOnExec(fd.sysfd)
        if err != nil {
-               syscall.ForkLock.RUnlock()
                return nil, &OpError{"dup", fd.net, fd.laddr, err}
        }
 
index 65d3e69a764036a397dafbef211252c8ff0b261a..fe8e8ff6a88683c0e8ada5417717d236c2e26ad5 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.
 
-// +build darwin dragonfly freebsd linux netbsd openbsd
+// +build darwin dragonfly freebsd linux netbsd openbsd solaris
 
 package net
 
index 0f8d6de5b547b3b0f544fd01a99ecd1a5ab633cb..a1f6bc5f8148cb40fcbf45c7507da707dd5c89dd 100644 (file)
@@ -431,11 +431,11 @@ func (fd *netFD) shutdown(how int) error {
        return nil
 }
 
-func (fd *netFD) CloseRead() error {
+func (fd *netFD) closeRead() error {
        return fd.shutdown(syscall.SHUT_RD)
 }
 
-func (fd *netFD) CloseWrite() error {
+func (fd *netFD) closeWrite() error {
        return fd.shutdown(syscall.SHUT_WR)
 }
 
@@ -458,7 +458,7 @@ func (fd *netFD) Read(buf []byte) (int, error) {
        return n, err
 }
 
-func (fd *netFD) ReadFrom(buf []byte) (n int, sa syscall.Sockaddr, err error) {
+func (fd *netFD) readFrom(buf []byte) (n int, sa syscall.Sockaddr, err error) {
        if len(buf) == 0 {
                return 0, nil, nil
        }
@@ -497,7 +497,7 @@ func (fd *netFD) Write(buf []byte) (int, error) {
        })
 }
 
-func (fd *netFD) WriteTo(buf []byte, sa syscall.Sockaddr) (int, error) {
+func (fd *netFD) writeTo(buf []byte, sa syscall.Sockaddr) (int, error) {
        if len(buf) == 0 {
                return 0, nil
        }
@@ -628,10 +628,10 @@ func (fd *netFD) dup() (*os.File, error) {
 
 var errNoSupport = errors.New("address family not supported")
 
-func (fd *netFD) ReadMsg(p []byte, oob []byte) (n, oobn, flags int, sa syscall.Sockaddr, err error) {
+func (fd *netFD) readMsg(p []byte, oob []byte) (n, oobn, flags int, sa syscall.Sockaddr, err error) {
        return 0, 0, 0, nil, errNoSupport
 }
 
-func (fd *netFD) WriteMsg(p []byte, oob []byte, sa syscall.Sockaddr) (n int, oobn int, err error) {
+func (fd *netFD) writeMsg(p []byte, oob []byte, sa syscall.Sockaddr) (n int, oobn int, err error) {
        return 0, 0, errNoSupport
 }
index e4615b74fc376de976fe9d9e0e95e8348ac5cfb1..d81bca78249472e4b001924604c64b84f7d5813d 100644 (file)
@@ -181,7 +181,7 @@ var filePacketConnTests = []struct {
 
 func TestFilePacketConn(t *testing.T) {
        switch runtime.GOOS {
-       case "plan9", "windows":
+       case "nacl", "plan9", "windows":
                t.Skipf("skipping test on %q", runtime.GOOS)
        }
 
index 38ae47f7847d1d3088b8e0d56495f8b0732f079e..07b3ecf62637f44d726a674109f9fa01696974b6 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.
 
-// +build darwin dragonfly freebsd linux netbsd openbsd
+// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
 
 package net
 
index d27cc4dc9a8abfb6ba474cf85f26488d7903f6a2..ec95a972c1a425b860c78d6456f320ec5d748d26 100644 (file)
@@ -214,12 +214,17 @@ func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
                internalError(err)
                return
        }
+       if hook := testHookStartProcess; hook != nil {
+               hook(cmd.Process)
+       }
        defer cmd.Wait()
        defer stdoutRead.Close()
 
        linebody := bufio.NewReaderSize(stdoutRead, 1024)
        headers := make(http.Header)
        statusCode := 0
+       headerLines := 0
+       sawBlankLine := false
        for {
                line, isPrefix, err := linebody.ReadLine()
                if isPrefix {
@@ -236,8 +241,10 @@ func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
                        return
                }
                if len(line) == 0 {
+                       sawBlankLine = true
                        break
                }
+               headerLines++
                parts := strings.SplitN(string(line), ":", 2)
                if len(parts) < 2 {
                        h.printf("cgi: bogus header line: %s", string(line))
@@ -263,6 +270,11 @@ func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
                        headers.Add(header, val)
                }
        }
+       if headerLines == 0 || !sawBlankLine {
+               rw.WriteHeader(http.StatusInternalServerError)
+               h.printf("cgi: no headers")
+               return
+       }
 
        if loc := headers.Get("Location"); loc != "" {
                if strings.HasPrefix(loc, "/") && h.PathLocationHandler != nil {
@@ -274,6 +286,12 @@ func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
                }
        }
 
+       if statusCode == 0 && headers.Get("Content-Type") == "" {
+               rw.WriteHeader(http.StatusInternalServerError)
+               h.printf("cgi: missing required Content-Type in headers")
+               return
+       }
+
        if statusCode == 0 {
                statusCode = http.StatusOK
        }
@@ -292,6 +310,13 @@ func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
        _, err = io.Copy(rw, linebody)
        if err != nil {
                h.printf("cgi: copy error: %v", err)
+               // And kill the child CGI process so we don't hang on
+               // the deferred cmd.Wait above if the error was just
+               // the client (rw) going away. If it was a read error
+               // (because the child died itself), then the extra
+               // kill of an already-dead process is harmless (the PID
+               // won't be reused until the Wait above).
+               cmd.Process.Kill()
        }
 }
 
@@ -348,3 +373,5 @@ func upperCaseAndUnderscore(r rune) rune {
        // TODO: other transformations in spec or practice?
        return r
 }
+
+var testHookStartProcess func(*os.Process) // nil except for some tests
index e1a78c8f62f4cdef7ea6b3f58a587541f33d70a9..18c4803e71b55374881674429d8e49d4adef3a9b 100644 (file)
@@ -9,15 +9,25 @@
 package cgi
 
 import (
+       "bytes"
+       "errors"
        "fmt"
+       "io"
        "net/http"
+       "net/http/httptest"
        "os"
+       "runtime"
        "testing"
+       "time"
 )
 
 // This test is a CGI host (testing host.go) that runs its own binary
 // as a child process testing the other half of CGI (child.go).
 func TestHostingOurselves(t *testing.T) {
+       if runtime.GOOS == "nacl" {
+               t.Skip("skipping on nacl")
+       }
+
        h := &Handler{
                Path: os.Args[0],
                Root: "/test.go",
@@ -51,8 +61,88 @@ func TestHostingOurselves(t *testing.T) {
        }
 }
 
-// Test that a child handler only writing headers works.
+type customWriterRecorder struct {
+       w io.Writer
+       *httptest.ResponseRecorder
+}
+
+func (r *customWriterRecorder) Write(p []byte) (n int, err error) {
+       return r.w.Write(p)
+}
+
+type limitWriter struct {
+       w io.Writer
+       n int
+}
+
+func (w *limitWriter) Write(p []byte) (n int, err error) {
+       if len(p) > w.n {
+               p = p[:w.n]
+       }
+       if len(p) > 0 {
+               n, err = w.w.Write(p)
+               w.n -= n
+       }
+       if w.n == 0 {
+               err = errors.New("past write limit")
+       }
+       return
+}
+
+// If there's an error copying the child's output to the parent, test
+// that we kill the child.
+func TestKillChildAfterCopyError(t *testing.T) {
+       if runtime.GOOS == "nacl" {
+               t.Skip("skipping on nacl")
+       }
+
+       defer func() { testHookStartProcess = nil }()
+       proc := make(chan *os.Process, 1)
+       testHookStartProcess = func(p *os.Process) {
+               proc <- p
+       }
+
+       h := &Handler{
+               Path: os.Args[0],
+               Root: "/test.go",
+               Args: []string{"-test.run=TestBeChildCGIProcess"},
+       }
+       req, _ := http.NewRequest("GET", "http://example.com/test.cgi?write-forever=1", nil)
+       rec := httptest.NewRecorder()
+       var out bytes.Buffer
+       const writeLen = 50 << 10
+       rw := &customWriterRecorder{&limitWriter{&out, writeLen}, rec}
+
+       donec := make(chan bool, 1)
+       go func() {
+               h.ServeHTTP(rw, req)
+               donec <- true
+       }()
+
+       select {
+       case <-donec:
+               if out.Len() != writeLen || out.Bytes()[0] != 'a' {
+                       t.Errorf("unexpected output: %q", out.Bytes())
+               }
+       case <-time.After(5 * time.Second):
+               t.Errorf("timeout. ServeHTTP hung and didn't kill the child process?")
+               select {
+               case p := <-proc:
+                       p.Kill()
+                       t.Logf("killed process")
+               default:
+                       t.Logf("didn't kill process")
+               }
+       }
+}
+
+// Test that a child handler writing only headers works.
+// golang.org/issue/7196
 func TestChildOnlyHeaders(t *testing.T) {
+       if runtime.GOOS == "nacl" {
+               t.Skip("skipping on nacl")
+       }
+
        h := &Handler{
                Path: os.Args[0],
                Root: "/test.go",
@@ -67,18 +157,63 @@ func TestChildOnlyHeaders(t *testing.T) {
        }
 }
 
+// golang.org/issue/7198
+func Test500WithNoHeaders(t *testing.T)     { want500Test(t, "/immediate-disconnect") }
+func Test500WithNoContentType(t *testing.T) { want500Test(t, "/no-content-type") }
+func Test500WithEmptyHeaders(t *testing.T)  { want500Test(t, "/empty-headers") }
+
+func want500Test(t *testing.T, path string) {
+       h := &Handler{
+               Path: os.Args[0],
+               Root: "/test.go",
+               Args: []string{"-test.run=TestBeChildCGIProcess"},
+       }
+       expectedMap := map[string]string{
+               "_body": "",
+       }
+       replay := runCgiTest(t, h, "GET "+path+" HTTP/1.0\nHost: example.com\n\n", expectedMap)
+       if replay.Code != 500 {
+               t.Errorf("Got code %d; want 500", replay.Code)
+       }
+}
+
+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
+}
+
 // Note: not actually a test.
 func TestBeChildCGIProcess(t *testing.T) {
        if os.Getenv("REQUEST_METHOD") == "" {
                // Not in a CGI environment; skipping test.
                return
        }
+       switch os.Getenv("REQUEST_URI") {
+       case "/immediate-disconnect":
+               os.Exit(0)
+       case "/no-content-type":
+               fmt.Printf("Content-Length: 6\n\nHello\n")
+               os.Exit(0)
+       case "/empty-headers":
+               fmt.Printf("\nHello")
+               os.Exit(0)
+       }
        Serve(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
                rw.Header().Set("X-Test-Header", "X-Test-Value")
                req.ParseForm()
                if req.FormValue("no-body") == "1" {
                        return
                }
+               if req.FormValue("write-forever") == "1" {
+                       io.Copy(rw, neverEnding('a'))
+                       for {
+                               time.Sleep(5 * time.Second) // hang forever, until killed
+                       }
+               }
                fmt.Fprintf(rw, "test=Hello CGI-in-CGI\n")
                for k, vv := range req.Form {
                        for _, v := range vv {
index 22f2e865cf7d69ef3fc0e10f9bd9e621d517c692..a5a3abe6138a596fbae22bf7e64b50651b638add 100644 (file)
@@ -14,9 +14,12 @@ import (
        "errors"
        "fmt"
        "io"
+       "io/ioutil"
        "log"
        "net/url"
        "strings"
+       "sync"
+       "time"
 )
 
 // A Client is an HTTP client. Its zero value (DefaultClient) is a
@@ -52,6 +55,20 @@ type Client struct {
        // If Jar is nil, cookies are not sent in requests and ignored
        // in responses.
        Jar CookieJar
+
+       // Timeout specifies a time limit for requests made by this
+       // Client. The timeout includes connection time, any
+       // redirects, and reading the response body. The timer remains
+       // running after Get, Head, Post, or Do return and will
+       // interrupt reading of the Response.Body.
+       //
+       // A Timeout of zero means no timeout.
+       //
+       // The Client's Transport must support the CancelRequest
+       // method or Client will return errors when attempting to make
+       // a request with Get, Head, Post, or Do. Client's default
+       // Transport (DefaultTransport) supports CancelRequest.
+       Timeout time.Duration
 }
 
 // DefaultClient is the default Client and is used by Get, Head, and Post.
@@ -74,8 +91,9 @@ type RoundTripper interface {
        // authentication, or cookies.
        //
        // RoundTrip should not modify the request, except for
-       // consuming and closing the Body. The request's URL and
-       // Header fields are guaranteed to be initialized.
+       // consuming and closing the Body, including on errors. The
+       // request's URL and Header fields are guaranteed to be
+       // initialized.
        RoundTrip(*Request) (*Response, error)
 }
 
@@ -97,7 +115,7 @@ func (c *Client) send(req *Request) (*Response, error) {
                        req.AddCookie(cookie)
                }
        }
-       resp, err := send(req, c.Transport)
+       resp, err := send(req, c.transport())
        if err != nil {
                return nil, err
        }
@@ -123,6 +141,9 @@ func (c *Client) send(req *Request) (*Response, error) {
 // (typically Transport) may not be able to re-use a persistent TCP
 // connection to the server for a subsequent "keep-alive" request.
 //
+// The request Body, if non-nil, will be closed by the underlying
+// Transport, even on errors.
+//
 // Generally Get, Post, or PostForm will be used instead of Do.
 func (c *Client) Do(req *Request) (resp *Response, err error) {
        if req.Method == "GET" || req.Method == "HEAD" {
@@ -134,22 +155,28 @@ func (c *Client) Do(req *Request) (resp *Response, err error) {
        return c.send(req)
 }
 
+func (c *Client) transport() RoundTripper {
+       if c.Transport != nil {
+               return c.Transport
+       }
+       return DefaultTransport
+}
+
 // send issues an HTTP request.
 // Caller should close resp.Body when done reading from it.
 func send(req *Request, t RoundTripper) (resp *Response, err error) {
        if t == nil {
-               t = DefaultTransport
-               if t == nil {
-                       err = errors.New("http: no Client.Transport or DefaultTransport")
-                       return
-               }
+               req.closeBody()
+               return nil, errors.New("http: no Client.Transport or DefaultTransport")
        }
 
        if req.URL == nil {
+               req.closeBody()
                return nil, errors.New("http: nil Request.URL")
        }
 
        if req.RequestURI != "" {
+               req.closeBody()
                return nil, errors.New("http: Request.RequestURI can't be set in client requests.")
        }
 
@@ -257,21 +284,40 @@ func (c *Client) doFollowingRedirects(ireq *Request, shouldRedirect func(int) bo
        var via []*Request
 
        if ireq.URL == nil {
+               ireq.closeBody()
                return nil, errors.New("http: nil Request.URL")
        }
 
+       var reqmu sync.Mutex // guards req
        req := ireq
+
+       var timer *time.Timer
+       if c.Timeout > 0 {
+               type canceler interface {
+                       CancelRequest(*Request)
+               }
+               tr, ok := c.transport().(canceler)
+               if !ok {
+                       return nil, fmt.Errorf("net/http: Client Transport of type %T doesn't support CancelRequest; Timeout not supported", c.transport())
+               }
+               timer = time.AfterFunc(c.Timeout, func() {
+                       reqmu.Lock()
+                       defer reqmu.Unlock()
+                       tr.CancelRequest(req)
+               })
+       }
+
        urlStr := "" // next relative or absolute URL to fetch (after first request)
        redirectFailed := false
        for redirect := 0; ; redirect++ {
                if redirect != 0 {
-                       req = new(Request)
-                       req.Method = ireq.Method
+                       nreq := new(Request)
+                       nreq.Method = ireq.Method
                        if ireq.Method == "POST" || ireq.Method == "PUT" {
-                               req.Method = "GET"
+                               nreq.Method = "GET"
                        }
-                       req.Header = make(Header)
-                       req.URL, err = base.Parse(urlStr)
+                       nreq.Header = make(Header)
+                       nreq.URL, err = base.Parse(urlStr)
                        if err != nil {
                                break
                        }
@@ -279,15 +325,18 @@ func (c *Client) doFollowingRedirects(ireq *Request, shouldRedirect func(int) bo
                                // Add the Referer header.
                                lastReq := via[len(via)-1]
                                if lastReq.URL.Scheme != "https" {
-                                       req.Header.Set("Referer", lastReq.URL.String())
+                                       nreq.Header.Set("Referer", lastReq.URL.String())
                                }
 
-                               err = redirectChecker(req, via)
+                               err = redirectChecker(nreq, via)
                                if err != nil {
                                        redirectFailed = true
                                        break
                                }
                        }
+                       reqmu.Lock()
+                       req = nreq
+                       reqmu.Unlock()
                }
 
                urlStr = req.URL.String()
@@ -296,6 +345,12 @@ func (c *Client) doFollowingRedirects(ireq *Request, shouldRedirect func(int) bo
                }
 
                if shouldRedirect(resp.StatusCode) {
+                       // Read the body if small so underlying TCP connection will be re-used.
+                       // No need to check for errors: if it fails, Transport won't reuse it anyway.
+                       const maxBodySlurpSize = 2 << 10
+                       if resp.ContentLength == -1 || resp.ContentLength <= maxBodySlurpSize {
+                               io.CopyN(ioutil.Discard, resp.Body, maxBodySlurpSize)
+                       }
                        resp.Body.Close()
                        if urlStr = resp.Header.Get("Location"); urlStr == "" {
                                err = errors.New(fmt.Sprintf("%d response missing Location header", resp.StatusCode))
@@ -305,7 +360,10 @@ func (c *Client) doFollowingRedirects(ireq *Request, shouldRedirect func(int) bo
                        via = append(via, req)
                        continue
                }
-               return
+               if timer != nil {
+                       resp.Body = &cancelTimerBody{timer, resp.Body}
+               }
+               return resp, nil
        }
 
        method := ireq.Method
@@ -349,7 +407,7 @@ func Post(url string, bodyType string, body io.Reader) (resp *Response, err erro
 // Caller should close resp.Body when done reading from it.
 //
 // If the provided body is also an io.Closer, it is closed after the
-// body is successfully written to the server.
+// request.
 func (c *Client) Post(url string, bodyType string, body io.Reader) (resp *Response, err error) {
        req, err := NewRequest("POST", url, body)
        if err != nil {
@@ -408,3 +466,22 @@ func (c *Client) Head(url string) (resp *Response, err error) {
        }
        return c.doFollowingRedirects(req, shouldRedirectGet)
 }
+
+type cancelTimerBody struct {
+       t  *time.Timer
+       rc io.ReadCloser
+}
+
+func (b *cancelTimerBody) Read(p []byte) (n int, err error) {
+       n, err = b.rc.Read(p)
+       if err == io.EOF {
+               b.t.Stop()
+       }
+       return
+}
+
+func (b *cancelTimerBody) Close() error {
+       err := b.rc.Close()
+       b.t.Stop()
+       return err
+}
index e5ad39c77418a568adff50ac258a236d31147605..6392c1baf39cdf83852a51e0aca8feb9cd9a1e23 100644 (file)
@@ -15,14 +15,18 @@ import (
        "fmt"
        "io"
        "io/ioutil"
+       "log"
        "net"
        . "net/http"
        "net/http/httptest"
        "net/url"
+       "reflect"
+       "sort"
        "strconv"
        "strings"
        "sync"
        "testing"
+       "time"
 )
 
 var robotsTxtHandler = HandlerFunc(func(w ResponseWriter, r *Request) {
@@ -54,6 +58,13 @@ func pedanticReadAll(r io.Reader) (b []byte, err error) {
        }
 }
 
+type chanWriter chan string
+
+func (w chanWriter) Write(p []byte) (n int, err error) {
+       w <- string(p)
+       return len(p), nil
+}
+
 func TestClient(t *testing.T) {
        defer afterTest(t)
        ts := httptest.NewServer(robotsTxtHandler)
@@ -564,6 +575,8 @@ func TestClientInsecureTransport(t *testing.T) {
        ts := httptest.NewTLSServer(HandlerFunc(func(w ResponseWriter, r *Request) {
                w.Write([]byte("Hello"))
        }))
+       errc := make(chanWriter, 10) // but only expecting 1
+       ts.Config.ErrorLog = log.New(errc, "", 0)
        defer ts.Close()
 
        // TODO(bradfitz): add tests for skipping hostname checks too?
@@ -585,6 +598,16 @@ func TestClientInsecureTransport(t *testing.T) {
                        res.Body.Close()
                }
        }
+
+       select {
+       case v := <-errc:
+               if !strings.Contains(v, "TLS handshake error") {
+                       t.Errorf("expected an error log message containing 'TLS handshake error'; got %q", v)
+               }
+       case <-time.After(5 * time.Second):
+               t.Errorf("timeout waiting for logged error")
+       }
+
 }
 
 func TestClientErrorWithRequestURI(t *testing.T) {
@@ -635,6 +658,8 @@ func TestClientWithIncorrectTLSServerName(t *testing.T) {
        defer afterTest(t)
        ts := httptest.NewTLSServer(HandlerFunc(func(w ResponseWriter, r *Request) {}))
        defer ts.Close()
+       errc := make(chanWriter, 10) // but only expecting 1
+       ts.Config.ErrorLog = log.New(errc, "", 0)
 
        trans := newTLSTransport(t, ts)
        trans.TLSClientConfig.ServerName = "badserver"
@@ -646,6 +671,14 @@ func TestClientWithIncorrectTLSServerName(t *testing.T) {
        if !strings.Contains(err.Error(), "127.0.0.1") || !strings.Contains(err.Error(), "badserver") {
                t.Errorf("wanted error mentioning 127.0.0.1 and badserver; got error: %v", err)
        }
+       select {
+       case v := <-errc:
+               if !strings.Contains(v, "TLS handshake error") {
+                       t.Errorf("expected an error log message containing 'TLS handshake error'; got %q", v)
+               }
+       case <-time.After(5 * time.Second):
+               t.Errorf("timeout waiting for logged error")
+       }
 }
 
 // Test for golang.org/issue/5829; the Transport should respect TLSClientConfig.ServerName
@@ -678,6 +711,33 @@ func TestTransportUsesTLSConfigServerName(t *testing.T) {
        res.Body.Close()
 }
 
+func TestResponseSetsTLSConnectionState(t *testing.T) {
+       defer afterTest(t)
+       ts := httptest.NewTLSServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+               w.Write([]byte("Hello"))
+       }))
+       defer ts.Close()
+
+       tr := newTLSTransport(t, ts)
+       tr.TLSClientConfig.CipherSuites = []uint16{tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA}
+       tr.Dial = func(netw, addr string) (net.Conn, error) {
+               return net.Dial(netw, ts.Listener.Addr().String())
+       }
+       defer tr.CloseIdleConnections()
+       c := &Client{Transport: tr}
+       res, err := c.Get("https://example.com/")
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer res.Body.Close()
+       if res.TLS == nil {
+               t.Fatal("Response didn't set TLS Connection State.")
+       }
+       if got, want := res.TLS.CipherSuite, tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA; got != want {
+               t.Errorf("TLS Cipher Suite = %d; want %d", got, want)
+       }
+}
+
 // Verify Response.ContentLength is populated. http://golang.org/issue/4126
 func TestClientHeadContentLength(t *testing.T) {
        defer afterTest(t)
@@ -781,3 +841,198 @@ func TestBasicAuth(t *testing.T) {
                t.Errorf("Invalid auth %q", auth)
        }
 }
+
+func TestClientTimeout(t *testing.T) {
+       if testing.Short() {
+               t.Skip("skipping in short mode")
+       }
+       defer afterTest(t)
+       sawRoot := make(chan bool, 1)
+       sawSlow := make(chan bool, 1)
+       ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+               if r.URL.Path == "/" {
+                       sawRoot <- true
+                       Redirect(w, r, "/slow", StatusFound)
+                       return
+               }
+               if r.URL.Path == "/slow" {
+                       w.Write([]byte("Hello"))
+                       w.(Flusher).Flush()
+                       sawSlow <- true
+                       time.Sleep(2 * time.Second)
+                       return
+               }
+       }))
+       defer ts.Close()
+       const timeout = 500 * time.Millisecond
+       c := &Client{
+               Timeout: timeout,
+       }
+
+       res, err := c.Get(ts.URL)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       select {
+       case <-sawRoot:
+               // good.
+       default:
+               t.Fatal("handler never got / request")
+       }
+
+       select {
+       case <-sawSlow:
+               // good.
+       default:
+               t.Fatal("handler never got /slow request")
+       }
+
+       errc := make(chan error, 1)
+       go func() {
+               _, err := ioutil.ReadAll(res.Body)
+               errc <- err
+               res.Body.Close()
+       }()
+
+       const failTime = timeout * 2
+       select {
+       case err := <-errc:
+               if err == nil {
+                       t.Error("expected error from ReadAll")
+               }
+               // Expected error.
+       case <-time.After(failTime):
+               t.Errorf("timeout after %v waiting for timeout of %v", failTime, timeout)
+       }
+}
+
+func TestClientRedirectEatsBody(t *testing.T) {
+       defer afterTest(t)
+       saw := make(chan string, 2)
+       ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+               saw <- r.RemoteAddr
+               if r.URL.Path == "/" {
+                       Redirect(w, r, "/foo", StatusFound) // which includes a body
+               }
+       }))
+       defer ts.Close()
+
+       res, err := Get(ts.URL)
+       if err != nil {
+               t.Fatal(err)
+       }
+       _, err = ioutil.ReadAll(res.Body)
+       if err != nil {
+               t.Fatal(err)
+       }
+       res.Body.Close()
+
+       var first string
+       select {
+       case first = <-saw:
+       default:
+               t.Fatal("server didn't see a request")
+       }
+
+       var second string
+       select {
+       case second = <-saw:
+       default:
+               t.Fatal("server didn't see a second request")
+       }
+
+       if first != second {
+               t.Fatal("server saw different client ports before & after the redirect")
+       }
+}
+
+// eofReaderFunc is an io.Reader that runs itself, and then returns io.EOF.
+type eofReaderFunc func()
+
+func (f eofReaderFunc) Read(p []byte) (n int, err error) {
+       f()
+       return 0, io.EOF
+}
+
+func TestClientTrailers(t *testing.T) {
+       defer afterTest(t)
+       ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+               w.Header().Set("Connection", "close")
+               w.Header().Set("Trailer", "Server-Trailer-A, Server-Trailer-B")
+               w.Header().Add("Trailer", "Server-Trailer-C")
+
+               var decl []string
+               for k := range r.Trailer {
+                       decl = append(decl, k)
+               }
+               sort.Strings(decl)
+
+               slurp, err := ioutil.ReadAll(r.Body)
+               if err != nil {
+                       t.Errorf("Server reading request body: %v", err)
+               }
+               if string(slurp) != "foo" {
+                       t.Errorf("Server read request body %q; want foo", slurp)
+               }
+               if r.Trailer == nil {
+                       io.WriteString(w, "nil Trailer")
+               } else {
+                       fmt.Fprintf(w, "decl: %v, vals: %s, %s",
+                               decl,
+                               r.Trailer.Get("Client-Trailer-A"),
+                               r.Trailer.Get("Client-Trailer-B"))
+               }
+
+               // TODO: golang.org/issue/7759: there's no way yet for
+               // the server to set trailers without hijacking, so do
+               // that for now, just to test the client.  Later, in
+               // Go 1.4, it should be implicit that any mutations
+               // to w.Header() after the initial write are the
+               // trailers to be sent, if and only if they were
+               // previously declared with w.Header().Set("Trailer",
+               // ..keys..)
+               w.(Flusher).Flush()
+               conn, buf, _ := w.(Hijacker).Hijack()
+               t := Header{}
+               t.Set("Server-Trailer-A", "valuea")
+               t.Set("Server-Trailer-C", "valuec") // skipping B
+               buf.WriteString("0\r\n")            // eof
+               t.Write(buf)
+               buf.WriteString("\r\n") // end of trailers
+               buf.Flush()
+               conn.Close()
+       }))
+       defer ts.Close()
+
+       var req *Request
+       req, _ = NewRequest("POST", ts.URL, io.MultiReader(
+               eofReaderFunc(func() {
+                       req.Trailer["Client-Trailer-A"] = []string{"valuea"}
+               }),
+               strings.NewReader("foo"),
+               eofReaderFunc(func() {
+                       req.Trailer["Client-Trailer-B"] = []string{"valueb"}
+               }),
+       ))
+       req.Trailer = Header{
+               "Client-Trailer-A": nil, //  to be set later
+               "Client-Trailer-B": nil, //  to be set later
+       }
+       req.ContentLength = -1
+       res, err := DefaultClient.Do(req)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if err := wantBody(res, err, "decl: [Client-Trailer-A Client-Trailer-B], vals: valuea, valueb"); err != nil {
+               t.Error(err)
+       }
+       want := Header{
+               "Server-Trailer-A": []string{"valuea"},
+               "Server-Trailer-B": nil,
+               "Server-Trailer-C": []string{"valuec"},
+       }
+       if !reflect.DeepEqual(res.Trailer, want) {
+               t.Errorf("Response trailers = %#v; want %#v", res.Trailer, want)
+       }
+}
index a1759214f3827c394441f43e18a7af433f22a390..dc60ba87f5f9bf8e19c1ccd769fb0540ace0ceb9 100644 (file)
@@ -76,11 +76,7 @@ func readSetCookies(h Header) []*Cookie {
                                attr, val = attr[:j], attr[j+1:]
                        }
                        lowerAttr := strings.ToLower(attr)
-                       parseCookieValueFn := parseCookieValue
-                       if lowerAttr == "expires" {
-                               parseCookieValueFn = parseCookieExpiresValue
-                       }
-                       val, success = parseCookieValueFn(val)
+                       val, success = parseCookieValue(val)
                        if !success {
                                c.Unparsed = append(c.Unparsed, parts[i])
                                continue
@@ -298,12 +294,23 @@ func sanitizeCookieName(n string) string {
 //           ; US-ASCII characters excluding CTLs,
 //           ; whitespace DQUOTE, comma, semicolon,
 //           ; and backslash
+// We loosen this as spaces and commas are common in cookie values
+// but we produce a quoted cookie-value in when value starts or ends
+// with a comma or space.
+// See http://golang.org/issue/7243 for the discussion.
 func sanitizeCookieValue(v string) string {
-       return sanitizeOrWarn("Cookie.Value", validCookieValueByte, v)
+       v = sanitizeOrWarn("Cookie.Value", validCookieValueByte, v)
+       if len(v) == 0 {
+               return v
+       }
+       if v[0] == ' ' || v[0] == ',' || v[len(v)-1] == ' ' || v[len(v)-1] == ',' {
+               return `"` + v + `"`
+       }
+       return v
 }
 
 func validCookieValueByte(b byte) bool {
-       return 0x20 < b && b < 0x7f && b != '"' && b != ',' && b != ';' && b != '\\'
+       return 0x20 <= b && b < 0x7f && b != '"' && b != ';' && b != '\\'
 }
 
 // path-av           = "Path=" path-value
@@ -338,38 +345,13 @@ func sanitizeOrWarn(fieldName string, valid func(byte) bool, v string) string {
        return string(buf)
 }
 
-func unquoteCookieValue(v string) string {
-       if len(v) > 1 && v[0] == '"' && v[len(v)-1] == '"' {
-               return v[1 : len(v)-1]
-       }
-       return v
-}
-
-func isCookieByte(c byte) bool {
-       switch {
-       case c == 0x21, 0x23 <= c && c <= 0x2b, 0x2d <= c && c <= 0x3a,
-               0x3c <= c && c <= 0x5b, 0x5d <= c && c <= 0x7e:
-               return true
-       }
-       return false
-}
-
-func isCookieExpiresByte(c byte) (ok bool) {
-       return isCookieByte(c) || c == ',' || c == ' '
-}
-
 func parseCookieValue(raw string) (string, bool) {
-       return parseCookieValueUsing(raw, isCookieByte)
-}
-
-func parseCookieExpiresValue(raw string) (string, bool) {
-       return parseCookieValueUsing(raw, isCookieExpiresByte)
-}
-
-func parseCookieValueUsing(raw string, validByte func(byte) bool) (string, bool) {
-       raw = unquoteCookieValue(raw)
+       // Strip the quotes, if present.
+       if len(raw) > 1 && raw[0] == '"' && raw[len(raw)-1] == '"' {
+               raw = raw[1 : len(raw)-1]
+       }
        for i := 0; i < len(raw); i++ {
-               if !validByte(raw[i]) {
+               if !validCookieValueByte(raw[i]) {
                        return "", false
                }
        }
index 1aa9d49d96ec40d36128a0e163db7e6d751b7421..f78f37299f4885ed0407a74fa131c32b3cacc646 100644 (file)
@@ -52,6 +52,44 @@ var writeSetCookiesTests = []struct {
                &Cookie{Name: "cookie-8", Value: "eight", Domain: "::1"},
                "cookie-8=eight",
        },
+       // The "special" cookies have values containing commas or spaces which
+       // are disallowed by RFC 6265 but are common in the wild.
+       {
+               &Cookie{Name: "special-1", Value: "a z"},
+               `special-1=a z`,
+       },
+       {
+               &Cookie{Name: "special-2", Value: " z"},
+               `special-2=" z"`,
+       },
+       {
+               &Cookie{Name: "special-3", Value: "a "},
+               `special-3="a "`,
+       },
+       {
+               &Cookie{Name: "special-4", Value: " "},
+               `special-4=" "`,
+       },
+       {
+               &Cookie{Name: "special-5", Value: "a,z"},
+               `special-5=a,z`,
+       },
+       {
+               &Cookie{Name: "special-6", Value: ",z"},
+               `special-6=",z"`,
+       },
+       {
+               &Cookie{Name: "special-7", Value: "a,"},
+               `special-7="a,"`,
+       },
+       {
+               &Cookie{Name: "special-8", Value: ","},
+               `special-8=","`,
+       },
+       {
+               &Cookie{Name: "empty-value", Value: ""},
+               `empty-value=`,
+       },
 }
 
 func TestWriteSetCookies(t *testing.T) {
@@ -178,6 +216,40 @@ var readSetCookiesTests = []struct {
                        Raw:      "ASP.NET_SessionId=foo; path=/; HttpOnly",
                }},
        },
+       // Make sure we can properly read back the Set-Cookie headers we create
+       // for values containing spaces or commas:
+       {
+               Header{"Set-Cookie": {`special-1=a z`}},
+               []*Cookie{{Name: "special-1", Value: "a z", Raw: `special-1=a z`}},
+       },
+       {
+               Header{"Set-Cookie": {`special-2=" z"`}},
+               []*Cookie{{Name: "special-2", Value: " z", Raw: `special-2=" z"`}},
+       },
+       {
+               Header{"Set-Cookie": {`special-3="a "`}},
+               []*Cookie{{Name: "special-3", Value: "a ", Raw: `special-3="a "`}},
+       },
+       {
+               Header{"Set-Cookie": {`special-4=" "`}},
+               []*Cookie{{Name: "special-4", Value: " ", Raw: `special-4=" "`}},
+       },
+       {
+               Header{"Set-Cookie": {`special-5=a,z`}},
+               []*Cookie{{Name: "special-5", Value: "a,z", Raw: `special-5=a,z`}},
+       },
+       {
+               Header{"Set-Cookie": {`special-6=",z"`}},
+               []*Cookie{{Name: "special-6", Value: ",z", Raw: `special-6=",z"`}},
+       },
+       {
+               Header{"Set-Cookie": {`special-7=a,`}},
+               []*Cookie{{Name: "special-7", Value: "a,", Raw: `special-7=a,`}},
+       },
+       {
+               Header{"Set-Cookie": {`special-8=","`}},
+               []*Cookie{{Name: "special-8", Value: ",", Raw: `special-8=","`}},
+       },
 
        // TODO(bradfitz): users have reported seeing this in the
        // wild, but do browsers handle it? RFC 6265 just says "don't
@@ -264,9 +336,14 @@ func TestCookieSanitizeValue(t *testing.T) {
                in, want string
        }{
                {"foo", "foo"},
-               {"foo bar", "foobar"},
+               {"foo;bar", "foobar"},
+               {"foo\\bar", "foobar"},
+               {"foo\"bar", "foobar"},
                {"\x00\x7e\x7f\x80", "\x7e"},
                {`"withquotes"`, "withquotes"},
+               {"a z", "a z"},
+               {" z", `" z"`},
+               {"a ", `"a "`},
        }
        for _, tt := range tests {
                if got := sanitizeCookieValue(tt.in); got != tt.want {
index 8074df5bbde17f3ffad29dd1f1fe51be3c772d18..960563b240945d9547cb4d4c6e87d9a09708b6ff 100644 (file)
@@ -21,7 +21,7 @@ var ExportAppendTime = appendTime
 func (t *Transport) NumPendingRequestsForTesting() int {
        t.reqMu.Lock()
        defer t.reqMu.Unlock()
-       return len(t.reqConn)
+       return len(t.reqCanceler)
 }
 
 func (t *Transport) IdleConnKeysForTesting() (keys []string) {
index 60b794e077506e73c89f671c9a41697ffafa9dbc..a3beaa33a8604cb35a7258078ef546021438cec6 100644 (file)
@@ -16,6 +16,7 @@ import (
        "net/http/cgi"
        "os"
        "strings"
+       "sync"
        "time"
 )
 
@@ -126,8 +127,10 @@ func (r *response) Close() error {
 }
 
 type child struct {
-       conn     *conn
-       handler  http.Handler
+       conn    *conn
+       handler http.Handler
+
+       mu       sync.Mutex          // protects requests:
        requests map[uint16]*request // keyed by request ID
 }
 
@@ -157,7 +160,9 @@ var errCloseConn = errors.New("fcgi: connection should be closed")
 var emptyBody = ioutil.NopCloser(strings.NewReader(""))
 
 func (c *child) handleRecord(rec *record) error {
+       c.mu.Lock()
        req, ok := c.requests[rec.h.Id]
+       c.mu.Unlock()
        if !ok && rec.h.Type != typeBeginRequest && rec.h.Type != typeGetValues {
                // The spec says to ignore unknown request IDs.
                return nil
@@ -179,7 +184,10 @@ func (c *child) handleRecord(rec *record) error {
                        c.conn.writeEndRequest(rec.h.Id, 0, statusUnknownRole)
                        return nil
                }
-               c.requests[rec.h.Id] = newRequest(rec.h.Id, br.flags)
+               req = newRequest(rec.h.Id, br.flags)
+               c.mu.Lock()
+               c.requests[rec.h.Id] = req
+               c.mu.Unlock()
                return nil
        case typeParams:
                // NOTE(eds): Technically a key-value pair can straddle the boundary
@@ -220,7 +228,9 @@ func (c *child) handleRecord(rec *record) error {
                return nil
        case typeAbortRequest:
                println("abort")
+               c.mu.Lock()
                delete(c.requests, rec.h.Id)
+               c.mu.Unlock()
                c.conn.writeEndRequest(rec.h.Id, 0, statusRequestComplete)
                if !req.keepConn {
                        // connection will close upon return
@@ -247,6 +257,9 @@ func (c *child) serveRequest(req *request, body io.ReadCloser) {
                c.handler.ServeHTTP(r, httpReq)
        }
        r.Close()
+       c.mu.Lock()
+       delete(c.requests, req.reqId)
+       c.mu.Unlock()
        c.conn.writeEndRequest(req.reqId, 0, statusRequestComplete)
 
        // Consume the entire body, so the host isn't still writing to
index 9df5cc481897d98ac628b23252bcee0c0a910510..8576cf844a3ca412e4e1a6e296ef90bdea95699a 100644 (file)
@@ -527,7 +527,7 @@ func (w *countingWriter) Write(p []byte) (n int, err error) {
        return len(p), nil
 }
 
-// rangesMIMESize returns the nunber of bytes it takes to encode the
+// rangesMIMESize returns the number of bytes it takes to encode the
 // provided ranges as a multipart response.
 func rangesMIMESize(ranges []httpRange, contentType string, contentSize int64) (encSize int64) {
        var w countingWriter
index de62bef55254c554b1cb417c5b228d01ff7d9606..153b94370f856fd8ce23b8c626468b2b89590ead 100644 (file)
@@ -13,6 +13,8 @@ import (
        "time"
 )
 
+var raceEnabled = false // set by race.go
+
 // A Header represents the key-value pairs in an HTTP header.
 type Header map[string][]string
 
index 2c896c5ad2395557dbf0c725d9c67785fb5f82ca..299576ba8cf66269c1f55cc11aeef0339164f075 100644 (file)
@@ -192,9 +192,12 @@ func BenchmarkHeaderWriteSubset(b *testing.B) {
        }
 }
 
-func TestHeaderWriteSubsetMallocs(t *testing.T) {
+func TestHeaderWriteSubsetAllocs(t *testing.T) {
        if testing.Short() {
-               t.Skip("skipping malloc count in short mode")
+               t.Skip("skipping alloc test in short mode")
+       }
+       if raceEnabled {
+               t.Skip("skipping test under race detector")
        }
        t.Skip("Skipping alloc count test on gccgo")
        if runtime.GOMAXPROCS(0) > 1 {
@@ -205,6 +208,6 @@ func TestHeaderWriteSubsetMallocs(t *testing.T) {
                testHeader.WriteSubset(&buf, nil)
        })
        if n > 0 {
-               t.Errorf("mallocs = %g; want 0", n)
+               t.Errorf("allocs = %g; want 0", n)
        }
 }
index 500a9f0b80000b93a1c85f9f6fd1689e487c7239..501cc8a999562881be5fa81bd5e24857e418860f 100644 (file)
@@ -8,6 +8,7 @@ import (
        "io/ioutil"
        "net/http"
        "testing"
+       "time"
 )
 
 func TestServer(t *testing.T) {
@@ -27,3 +28,25 @@ func TestServer(t *testing.T) {
                t.Errorf("got %q, want hello", string(got))
        }
 }
+
+func TestIssue7264(t *testing.T) {
+       for i := 0; i < 1000; i++ {
+               func() {
+                       inHandler := make(chan bool, 1)
+                       ts := NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+                               inHandler <- true
+                       }))
+                       defer ts.Close()
+                       tr := &http.Transport{
+                               ResponseHeaderTimeout: time.Nanosecond,
+                       }
+                       defer tr.CloseIdleConnections()
+                       c := &http.Client{Transport: tr}
+                       res, err := c.Get(ts.URL)
+                       <-inHandler
+                       if err == nil {
+                               res.Body.Close()
+                       }
+               }()
+       }
+}
index ab1eab21bc63f44bcf50a30c74240aa35a4d9c58..2a7a413d01a2d02ad6176e1f53e760799eed1290 100644 (file)
@@ -7,6 +7,7 @@ package httputil
 import (
        "bufio"
        "bytes"
+       "errors"
        "fmt"
        "io"
        "io/ioutil"
@@ -106,6 +107,7 @@ func DumpRequestOut(req *http.Request, body bool) ([]byte, error) {
                        return &dumpConn{io.MultiWriter(&buf, pw), dr}, nil
                },
        }
+       defer t.CloseIdleConnections()
 
        _, err := t.RoundTrip(reqSend)
 
@@ -230,14 +232,31 @@ func DumpRequest(req *http.Request, body bool) (dump []byte, err error) {
        return
 }
 
+// errNoBody is a sentinel error value used by failureToReadBody so we can detect
+// that the lack of body was intentional.
+var errNoBody = errors.New("sentinel error value")
+
+// failureToReadBody is a io.ReadCloser that just returns errNoBody on
+// Read.  It's swapped in when we don't actually want to consume the
+// body, but need a non-nil one, and want to distinguish the error
+// from reading the dummy body.
+type failureToReadBody struct{}
+
+func (failureToReadBody) Read([]byte) (int, error) { return 0, errNoBody }
+func (failureToReadBody) Close() error             { return nil }
+
+var emptyBody = ioutil.NopCloser(strings.NewReader(""))
+
 // DumpResponse is like DumpRequest but dumps a response.
 func DumpResponse(resp *http.Response, body bool) (dump []byte, err error) {
        var b bytes.Buffer
        save := resp.Body
        savecl := resp.ContentLength
-       if !body || resp.Body == nil {
-               resp.Body = nil
-               resp.ContentLength = 0
+
+       if !body {
+               resp.Body = failureToReadBody{}
+       } else if resp.Body == nil {
+               resp.Body = emptyBody
        } else {
                save, resp.Body, err = drainBody(resp.Body)
                if err != nil {
@@ -245,11 +264,13 @@ func DumpResponse(resp *http.Response, body bool) (dump []byte, err error) {
                }
        }
        err = resp.Write(&b)
+       if err == errNoBody {
+               err = nil
+       }
        resp.Body = save
        resp.ContentLength = savecl
        if err != nil {
-               return
+               return nil, err
        }
-       dump = b.Bytes()
-       return
+       return b.Bytes(), nil
 }
index a1dbfc39d6be446e802a00cf90ee28c535fd71ba..e1ffb3935ac036a0a422796b9bd91266f91bf901 100644 (file)
@@ -11,6 +11,8 @@ import (
        "io/ioutil"
        "net/http"
        "net/url"
+       "runtime"
+       "strings"
        "testing"
 )
 
@@ -112,6 +114,7 @@ var dumpTests = []dumpTest{
 }
 
 func TestDumpRequest(t *testing.T) {
+       numg0 := runtime.NumGoroutine()
        for i, tt := range dumpTests {
                setBody := func() {
                        if tt.Body == nil {
@@ -155,6 +158,9 @@ func TestDumpRequest(t *testing.T) {
                        }
                }
        }
+       if dg := runtime.NumGoroutine() - numg0; dg > 4 {
+               t.Errorf("Unexpectedly large number of new goroutines: %d new", dg)
+       }
 }
 
 func chunk(s string) string {
@@ -176,3 +182,82 @@ func mustNewRequest(method, url string, body io.Reader) *http.Request {
        }
        return req
 }
+
+var dumpResTests = []struct {
+       res  *http.Response
+       body bool
+       want string
+}{
+       {
+               res: &http.Response{
+                       Status:        "200 OK",
+                       StatusCode:    200,
+                       Proto:         "HTTP/1.1",
+                       ProtoMajor:    1,
+                       ProtoMinor:    1,
+                       ContentLength: 50,
+                       Header: http.Header{
+                               "Foo": []string{"Bar"},
+                       },
+                       Body: ioutil.NopCloser(strings.NewReader("foo")), // shouldn't be used
+               },
+               body: false, // to verify we see 50, not empty or 3.
+               want: `HTTP/1.1 200 OK
+Content-Length: 50
+Foo: Bar`,
+       },
+
+       {
+               res: &http.Response{
+                       Status:        "200 OK",
+                       StatusCode:    200,
+                       Proto:         "HTTP/1.1",
+                       ProtoMajor:    1,
+                       ProtoMinor:    1,
+                       ContentLength: 3,
+                       Body:          ioutil.NopCloser(strings.NewReader("foo")),
+               },
+               body: true,
+               want: `HTTP/1.1 200 OK
+Content-Length: 3
+
+foo`,
+       },
+
+       {
+               res: &http.Response{
+                       Status:           "200 OK",
+                       StatusCode:       200,
+                       Proto:            "HTTP/1.1",
+                       ProtoMajor:       1,
+                       ProtoMinor:       1,
+                       ContentLength:    -1,
+                       Body:             ioutil.NopCloser(strings.NewReader("foo")),
+                       TransferEncoding: []string{"chunked"},
+               },
+               body: true,
+               want: `HTTP/1.1 200 OK
+Transfer-Encoding: chunked
+
+3
+foo
+0`,
+       },
+}
+
+func TestDumpResponse(t *testing.T) {
+       for i, tt := range dumpResTests {
+               gotb, err := DumpResponse(tt.res, tt.body)
+               if err != nil {
+                       t.Errorf("%d. DumpResponse = %v", i, err)
+                       continue
+               }
+               got := string(gotb)
+               got = strings.TrimSpace(got)
+               got = strings.Replace(got, "\r", "", -1)
+
+               if got != tt.want {
+                       t.Errorf("%d.\nDumpResponse got:\n%s\n\nWant:\n%s\n", i, got, tt.want)
+               }
+       }
+}
index 86d23e03706e5ce7ba4b1ab4233835b75ebaa1f7..987bcc96ba1d16c8dd26ffd3296f287c1ddb1c74 100644 (file)
@@ -31,8 +31,8 @@ var errClosed = errors.New("i/o operation on closed connection")
 // i.e. requests can be read out of sync (but in the same order) while the
 // respective responses are sent.
 //
-// ServerConn is low-level and should not be needed by most applications.
-// See Server.
+// ServerConn is low-level and old. Applications should instead use Server
+// in the net/http package.
 type ServerConn struct {
        lk              sync.Mutex // read-write protects the following fields
        c               net.Conn
@@ -45,8 +45,11 @@ type ServerConn struct {
        pipe textproto.Pipeline
 }
 
-// NewServerConn returns a new ServerConn reading and writing c.  If r is not
+// NewServerConn returns a new ServerConn reading and writing c. If r is not
 // nil, it is the buffer to use when reading c.
+//
+// ServerConn is low-level and old. Applications should instead use Server
+// in the net/http package.
 func NewServerConn(c net.Conn, r *bufio.Reader) *ServerConn {
        if r == nil {
                r = bufio.NewReader(c)
@@ -221,8 +224,8 @@ func (sc *ServerConn) Write(req *http.Request, resp *http.Response) error {
 // supports hijacking the connection calling Hijack to
 // regain control of the underlying net.Conn and deal with it as desired.
 //
-// ClientConn is low-level and should not be needed by most applications.
-// See Client.
+// ClientConn is low-level and old. Applications should instead use
+// Client or Transport in the net/http package.
 type ClientConn struct {
        lk              sync.Mutex // read-write protects the following fields
        c               net.Conn
@@ -238,6 +241,9 @@ type ClientConn struct {
 
 // NewClientConn returns a new ClientConn reading and writing c.  If r is not
 // nil, it is the buffer to use when reading c.
+//
+// ClientConn is low-level and old. Applications should use Client or
+// Transport in the net/http package.
 func NewClientConn(c net.Conn, r *bufio.Reader) *ClientConn {
        if r == nil {
                r = bufio.NewReader(c)
@@ -252,6 +258,9 @@ func NewClientConn(c net.Conn, r *bufio.Reader) *ClientConn {
 
 // NewProxyClientConn works like NewClientConn but writes Requests
 // using Request's WriteProxy method.
+//
+// New code should not use NewProxyClientConn. See Client or
+// Transport in the net/http package instead.
 func NewProxyClientConn(c net.Conn, r *bufio.Reader) *ClientConn {
        cc := NewClientConn(c, r)
        cc.writeReq = (*http.Request).WriteProxy
index d0726f61f3bf3e64fa21839661a1a23354da724e..b6aed3792b63304555e7bc967d3fa00c86e331b5 100644 (file)
@@ -35,12 +35,8 @@ var UseProxyTests = []struct {
 }
 
 func TestUseProxy(t *testing.T) {
-       oldenv := os.Getenv("NO_PROXY")
-       defer os.Setenv("NO_PROXY", oldenv)
-
-       no_proxy := "foobar.com, .barbaz.net"
-       os.Setenv("NO_PROXY", no_proxy)
-
+       ResetProxyEnv()
+       os.Setenv("NO_PROXY", "foobar.com, .barbaz.net")
        for _, test := range UseProxyTests {
                if useProxy(test.host+":80") != test.match {
                        t.Errorf("useProxy(%v) = %v, want %v", test.host, !test.match, test.match)
@@ -76,3 +72,10 @@ func TestCacheKeys(t *testing.T) {
                }
        }
 }
+
+func ResetProxyEnv() {
+       for _, v := range []string{"HTTP_PROXY", "http_proxy", "NO_PROXY", "no_proxy"} {
+               os.Setenv(v, "")
+       }
+       ResetCachedEnvironment()
+}
diff --git a/libgo/go/net/http/race.go b/libgo/go/net/http/race.go
new file mode 100644 (file)
index 0000000..7665039
--- /dev/null
@@ -0,0 +1,11 @@
+// Copyright 2014 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 race
+
+package http
+
+func init() {
+       raceEnabled = true
+}
index 7a97770314d7f824f87d754d53ac8c044b13fa5c..a67092066ad2045d23ea66a44f81b3e7d3d36292 100644 (file)
@@ -69,18 +69,31 @@ var reqWriteExcludeHeader = map[string]bool{
 
 // A Request represents an HTTP request received by a server
 // or to be sent by a client.
+//
+// The field semantics differ slightly between client and server
+// usage. In addition to the notes on the fields below, see the
+// documentation for Request.Write and RoundTripper.
 type Request struct {
-       Method string // GET, POST, PUT, etc.
+       // Method specifies the HTTP method (GET, POST, PUT, etc.).
+       // For client requests an empty string means GET.
+       Method string
 
-       // URL is created from the URI supplied on the Request-Line
-       // as stored in RequestURI.
+       // URL specifies either the URI being requested (for server
+       // requests) or the URL to access (for client requests).
+       //
+       // For server requests the URL is parsed from the URI
+       // supplied on the Request-Line as stored in RequestURI.  For
+       // most requests, fields other than Path and RawQuery will be
+       // empty. (See RFC 2616, Section 5.1.2)
        //
-       // For most requests, fields other than Path and RawQuery
-       // will be empty. (See RFC 2616, Section 5.1.2)
+       // For client requests, the URL's Host specifies the server to
+       // connect to, while the Request's Host field optionally
+       // specifies the Host header value to send in the HTTP
+       // request.
        URL *url.URL
 
        // The protocol version for incoming requests.
-       // Outgoing requests always use HTTP/1.1.
+       // Client requests always use HTTP/1.1.
        Proto      string // "HTTP/1.0"
        ProtoMajor int    // 1
        ProtoMinor int    // 0
@@ -104,15 +117,20 @@ type Request struct {
        // The request parser implements this by canonicalizing the
        // name, making the first character and any characters
        // following a hyphen uppercase and the rest lowercase.
+       //
+       // For client requests certain headers are automatically
+       // added and may override values in Header.
+       //
+       // See the documentation for the Request.Write method.
        Header Header
 
        // Body is the request's body.
        //
-       // For client requests, a nil body means the request has no
+       // For client requests a nil body means the request has no
        // body, such as a GET request. The HTTP Client's Transport
        // is responsible for calling the Close method.
        //
-       // For server requests, the Request Body is always non-nil
+       // For server requests the Request Body is always non-nil
        // but will return EOF immediately when no body is present.
        // The Server will close the request body. The ServeHTTP
        // Handler does not need to.
@@ -122,7 +140,7 @@ type Request struct {
        // The value -1 indicates that the length is unknown.
        // Values >= 0 indicate that the given number of bytes may
        // be read from Body.
-       // For outgoing requests, a value of 0 means unknown if Body is not nil.
+       // For client requests, a value of 0 means unknown if Body is not nil.
        ContentLength int64
 
        // TransferEncoding lists the transfer encodings from outermost to
@@ -133,13 +151,18 @@ type Request struct {
        TransferEncoding []string
 
        // Close indicates whether to close the connection after
-       // replying to this request.
+       // replying to this request (for servers) or after sending
+       // the request (for clients).
        Close bool
 
-       // 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.
+       // For server requests Host specifies 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".
+       //
+       // For client requests Host optionally overrides the Host
+       // header to send. If empty, the Request.Write method uses
+       // the value of URL.Host.
        Host string
 
        // Form contains the parsed form data, including both the URL
@@ -159,12 +182,24 @@ type Request struct {
        // The HTTP client ignores MultipartForm and uses Body instead.
        MultipartForm *multipart.Form
 
-       // Trailer maps trailer keys to values.  Like for Header, if the
-       // response has multiple trailer lines with the same key, they will be
-       // concatenated, delimited by commas.
-       // For server requests, Trailer is only populated after Body has been
-       // closed or fully consumed.
-       // Trailer support is only partially complete.
+       // Trailer specifies additional headers that are sent after the request
+       // body.
+       //
+       // For server requests the Trailer map initially contains only the
+       // trailer keys, with nil values. (The client declares which trailers it
+       // will later send.)  While the handler is reading from Body, it must
+       // not reference Trailer. After reading from Body returns EOF, Trailer
+       // can be read again and will contain non-nil values, if they were sent
+       // by the client.
+       //
+       // For client requests Trailer must be initialized to a map containing
+       // the trailer keys to later send. The values may be nil or their final
+       // values. The ContentLength must be 0 or -1, to send a chunked request.
+       // After the HTTP request is sent the map values can be updated while
+       // the request body is read. Once the body returns EOF, the caller must
+       // not mutate Trailer.
+       //
+       // Few HTTP clients, servers, or proxies support HTTP trailers.
        Trailer Header
 
        // RemoteAddr allows HTTP servers and other software to record
@@ -382,7 +417,6 @@ func (req *Request) write(w io.Writer, usingProxy bool, extraHeaders Header) err
                return err
        }
 
-       // TODO: split long values?  (If so, should share code with Conn.Write)
        err = req.Header.WriteSubset(w, reqWriteExcludeHeader)
        if err != nil {
                return err
@@ -584,32 +618,6 @@ func ReadRequest(b *bufio.Reader) (req *Request, err error) {
 
        fixPragmaCacheControl(req.Header)
 
-       // TODO: Parse specific header values:
-       //      Accept
-       //      Accept-Encoding
-       //      Accept-Language
-       //      Authorization
-       //      Cache-Control
-       //      Connection
-       //      Date
-       //      Expect
-       //      From
-       //      If-Match
-       //      If-Modified-Since
-       //      If-None-Match
-       //      If-Range
-       //      If-Unmodified-Since
-       //      Max-Forwards
-       //      Proxy-Authorization
-       //      Referer [sic]
-       //      TE (transfer-codings)
-       //      Trailer
-       //      Transfer-Encoding
-       //      Upgrade
-       //      User-Agent
-       //      Via
-       //      Warning
-
        err = readTransfer(req, b)
        if err != nil {
                return nil, err
@@ -728,7 +736,7 @@ func parsePostForm(r *Request) (vs url.Values, err error) {
 func (r *Request) ParseForm() error {
        var err error
        if r.PostForm == nil {
-               if r.Method == "POST" || r.Method == "PUT" {
+               if r.Method == "POST" || r.Method == "PUT" || r.Method == "PATCH" {
                        r.PostForm, err = parsePostForm(r)
                }
                if r.PostForm == nil {
@@ -781,9 +789,7 @@ func (r *Request) ParseMultipartForm(maxMemory int64) error {
        }
 
        mr, err := r.multipartReader()
-       if err == ErrNotMultipart {
-               return nil
-       } else if err != nil {
+       if err != nil {
                return err
        }
 
@@ -861,3 +867,9 @@ func (r *Request) wantsHttp10KeepAlive() bool {
 func (r *Request) wantsClose() bool {
        return hasToken(r.Header.get("Connection"), "close")
 }
+
+func (r *Request) closeBody() {
+       if r.Body != nil {
+               r.Body.Close()
+       }
+}
index 68d141398aab3e2420c22112489cec0af6a57999..b9fa3c2bfc4f68fae9fb4df302325971dd356355 100644 (file)
@@ -60,6 +60,37 @@ func TestPostQuery(t *testing.T) {
        }
 }
 
+func TestPatchQuery(t *testing.T) {
+       req, _ := NewRequest("PATCH", "http://www.google.com/search?q=foo&q=bar&both=x&prio=1&empty=not",
+               strings.NewReader("z=post&both=y&prio=2&empty="))
+       req.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value")
+
+       if q := req.FormValue("q"); q != "foo" {
+               t.Errorf(`req.FormValue("q") = %q, want "foo"`, q)
+       }
+       if z := req.FormValue("z"); z != "post" {
+               t.Errorf(`req.FormValue("z") = %q, want "post"`, z)
+       }
+       if bq, found := req.PostForm["q"]; found {
+               t.Errorf(`req.PostForm["q"] = %q, want no entry in map`, bq)
+       }
+       if bz := req.PostFormValue("z"); bz != "post" {
+               t.Errorf(`req.PostFormValue("z") = %q, want "post"`, bz)
+       }
+       if qs := req.Form["q"]; !reflect.DeepEqual(qs, []string{"foo", "bar"}) {
+               t.Errorf(`req.Form["q"] = %q, want ["foo", "bar"]`, qs)
+       }
+       if both := req.Form["both"]; !reflect.DeepEqual(both, []string{"y", "x"}) {
+               t.Errorf(`req.Form["both"] = %q, want ["y", "x"]`, both)
+       }
+       if prio := req.FormValue("prio"); prio != "2" {
+               t.Errorf(`req.FormValue("prio") = %q, want "2" (from body)`, prio)
+       }
+       if empty := req.FormValue("empty"); empty != "" {
+               t.Errorf(`req.FormValue("empty") = %q, want "" (from body)`, empty)
+       }
+}
+
 type stringMap map[string][]string
 type parseContentTypeTest struct {
        shouldError bool
@@ -123,7 +154,25 @@ func TestMultipartReader(t *testing.T) {
        req.Header = Header{"Content-Type": {"text/plain"}}
        multipart, err = req.MultipartReader()
        if multipart != nil {
-               t.Errorf("unexpected multipart for text/plain")
+               t.Error("unexpected multipart for text/plain")
+       }
+}
+
+func TestParseMultipartForm(t *testing.T) {
+       req := &Request{
+               Method: "POST",
+               Header: Header{"Content-Type": {`multipart/form-data; boundary="foo123"`}},
+               Body:   ioutil.NopCloser(new(bytes.Buffer)),
+       }
+       err := req.ParseMultipartForm(25)
+       if err == nil {
+               t.Error("expected multipart EOF, got nil")
+       }
+
+       req.Header = Header{"Content-Type": {"text/plain"}}
+       err = req.ParseMultipartForm(25)
+       if err != ErrNotMultipart {
+               t.Error("expected ErrNotMultipart for text/plain")
        }
 }
 
@@ -189,16 +238,38 @@ func TestMultipartRequestAuto(t *testing.T) {
        validateTestMultipartContents(t, req, true)
 }
 
-func TestEmptyMultipartRequest(t *testing.T) {
-       // Test that FormValue and FormFile automatically invoke
-       // ParseMultipartForm and return the right values.
-       req, err := NewRequest("GET", "/", nil)
-       if err != nil {
-               t.Errorf("NewRequest err = %q", err)
-       }
+func TestMissingFileMultipartRequest(t *testing.T) {
+       // Test that FormFile returns an error if
+       // the named file is missing.
+       req := newTestMultipartRequest(t)
        testMissingFile(t, req)
 }
 
+// Test that FormValue invokes ParseMultipartForm.
+func TestFormValueCallsParseMultipartForm(t *testing.T) {
+       req, _ := NewRequest("POST", "http://www.google.com/", strings.NewReader("z=post"))
+       req.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value")
+       if req.Form != nil {
+               t.Fatal("Unexpected request Form, want nil")
+       }
+       req.FormValue("z")
+       if req.Form == nil {
+               t.Fatal("ParseMultipartForm not called by FormValue")
+       }
+}
+
+// Test that FormFile invokes ParseMultipartForm.
+func TestFormFileCallsParseMultipartForm(t *testing.T) {
+       req := newTestMultipartRequest(t)
+       if req.Form != nil {
+               t.Fatal("Unexpected request Form, want nil")
+       }
+       req.FormFile("")
+       if req.Form == nil {
+               t.Fatal("ParseMultipartForm not called by FormFile")
+       }
+}
+
 // Test that ParseMultipartForm errors if called
 // after MultipartReader on the same request.
 func TestParseMultipartFormOrder(t *testing.T) {
index 561eea28e5ab6059eabf34f088c84f9276688ce1..dc0e204cac98c7c2d86686ff76c438604513e8dc 100644 (file)
@@ -310,6 +310,46 @@ var reqWriteTests = []reqWriteTest{
                WantError: errors.New("http: Request.ContentLength=5 with nil Body"),
        },
 
+       // Request with a 0 ContentLength and a body with 1 byte content and an error.
+       {
+               Req: Request{
+                       Method:        "POST",
+                       URL:           mustParseURL("/"),
+                       Host:          "example.com",
+                       ProtoMajor:    1,
+                       ProtoMinor:    1,
+                       ContentLength: 0, // as if unset by user
+               },
+
+               Body: func() io.ReadCloser {
+                       err := errors.New("Custom reader error")
+                       errReader := &errorReader{err}
+                       return ioutil.NopCloser(io.MultiReader(strings.NewReader("x"), errReader))
+               },
+
+               WantError: errors.New("Custom reader error"),
+       },
+
+       // Request with a 0 ContentLength and a body without content and an error.
+       {
+               Req: Request{
+                       Method:        "POST",
+                       URL:           mustParseURL("/"),
+                       Host:          "example.com",
+                       ProtoMajor:    1,
+                       ProtoMinor:    1,
+                       ContentLength: 0, // as if unset by user
+               },
+
+               Body: func() io.ReadCloser {
+                       err := errors.New("Custom reader error")
+                       errReader := &errorReader{err}
+                       return ioutil.NopCloser(errReader)
+               },
+
+               WantError: errors.New("Custom reader error"),
+       },
+
        // Verify that DumpRequest preserves the HTTP version number, doesn't add a Host,
        // and doesn't add a User-Agent.
        {
index 0b991c72ef0e462c781778051d7784cea40519fe..5d2c39080e4b9176b10c69e8f50a27431107e494 100644 (file)
@@ -8,6 +8,8 @@ package http
 
 import (
        "bufio"
+       "bytes"
+       "crypto/tls"
        "errors"
        "io"
        "net/textproto"
@@ -45,7 +47,8 @@ type Response struct {
        //
        // The http Client and Transport guarantee that Body is always
        // non-nil, even on responses without a body or responses with
-       // a zero-lengthed body.
+       // a zero-length body. It is the caller's responsibility to
+       // close Body.
        //
        // The Body is automatically dechunked if the server replied
        // with a "chunked" Transfer-Encoding.
@@ -74,6 +77,12 @@ type Response struct {
        // Request's Body is nil (having already been consumed).
        // This is only populated for Client requests.
        Request *Request
+
+       // TLS contains information about the TLS connection on which the
+       // response was received. It is nil for unencrypted responses.
+       // The pointer is shared between responses and should not be
+       // modified.
+       TLS *tls.ConnectionState
 }
 
 // Cookies parses and returns the cookies set in the Set-Cookie headers.
@@ -192,7 +201,6 @@ func (r *Response) ProtoAtLeast(major, minor int) bool {
 //
 // Body is closed after it is sent.
 func (r *Response) Write(w io.Writer) error {
-
        // Status line
        text := r.Status
        if text == "" {
@@ -205,10 +213,45 @@ func (r *Response) Write(w io.Writer) error {
        protoMajor, protoMinor := strconv.Itoa(r.ProtoMajor), strconv.Itoa(r.ProtoMinor)
        statusCode := strconv.Itoa(r.StatusCode) + " "
        text = strings.TrimPrefix(text, statusCode)
-       io.WriteString(w, "HTTP/"+protoMajor+"."+protoMinor+" "+statusCode+text+"\r\n")
+       if _, err := io.WriteString(w, "HTTP/"+protoMajor+"."+protoMinor+" "+statusCode+text+"\r\n"); err != nil {
+               return err
+       }
+
+       // Clone it, so we can modify r1 as needed.
+       r1 := new(Response)
+       *r1 = *r
+       if r1.ContentLength == 0 && r1.Body != nil {
+               // Is it actually 0 length? Or just unknown?
+               var buf [1]byte
+               n, err := r1.Body.Read(buf[:])
+               if err != nil && err != io.EOF {
+                       return err
+               }
+               if n == 0 {
+                       // Reset it to a known zero reader, in case underlying one
+                       // is unhappy being read repeatedly.
+                       r1.Body = eofReader
+               } else {
+                       r1.ContentLength = -1
+                       r1.Body = struct {
+                               io.Reader
+                               io.Closer
+                       }{
+                               io.MultiReader(bytes.NewReader(buf[:1]), r.Body),
+                               r.Body,
+                       }
+               }
+       }
+       // If we're sending a non-chunked HTTP/1.1 response without a
+       // content-length, the only way to do that is the old HTTP/1.0
+       // way, by noting the EOF with a connection close, so we need
+       // to set Close.
+       if r1.ContentLength == -1 && !r1.Close && r1.ProtoAtLeast(1, 1) && !chunked(r1.TransferEncoding) {
+               r1.Close = true
+       }
 
        // Process Body,ContentLength,Close,Trailer
-       tw, err := newTransferWriter(r)
+       tw, err := newTransferWriter(r1)
        if err != nil {
                return err
        }
@@ -223,8 +266,19 @@ func (r *Response) Write(w io.Writer) error {
                return err
        }
 
+       // contentLengthAlreadySent may have been already sent for
+       // POST/PUT requests, even if zero length. See Issue 8180.
+       contentLengthAlreadySent := tw.shouldSendContentLength()
+       if r1.ContentLength == 0 && !chunked(r1.TransferEncoding) && !contentLengthAlreadySent {
+               if _, err := io.WriteString(w, "Content-Length: 0\r\n"); err != nil {
+                       return err
+               }
+       }
+
        // End-of-header
-       io.WriteString(w, "\r\n")
+       if _, err := io.WriteString(w, "\r\n"); err != nil {
+               return err
+       }
 
        // Write body and trailer
        err = tw.WriteBody(w)
index d6e77b19c107d195a7dc0492550bd388bf804a46..4b8946f7ae4821e59b280f692d69e3e9799af464 100644 (file)
@@ -29,6 +29,10 @@ func dummyReq(method string) *Request {
        return &Request{Method: method}
 }
 
+func dummyReq11(method string) *Request {
+       return &Request{Method: method, Proto: "HTTP/1.1", ProtoMajor: 1, ProtoMinor: 1}
+}
+
 var respTests = []respTest{
        // Unchunked response without Content-Length.
        {
index 4799b4792b34cf410b8726f7cc9b7b04519a2e9b..585b13b8504191f2c4db333f39712e54cfca9f53 100644 (file)
@@ -26,7 +26,7 @@ func TestResponseWrite(t *testing.T) {
                                ProtoMinor:    0,
                                Request:       dummyReq("GET"),
                                Header:        Header{},
-                               Body:          ioutil.NopCloser(bytes.NewBufferString("abcdef")),
+                               Body:          ioutil.NopCloser(strings.NewReader("abcdef")),
                                ContentLength: 6,
                        },
 
@@ -49,6 +49,106 @@ func TestResponseWrite(t *testing.T) {
                                "\r\n" +
                                "abcdef",
                },
+               // HTTP/1.1 response with unknown length and Connection: close
+               {
+                       Response{
+                               StatusCode:    200,
+                               ProtoMajor:    1,
+                               ProtoMinor:    1,
+                               Request:       dummyReq("GET"),
+                               Header:        Header{},
+                               Body:          ioutil.NopCloser(strings.NewReader("abcdef")),
+                               ContentLength: -1,
+                               Close:         true,
+                       },
+                       "HTTP/1.1 200 OK\r\n" +
+                               "Connection: close\r\n" +
+                               "\r\n" +
+                               "abcdef",
+               },
+               // HTTP/1.1 response with unknown length and not setting connection: close
+               {
+                       Response{
+                               StatusCode:    200,
+                               ProtoMajor:    1,
+                               ProtoMinor:    1,
+                               Request:       dummyReq11("GET"),
+                               Header:        Header{},
+                               Body:          ioutil.NopCloser(strings.NewReader("abcdef")),
+                               ContentLength: -1,
+                               Close:         false,
+                       },
+                       "HTTP/1.1 200 OK\r\n" +
+                               "Connection: close\r\n" +
+                               "\r\n" +
+                               "abcdef",
+               },
+               // HTTP/1.1 response with unknown length and not setting connection: close, but
+               // setting chunked.
+               {
+                       Response{
+                               StatusCode:       200,
+                               ProtoMajor:       1,
+                               ProtoMinor:       1,
+                               Request:          dummyReq11("GET"),
+                               Header:           Header{},
+                               Body:             ioutil.NopCloser(strings.NewReader("abcdef")),
+                               ContentLength:    -1,
+                               TransferEncoding: []string{"chunked"},
+                               Close:            false,
+                       },
+                       "HTTP/1.1 200 OK\r\n" +
+                               "Transfer-Encoding: chunked\r\n\r\n" +
+                               "6\r\nabcdef\r\n0\r\n\r\n",
+               },
+               // HTTP/1.1 response 0 content-length, and nil body
+               {
+                       Response{
+                               StatusCode:    200,
+                               ProtoMajor:    1,
+                               ProtoMinor:    1,
+                               Request:       dummyReq11("GET"),
+                               Header:        Header{},
+                               Body:          nil,
+                               ContentLength: 0,
+                               Close:         false,
+                       },
+                       "HTTP/1.1 200 OK\r\n" +
+                               "Content-Length: 0\r\n" +
+                               "\r\n",
+               },
+               // HTTP/1.1 response 0 content-length, and non-nil empty body
+               {
+                       Response{
+                               StatusCode:    200,
+                               ProtoMajor:    1,
+                               ProtoMinor:    1,
+                               Request:       dummyReq11("GET"),
+                               Header:        Header{},
+                               Body:          ioutil.NopCloser(strings.NewReader("")),
+                               ContentLength: 0,
+                               Close:         false,
+                       },
+                       "HTTP/1.1 200 OK\r\n" +
+                               "Content-Length: 0\r\n" +
+                               "\r\n",
+               },
+               // HTTP/1.1 response 0 content-length, and non-nil non-empty body
+               {
+                       Response{
+                               StatusCode:    200,
+                               ProtoMajor:    1,
+                               ProtoMinor:    1,
+                               Request:       dummyReq11("GET"),
+                               Header:        Header{},
+                               Body:          ioutil.NopCloser(strings.NewReader("foo")),
+                               ContentLength: 0,
+                               Close:         false,
+                       },
+                       "HTTP/1.1 200 OK\r\n" +
+                               "Connection: close\r\n" +
+                               "\r\nfoo",
+               },
                // HTTP/1.1, chunked coding; empty trailer; close
                {
                        Response{
@@ -91,6 +191,22 @@ func TestResponseWrite(t *testing.T) {
                                "Foo: Bar Baz\r\n" +
                                "\r\n",
                },
+
+               // Want a single Content-Length header. Fixing issue 8180 where
+               // there were two.
+               {
+                       Response{
+                               StatusCode:       StatusOK,
+                               ProtoMajor:       1,
+                               ProtoMinor:       1,
+                               Request:          &Request{Method: "POST"},
+                               Header:           Header{},
+                               ContentLength:    0,
+                               TransferEncoding: nil,
+                               Body:             nil,
+                       },
+                       "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n",
+               },
        }
 
        for i := range respWriteTests {
index e7a3e6ea75f90f6e7b4c29cd220b99ca9217fdc1..8371dd82f584eaddc677479098ba7e381e7e3717 100644 (file)
@@ -851,7 +851,9 @@ func TestTLSHandshakeTimeout(t *testing.T) {
        }
        defer afterTest(t)
        ts := httptest.NewUnstartedServer(HandlerFunc(func(w ResponseWriter, r *Request) {}))
+       errc := make(chanWriter, 10) // but only expecting 1
        ts.Config.ReadTimeout = 250 * time.Millisecond
+       ts.Config.ErrorLog = log.New(errc, "", 0)
        ts.StartTLS()
        defer ts.Close()
        conn, err := net.Dial("tcp", ts.Listener.Addr().String())
@@ -866,6 +868,14 @@ func TestTLSHandshakeTimeout(t *testing.T) {
                        t.Errorf("Read = %d, %v; want an error and no bytes", n, err)
                }
        })
+       select {
+       case v := <-errc:
+               if !strings.Contains(v, "timeout") && !strings.Contains(v, "TLS handshake") {
+                       t.Errorf("expected a TLS handshake timeout error; got %q", v)
+               }
+       case <-time.After(5 * time.Second):
+               t.Errorf("timeout waiting for logged error")
+       }
 }
 
 func TestTLSServer(t *testing.T) {
@@ -878,6 +888,7 @@ func TestTLSServer(t *testing.T) {
                        }
                }
        }))
+       ts.Config.ErrorLog = log.New(ioutil.Discard, "", 0)
        defer ts.Close()
 
        // Connect an idle TCP connection to this server before we run
@@ -922,31 +933,50 @@ func TestTLSServer(t *testing.T) {
 }
 
 type serverExpectTest struct {
-       contentLength    int    // of request body
+       contentLength    int // of request body
+       chunked          bool
        expectation      string // e.g. "100-continue"
        readBody         bool   // whether handler should read the body (if false, sends StatusUnauthorized)
        expectedResponse string // expected substring in first line of http response
 }
 
+func expectTest(contentLength int, expectation string, readBody bool, expectedResponse string) serverExpectTest {
+       return serverExpectTest{
+               contentLength:    contentLength,
+               expectation:      expectation,
+               readBody:         readBody,
+               expectedResponse: expectedResponse,
+       }
+}
+
 var serverExpectTests = []serverExpectTest{
        // Normal 100-continues, case-insensitive.
-       {100, "100-continue", true, "100 Continue"},
-       {100, "100-cOntInUE", true, "100 Continue"},
+       expectTest(100, "100-continue", true, "100 Continue"),
+       expectTest(100, "100-cOntInUE", true, "100 Continue"),
 
        // No 100-continue.
-       {100, "", true, "200 OK"},
+       expectTest(100, "", true, "200 OK"),
 
        // 100-continue but requesting client to deny us,
        // so it never reads the body.
-       {100, "100-continue", false, "401 Unauthorized"},
+       expectTest(100, "100-continue", false, "401 Unauthorized"),
        // Likewise without 100-continue:
-       {100, "", false, "401 Unauthorized"},
+       expectTest(100, "", false, "401 Unauthorized"),
 
        // Non-standard expectations are failures
-       {0, "a-pony", false, "417 Expectation Failed"},
+       expectTest(0, "a-pony", false, "417 Expectation Failed"),
 
-       // Expect-100 requested but no body
-       {0, "100-continue", true, "400 Bad Request"},
+       // Expect-100 requested but no body (is apparently okay: Issue 7625)
+       expectTest(0, "100-continue", true, "200 OK"),
+       // Expect-100 requested but handler doesn't read the body
+       expectTest(0, "100-continue", false, "401 Unauthorized"),
+       // Expect-100 continue with no body, but a chunked body.
+       {
+               expectation:      "100-continue",
+               readBody:         true,
+               chunked:          true,
+               expectedResponse: "100 Continue",
+       },
 }
 
 // Tests that the server responds to the "Expect" request header
@@ -975,21 +1005,38 @@ func TestServerExpect(t *testing.T) {
 
                // Only send the body immediately if we're acting like an HTTP client
                // that doesn't send 100-continue expectations.
-               writeBody := test.contentLength > 0 && strings.ToLower(test.expectation) != "100-continue"
+               writeBody := test.contentLength != 0 && strings.ToLower(test.expectation) != "100-continue"
 
                go func() {
+                       contentLen := fmt.Sprintf("Content-Length: %d", test.contentLength)
+                       if test.chunked {
+                               contentLen = "Transfer-Encoding: chunked"
+                       }
                        _, err := fmt.Fprintf(conn, "POST /?readbody=%v HTTP/1.1\r\n"+
                                "Connection: close\r\n"+
-                               "Content-Length: %d\r\n"+
+                               "%s\r\n"+
                                "Expect: %s\r\nHost: foo\r\n\r\n",
-                               test.readBody, test.contentLength, test.expectation)
+                               test.readBody, contentLen, test.expectation)
                        if err != nil {
                                t.Errorf("On test %#v, error writing request headers: %v", test, err)
                                return
                        }
                        if writeBody {
+                               var targ io.WriteCloser = struct {
+                                       io.Writer
+                                       io.Closer
+                               }{
+                                       conn,
+                                       ioutil.NopCloser(nil),
+                               }
+                               if test.chunked {
+                                       targ = httputil.NewChunkedWriter(conn)
+                               }
                                body := strings.Repeat("A", test.contentLength)
-                               _, err = fmt.Fprint(conn, body)
+                               _, err = fmt.Fprint(targ, body)
+                               if err == nil {
+                                       err = targ.Close()
+                               }
                                if err != nil {
                                        if !test.readBody {
                                                // Server likely already hung up on us.
@@ -2097,7 +2144,7 @@ func TestCodesPreventingContentTypeAndBody(t *testing.T) {
                        got := ht.rawResponse(req)
                        wantStatus := fmt.Sprintf("%d %s", code, StatusText(code))
                        if !strings.Contains(got, wantStatus) {
-                               t.Errorf("Code %d: Wanted %q Modified for %q: %s", code, req, got)
+                               t.Errorf("Code %d: Wanted %q Modified for %q: %s", code, wantStatus, req, got)
                        } else if strings.Contains(got, "Content-Length") {
                                t.Errorf("Code %d: Got a Content-Length from %q: %s", code, req, got)
                        } else if strings.Contains(got, "stuff") {
@@ -2107,6 +2154,21 @@ func TestCodesPreventingContentTypeAndBody(t *testing.T) {
        }
 }
 
+func TestContentTypeOkayOn204(t *testing.T) {
+       ht := newHandlerTest(HandlerFunc(func(w ResponseWriter, r *Request) {
+               w.Header().Set("Content-Length", "123") // suppressed
+               w.Header().Set("Content-Type", "foo/bar")
+               w.WriteHeader(204)
+       }))
+       got := ht.rawResponse("GET / HTTP/1.1")
+       if !strings.Contains(got, "Content-Type: foo/bar") {
+               t.Errorf("Response = %q; want Content-Type: foo/bar", got)
+       }
+       if strings.Contains(got, "Content-Length: 123") {
+               t.Errorf("Response = %q; don't want a Content-Length", got)
+       }
+}
+
 // Issue 6995
 // A server Handler can receive a Request, and then turn around and
 // give a copy of that Request.Body out to the Transport (e.g. any
@@ -2225,8 +2287,8 @@ func TestResponseWriterWriteStringAllocs(t *testing.T) {
                        w.Write([]byte("Hello world"))
                }
        }))
-       before := testing.AllocsPerRun(25, func() { ht.rawResponse("GET / HTTP/1.0") })
-       after := testing.AllocsPerRun(25, func() { ht.rawResponse("GET /s HTTP/1.0") })
+       before := testing.AllocsPerRun(50, func() { ht.rawResponse("GET / HTTP/1.0") })
+       after := testing.AllocsPerRun(50, func() { ht.rawResponse("GET /s HTTP/1.0") })
        if int(after) >= int(before) {
                t.Errorf("WriteString allocs of %v >= Write allocs of %v", after, before)
        }
@@ -2245,6 +2307,230 @@ func TestAppendTime(t *testing.T) {
        }
 }
 
+func TestServerConnState(t *testing.T) {
+       defer afterTest(t)
+       handler := map[string]func(w ResponseWriter, r *Request){
+               "/": func(w ResponseWriter, r *Request) {
+                       fmt.Fprintf(w, "Hello.")
+               },
+               "/close": func(w ResponseWriter, r *Request) {
+                       w.Header().Set("Connection", "close")
+                       fmt.Fprintf(w, "Hello.")
+               },
+               "/hijack": func(w ResponseWriter, r *Request) {
+                       c, _, _ := w.(Hijacker).Hijack()
+                       c.Write([]byte("HTTP/1.0 200 OK\r\nConnection: close\r\n\r\nHello."))
+                       c.Close()
+               },
+               "/hijack-panic": func(w ResponseWriter, r *Request) {
+                       c, _, _ := w.(Hijacker).Hijack()
+                       c.Write([]byte("HTTP/1.0 200 OK\r\nConnection: close\r\n\r\nHello."))
+                       c.Close()
+                       panic("intentional panic")
+               },
+       }
+       ts := httptest.NewUnstartedServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+               handler[r.URL.Path](w, r)
+       }))
+       defer ts.Close()
+
+       var mu sync.Mutex // guard stateLog and connID
+       var stateLog = map[int][]ConnState{}
+       var connID = map[net.Conn]int{}
+
+       ts.Config.ErrorLog = log.New(ioutil.Discard, "", 0)
+       ts.Config.ConnState = func(c net.Conn, state ConnState) {
+               if c == nil {
+                       t.Errorf("nil conn seen in state %s", state)
+                       return
+               }
+               mu.Lock()
+               defer mu.Unlock()
+               id, ok := connID[c]
+               if !ok {
+                       id = len(connID) + 1
+                       connID[c] = id
+               }
+               stateLog[id] = append(stateLog[id], state)
+       }
+       ts.Start()
+
+       mustGet(t, ts.URL+"/")
+       mustGet(t, ts.URL+"/close")
+
+       mustGet(t, ts.URL+"/")
+       mustGet(t, ts.URL+"/", "Connection", "close")
+
+       mustGet(t, ts.URL+"/hijack")
+       mustGet(t, ts.URL+"/hijack-panic")
+
+       // New->Closed
+       {
+               c, err := net.Dial("tcp", ts.Listener.Addr().String())
+               if err != nil {
+                       t.Fatal(err)
+               }
+               c.Close()
+       }
+
+       // New->Active->Closed
+       {
+               c, err := net.Dial("tcp", ts.Listener.Addr().String())
+               if err != nil {
+                       t.Fatal(err)
+               }
+               if _, err := io.WriteString(c, "BOGUS REQUEST\r\n\r\n"); err != nil {
+                       t.Fatal(err)
+               }
+               c.Close()
+       }
+
+       // New->Idle->Closed
+       {
+               c, err := net.Dial("tcp", ts.Listener.Addr().String())
+               if err != nil {
+                       t.Fatal(err)
+               }
+               if _, err := io.WriteString(c, "GET / HTTP/1.1\r\nHost: foo\r\n\r\n"); err != nil {
+                       t.Fatal(err)
+               }
+               res, err := ReadResponse(bufio.NewReader(c), nil)
+               if err != nil {
+                       t.Fatal(err)
+               }
+               if _, err := io.Copy(ioutil.Discard, res.Body); err != nil {
+                       t.Fatal(err)
+               }
+               c.Close()
+       }
+
+       want := map[int][]ConnState{
+               1: []ConnState{StateNew, StateActive, StateIdle, StateActive, StateClosed},
+               2: []ConnState{StateNew, StateActive, StateIdle, StateActive, StateClosed},
+               3: []ConnState{StateNew, StateActive, StateHijacked},
+               4: []ConnState{StateNew, StateActive, StateHijacked},
+               5: []ConnState{StateNew, StateClosed},
+               6: []ConnState{StateNew, StateActive, StateClosed},
+               7: []ConnState{StateNew, StateActive, StateIdle, StateClosed},
+       }
+       logString := func(m map[int][]ConnState) string {
+               var b bytes.Buffer
+               for id, l := range m {
+                       fmt.Fprintf(&b, "Conn %d: ", id)
+                       for _, s := range l {
+                               fmt.Fprintf(&b, "%s ", s)
+                       }
+                       b.WriteString("\n")
+               }
+               return b.String()
+       }
+
+       for i := 0; i < 5; i++ {
+               time.Sleep(time.Duration(i) * 50 * time.Millisecond)
+               mu.Lock()
+               match := reflect.DeepEqual(stateLog, want)
+               mu.Unlock()
+               if match {
+                       return
+               }
+       }
+
+       mu.Lock()
+       t.Errorf("Unexpected events.\nGot log: %s\n   Want: %s\n", logString(stateLog), logString(want))
+       mu.Unlock()
+}
+
+func mustGet(t *testing.T, url string, headers ...string) {
+       req, err := NewRequest("GET", url, nil)
+       if err != nil {
+               t.Fatal(err)
+       }
+       for len(headers) > 0 {
+               req.Header.Add(headers[0], headers[1])
+               headers = headers[2:]
+       }
+       res, err := DefaultClient.Do(req)
+       if err != nil {
+               t.Errorf("Error fetching %s: %v", url, err)
+               return
+       }
+       _, err = ioutil.ReadAll(res.Body)
+       defer res.Body.Close()
+       if err != nil {
+               t.Errorf("Error reading %s: %v", url, err)
+       }
+}
+
+func TestServerKeepAlivesEnabled(t *testing.T) {
+       defer afterTest(t)
+       ts := httptest.NewUnstartedServer(HandlerFunc(func(w ResponseWriter, r *Request) {}))
+       ts.Config.SetKeepAlivesEnabled(false)
+       ts.Start()
+       defer ts.Close()
+       res, err := Get(ts.URL)
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer res.Body.Close()
+       if !res.Close {
+               t.Errorf("Body.Close == false; want true")
+       }
+}
+
+// golang.org/issue/7856
+func TestServerEmptyBodyRace(t *testing.T) {
+       defer afterTest(t)
+       var n int32
+       ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, req *Request) {
+               atomic.AddInt32(&n, 1)
+       }))
+       defer ts.Close()
+       var wg sync.WaitGroup
+       const reqs = 20
+       for i := 0; i < reqs; i++ {
+               wg.Add(1)
+               go func() {
+                       defer wg.Done()
+                       res, err := Get(ts.URL)
+                       if err != nil {
+                               t.Error(err)
+                               return
+                       }
+                       defer res.Body.Close()
+                       _, err = io.Copy(ioutil.Discard, res.Body)
+                       if err != nil {
+                               t.Error(err)
+                               return
+                       }
+               }()
+       }
+       wg.Wait()
+       if got := atomic.LoadInt32(&n); got != reqs {
+               t.Errorf("handler ran %d times; want %d", got, reqs)
+       }
+}
+
+func TestServerConnStateNew(t *testing.T) {
+       sawNew := false // if the test is buggy, we'll race on this variable.
+       srv := &Server{
+               ConnState: func(c net.Conn, state ConnState) {
+                       if state == StateNew {
+                               sawNew = true // testing that this write isn't racy
+                       }
+               },
+               Handler: HandlerFunc(func(w ResponseWriter, r *Request) {}), // irrelevant
+       }
+       srv.Serve(&oneConnListener{
+               conn: &rwTestConn{
+                       Reader: strings.NewReader("GET / HTTP/1.1\r\nHost: foo\r\n\r\n"),
+                       Writer: ioutil.Discard,
+               },
+       })
+       if !sawNew { // testing that this read isn't racy
+               t.Error("StateNew not seen")
+       }
+}
+
 func BenchmarkClientServer(b *testing.B) {
        b.ReportAllocs()
        b.StopTimer()
@@ -2259,7 +2545,6 @@ func BenchmarkClientServer(b *testing.B) {
                if err != nil {
                        b.Fatal("Get:", err)
                }
-               defer res.Body.Close()
                all, err := ioutil.ReadAll(res.Body)
                res.Body.Close()
                if err != nil {
@@ -2282,42 +2567,33 @@ func BenchmarkClientServerParallel64(b *testing.B) {
        benchmarkClientServerParallel(b, 64)
 }
 
-func benchmarkClientServerParallel(b *testing.B, conc int) {
+func benchmarkClientServerParallel(b *testing.B, parallelism int) {
        b.ReportAllocs()
-       b.StopTimer()
        ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, r *Request) {
                fmt.Fprintf(rw, "Hello world.\n")
        }))
        defer ts.Close()
-       b.StartTimer()
-
-       numProcs := runtime.GOMAXPROCS(-1) * conc
-       var wg sync.WaitGroup
-       wg.Add(numProcs)
-       n := int32(b.N)
-       for p := 0; p < numProcs; p++ {
-               go func() {
-                       for atomic.AddInt32(&n, -1) >= 0 {
-                               res, err := Get(ts.URL)
-                               if err != nil {
-                                       b.Logf("Get: %v", err)
-                                       continue
-                               }
-                               all, err := ioutil.ReadAll(res.Body)
-                               res.Body.Close()
-                               if err != nil {
-                                       b.Logf("ReadAll: %v", err)
-                                       continue
-                               }
-                               body := string(all)
-                               if body != "Hello world.\n" {
-                                       panic("Got body: " + body)
-                               }
+       b.ResetTimer()
+       b.SetParallelism(parallelism)
+       b.RunParallel(func(pb *testing.PB) {
+               for pb.Next() {
+                       res, err := Get(ts.URL)
+                       if err != nil {
+                               b.Logf("Get: %v", err)
+                               continue
                        }
-                       wg.Done()
-               }()
-       }
-       wg.Wait()
+                       all, err := ioutil.ReadAll(res.Body)
+                       res.Body.Close()
+                       if err != nil {
+                               b.Logf("ReadAll: %v", err)
+                               continue
+                       }
+                       body := string(all)
+                       if body != "Hello world.\n" {
+                               panic("Got body: " + body)
+                       }
+               }
+       })
 }
 
 // A benchmark for profiling the server without the HTTP client code.
index fea1898fd7e794c3e98f973808c931a9f2acb22e..eae097eb8e91c1e6046dc8ddac0ab35941795296 100644 (file)
@@ -22,6 +22,7 @@ import (
        "strconv"
        "strings"
        "sync"
+       "sync/atomic"
        "time"
 )
 
@@ -138,6 +139,7 @@ func (c *conn) hijack() (rwc net.Conn, buf *bufio.ReadWriter, err error) {
        buf = c.buf
        c.rwc = nil
        c.buf = nil
+       c.setState(rwc, StateHijacked)
        return
 }
 
@@ -496,6 +498,10 @@ func (srv *Server) maxHeaderBytes() int {
        return DefaultMaxHeaderBytes
 }
 
+func (srv *Server) initialLimitedReaderSize() int64 {
+       return int64(srv.maxHeaderBytes()) + 4096 // bufio slop
+}
+
 // wrapper around io.ReaderCloser which on first read, sends an
 // HTTP/1.1 100 Continue header
 type expectContinueReader struct {
@@ -566,7 +572,7 @@ func (c *conn) readRequest() (w *response, err error) {
                }()
        }
 
-       c.lr.N = int64(c.server.maxHeaderBytes()) + 4096 /* bufio slop */
+       c.lr.N = c.server.initialLimitedReaderSize()
        var req *Request
        if req, err = ReadRequest(c.buf.Reader); err != nil {
                if c.lr.N == 0 {
@@ -614,11 +620,11 @@ const maxPostHandlerReadBytes = 256 << 10
 
 func (w *response) WriteHeader(code int) {
        if w.conn.hijacked() {
-               log.Print("http: response.WriteHeader on hijacked connection")
+               w.conn.server.logf("http: response.WriteHeader on hijacked connection")
                return
        }
        if w.wroteHeader {
-               log.Print("http: multiple response.WriteHeader calls")
+               w.conn.server.logf("http: multiple response.WriteHeader calls")
                return
        }
        w.wroteHeader = true
@@ -633,7 +639,7 @@ func (w *response) WriteHeader(code int) {
                if err == nil && v >= 0 {
                        w.contentLength = v
                } else {
-                       log.Printf("http: invalid Content-Length of %q", cl)
+                       w.conn.server.logf("http: invalid Content-Length of %q", cl)
                        w.handlerHeader.Del("Content-Length")
                }
        }
@@ -703,6 +709,7 @@ func (cw *chunkWriter) writeHeader(p []byte) {
        cw.wroteHeader = true
 
        w := cw.res
+       keepAlivesEnabled := w.conn.server.doKeepAlives()
        isHEAD := w.req.Method == "HEAD"
 
        // header is written out to w.conn.buf below. Depending on the
@@ -750,7 +757,7 @@ func (cw *chunkWriter) writeHeader(p []byte) {
 
        // If this was an HTTP/1.0 request with keep-alive and we sent a
        // Content-Length back, we can make this a keep-alive response ...
-       if w.req.wantsHttp10KeepAlive() {
+       if w.req.wantsHttp10KeepAlive() && keepAlivesEnabled {
                sentLength := header.get("Content-Length") != ""
                if sentLength && header.get("Connection") == "keep-alive" {
                        w.closeAfterReply = false
@@ -769,7 +776,7 @@ func (cw *chunkWriter) writeHeader(p []byte) {
                w.closeAfterReply = true
        }
 
-       if header.get("Connection") == "close" {
+       if header.get("Connection") == "close" || !keepAlivesEnabled {
                w.closeAfterReply = true
        }
 
@@ -792,18 +799,16 @@ func (cw *chunkWriter) writeHeader(p []byte) {
        }
 
        code := w.status
-       if !bodyAllowedForStatus(code) {
-               // Must not have body.
-               // RFC 2616 section 10.3.5: "the response MUST NOT include other entity-headers"
-               for _, k := range []string{"Content-Type", "Content-Length", "Transfer-Encoding"} {
-                       delHeader(k)
-               }
-       } else {
+       if bodyAllowedForStatus(code) {
                // If no content type, apply sniffing algorithm to body.
                _, haveType := header["Content-Type"]
                if !haveType {
                        setHeader.contentType = DetectContentType(p)
                }
+       } else {
+               for _, k := range suppressedHeaders(code) {
+                       delHeader(k)
+               }
        }
 
        if _, ok := header["Date"]; !ok {
@@ -815,7 +820,7 @@ func (cw *chunkWriter) writeHeader(p []byte) {
        if hasCL && hasTE && te != "identity" {
                // TODO: return an error if WriteHeader gets a return parameter
                // For now just ignore the Content-Length.
-               log.Printf("http: WriteHeader called with both Transfer-Encoding of %q and a Content-Length of %d",
+               w.conn.server.logf("http: WriteHeader called with both Transfer-Encoding of %q and a Content-Length of %d",
                        te, w.contentLength)
                delHeader("Content-Length")
                hasCL = false
@@ -851,7 +856,7 @@ func (cw *chunkWriter) writeHeader(p []byte) {
                return
        }
 
-       if w.closeAfterReply && !hasToken(cw.header.get("Connection"), "close") {
+       if w.closeAfterReply && (!keepAlivesEnabled || !hasToken(cw.header.get("Connection"), "close")) {
                delHeader("Connection")
                if w.req.ProtoAtLeast(1, 1) {
                        setHeader.connection = "close"
@@ -961,7 +966,7 @@ func (w *response) WriteString(data string) (n int, err error) {
 // either dataB or dataS is non-zero.
 func (w *response) write(lenData int, dataB []byte, dataS string) (n int, err error) {
        if w.conn.hijacked() {
-               log.Print("http: response.Write on hijacked connection")
+               w.conn.server.logf("http: response.Write on hijacked connection")
                return 0, ErrHijacked
        }
        if !w.wroteHeader {
@@ -1079,17 +1084,25 @@ func validNPN(proto string) bool {
        return true
 }
 
+func (c *conn) setState(nc net.Conn, state ConnState) {
+       if hook := c.server.ConnState; hook != nil {
+               hook(nc, state)
+       }
+}
+
 // Serve a new connection.
 func (c *conn) serve() {
+       origConn := c.rwc // copy it before it's set nil on Close or Hijack
        defer func() {
                if err := recover(); err != nil {
                        const size = 64 << 10
                        buf := make([]byte, size)
                        buf = buf[:runtime.Stack(buf, false)]
-                       log.Printf("http: panic serving %v: %v\n%s", c.remoteAddr, err, buf)
+                       c.server.logf("http: panic serving %v: %v\n%s", c.remoteAddr, err, buf)
                }
                if !c.hijacked() {
                        c.close()
+                       c.setState(origConn, StateClosed)
                }
        }()
 
@@ -1101,6 +1114,7 @@ func (c *conn) serve() {
                        c.rwc.SetWriteDeadline(time.Now().Add(d))
                }
                if err := tlsConn.Handshake(); err != nil {
+                       c.server.logf("http: TLS handshake error from %s: %v", c.rwc.RemoteAddr(), err)
                        return
                }
                c.tlsState = new(tls.ConnectionState)
@@ -1116,6 +1130,10 @@ func (c *conn) serve() {
 
        for {
                w, err := c.readRequest()
+               if c.lr.N != c.server.initialLimitedReaderSize() {
+                       // If we read any bytes off the wire, we're active.
+                       c.setState(c.rwc, StateActive)
+               }
                if err != nil {
                        if err == errTooLarge {
                                // Their HTTP client may or may not be
@@ -1138,16 +1156,10 @@ func (c *conn) serve() {
                // Expect 100 Continue support
                req := w.req
                if req.expectsContinue() {
-                       if req.ProtoAtLeast(1, 1) {
+                       if req.ProtoAtLeast(1, 1) && req.ContentLength != 0 {
                                // Wrap the Body reader with one that replies on the connection
                                req.Body = &expectContinueReader{readCloser: req.Body, resp: w}
                        }
-                       if req.ContentLength == 0 {
-                               w.Header().Set("Connection", "close")
-                               w.WriteHeader(StatusBadRequest)
-                               w.finishRequest()
-                               break
-                       }
                        req.Header.Del("Expect")
                } else if req.Header.get("Expect") != "" {
                        w.sendExpectationFailed()
@@ -1170,6 +1182,7 @@ func (c *conn) serve() {
                        }
                        break
                }
+               c.setState(c.rwc, StateIdle)
        }
 }
 
@@ -1564,6 +1577,7 @@ func Serve(l net.Listener, handler Handler) error {
 }
 
 // A Server defines parameters for running an HTTP server.
+// The zero value for Server is a valid configuration.
 type Server struct {
        Addr           string        // TCP address to listen on, ":http" if empty
        Handler        Handler       // handler to invoke, http.DefaultServeMux if nil
@@ -1580,6 +1594,66 @@ type Server struct {
        // and RemoteAddr if not already set.  The connection is
        // automatically closed when the function returns.
        TLSNextProto map[string]func(*Server, *tls.Conn, Handler)
+
+       // ConnState specifies an optional callback function that is
+       // called when a client connection changes state. See the
+       // ConnState type and associated constants for details.
+       ConnState func(net.Conn, ConnState)
+
+       // ErrorLog specifies an optional logger for errors accepting
+       // connections and unexpected behavior from handlers.
+       // If nil, logging goes to os.Stderr via the log package's
+       // standard logger.
+       ErrorLog *log.Logger
+
+       disableKeepAlives int32 // accessed atomically.
+}
+
+// A ConnState represents the state of a client connection to a server.
+// It's used by the optional Server.ConnState hook.
+type ConnState int
+
+const (
+       // StateNew represents a new connection that is expected to
+       // send a request immediately. Connections begin at this
+       // state and then transition to either StateActive or
+       // StateClosed.
+       StateNew ConnState = iota
+
+       // StateActive represents a connection that has read 1 or more
+       // bytes of a request. The Server.ConnState hook for
+       // StateActive fires before the request has entered a handler
+       // and doesn't fire again until the request has been
+       // handled. After the request is handled, the state
+       // transitions to StateClosed, StateHijacked, or StateIdle.
+       StateActive
+
+       // StateIdle represents a connection that has finished
+       // handling a request and is in the keep-alive state, waiting
+       // for a new request. Connections transition from StateIdle
+       // to either StateActive or StateClosed.
+       StateIdle
+
+       // StateHijacked represents a hijacked connection.
+       // This is a terminal state. It does not transition to StateClosed.
+       StateHijacked
+
+       // StateClosed represents a closed connection.
+       // This is a terminal state. Hijacked connections do not
+       // transition to StateClosed.
+       StateClosed
+)
+
+var stateName = map[ConnState]string{
+       StateNew:      "new",
+       StateActive:   "active",
+       StateIdle:     "idle",
+       StateHijacked: "hijacked",
+       StateClosed:   "closed",
+}
+
+func (c ConnState) String() string {
+       return stateName[c]
 }
 
 // serverHandler delegates to either the server's Handler or
@@ -1632,7 +1706,7 @@ func (srv *Server) Serve(l net.Listener) error {
                                if max := 1 * time.Second; tempDelay > max {
                                        tempDelay = max
                                }
-                               log.Printf("http: Accept error: %v; retrying in %v", e, tempDelay)
+                               srv.logf("http: Accept error: %v; retrying in %v", e, tempDelay)
                                time.Sleep(tempDelay)
                                continue
                        }
@@ -1643,10 +1717,35 @@ func (srv *Server) Serve(l net.Listener) error {
                if err != nil {
                        continue
                }
+               c.setState(c.rwc, StateNew) // before Serve can return
                go c.serve()
        }
 }
 
+func (s *Server) doKeepAlives() bool {
+       return atomic.LoadInt32(&s.disableKeepAlives) == 0
+}
+
+// SetKeepAlivesEnabled controls whether HTTP keep-alives are enabled.
+// By default, keep-alives are always enabled. Only very
+// resource-constrained environments or servers in the process of
+// shutting down should disable them.
+func (s *Server) SetKeepAlivesEnabled(v bool) {
+       if v {
+               atomic.StoreInt32(&s.disableKeepAlives, 0)
+       } else {
+               atomic.StoreInt32(&s.disableKeepAlives, 1)
+       }
+}
+
+func (s *Server) logf(format string, args ...interface{}) {
+       if s.ErrorLog != nil {
+               s.ErrorLog.Printf(format, args...)
+       } else {
+               log.Printf(format, args...)
+       }
+}
+
 // ListenAndServe listens on the TCP network address addr
 // and then calls Serve with handler to handle requests
 // on incoming connections.  Handler is typically nil,
@@ -1870,17 +1969,24 @@ func (globalOptionsHandler) ServeHTTP(w ResponseWriter, r *Request) {
        }
 }
 
+type eofReaderWithWriteTo struct{}
+
+func (eofReaderWithWriteTo) WriteTo(io.Writer) (int64, error) { return 0, nil }
+func (eofReaderWithWriteTo) Read([]byte) (int, error)         { return 0, io.EOF }
+
 // eofReader is a non-nil io.ReadCloser that always returns EOF.
-// It embeds a *strings.Reader so it still has a WriteTo method
-// and io.Copy won't need a buffer.
+// It has a WriteTo method so io.Copy won't need a buffer.
 var eofReader = &struct {
-       *strings.Reader
+       eofReaderWithWriteTo
        io.Closer
 }{
-       strings.NewReader(""),
+       eofReaderWithWriteTo{},
        ioutil.NopCloser(nil),
 }
 
+// Verify that an io.Copy from an eofReader won't require a buffer.
+var _ io.WriterTo = eofReader
+
 // initNPNRequest is an HTTP handler that initializes certain
 // uninitialized fields in its *Request. Such partially-initialized
 // Requests come from NPN protocol handlers.
index 2eec9d9abc4788357d636a56d6b5deba79ae6f6b..7f63686528aae75edca37ffbf3cdde4745e63b6c 100644 (file)
@@ -12,11 +12,20 @@ import (
        "io"
        "io/ioutil"
        "net/textproto"
+       "sort"
        "strconv"
        "strings"
        "sync"
 )
 
+type errorReader struct {
+       err error
+}
+
+func (r *errorReader) Read(p []byte) (n int, err error) {
+       return 0, r.err
+}
+
 // transferWriter inspects the fields of a user-supplied Request or Response,
 // sanitizes them without changing the user object and provides methods for
 // writing the respective header, body and trailer in wire format.
@@ -53,8 +62,11 @@ func newTransferWriter(r interface{}) (t *transferWriter, err error) {
                        if t.ContentLength == 0 {
                                // Test to see if it's actually zero or just unset.
                                var buf [1]byte
-                               n, _ := io.ReadFull(t.Body, buf[:])
-                               if n == 1 {
+                               n, rerr := io.ReadFull(t.Body, buf[:])
+                               if rerr != nil && rerr != io.EOF {
+                                       t.ContentLength = -1
+                                       t.Body = &errorReader{rerr}
+                               } else if n == 1 {
                                        // Oh, guess there is data in this Body Reader after all.
                                        // The ContentLength field just wasn't set.
                                        // Stich the Body back together again, re-attaching our
@@ -132,11 +144,10 @@ func (t *transferWriter) shouldSendContentLength() bool {
        return false
 }
 
-func (t *transferWriter) WriteHeader(w io.Writer) (err error) {
+func (t *transferWriter) WriteHeader(w io.Writer) error {
        if t.Close {
-               _, err = io.WriteString(w, "Connection: close\r\n")
-               if err != nil {
-                       return
+               if _, err := io.WriteString(w, "Connection: close\r\n"); err != nil {
+                       return err
                }
        }
 
@@ -144,43 +155,44 @@ func (t *transferWriter) WriteHeader(w io.Writer) (err error) {
        // function of the sanitized field triple (Body, ContentLength,
        // TransferEncoding)
        if t.shouldSendContentLength() {
-               io.WriteString(w, "Content-Length: ")
-               _, err = io.WriteString(w, strconv.FormatInt(t.ContentLength, 10)+"\r\n")
-               if err != nil {
-                       return
+               if _, err := io.WriteString(w, "Content-Length: "); err != nil {
+                       return err
+               }
+               if _, err := io.WriteString(w, strconv.FormatInt(t.ContentLength, 10)+"\r\n"); err != nil {
+                       return err
                }
        } else if chunked(t.TransferEncoding) {
-               _, err = io.WriteString(w, "Transfer-Encoding: chunked\r\n")
-               if err != nil {
-                       return
+               if _, err := io.WriteString(w, "Transfer-Encoding: chunked\r\n"); err != nil {
+                       return err
                }
        }
 
        // Write Trailer header
        if t.Trailer != nil {
-               // TODO: At some point, there should be a generic mechanism for
-               // writing long headers, using HTTP line splitting
-               io.WriteString(w, "Trailer: ")
-               needComma := false
+               keys := make([]string, 0, len(t.Trailer))
                for k := range t.Trailer {
                        k = CanonicalHeaderKey(k)
                        switch k {
                        case "Transfer-Encoding", "Trailer", "Content-Length":
                                return &badStringError{"invalid Trailer key", k}
                        }
-                       if needComma {
-                               io.WriteString(w, ",")
+                       keys = append(keys, k)
+               }
+               if len(keys) > 0 {
+                       sort.Strings(keys)
+                       // TODO: could do better allocation-wise here, but trailers are rare,
+                       // so being lazy for now.
+                       if _, err := io.WriteString(w, "Trailer: "+strings.Join(keys, ",")+"\r\n"); err != nil {
+                               return err
                        }
-                       io.WriteString(w, k)
-                       needComma = true
                }
-               _, err = io.WriteString(w, "\r\n")
        }
 
-       return
+       return nil
 }
 
-func (t *transferWriter) WriteBody(w io.Writer) (err error) {
+func (t *transferWriter) WriteBody(w io.Writer) error {
+       var err error
        var ncopy int64
 
        // Write body
@@ -217,11 +229,16 @@ func (t *transferWriter) WriteBody(w io.Writer) (err error) {
 
        // TODO(petar): Place trailer writer code here.
        if chunked(t.TransferEncoding) {
+               // Write Trailer header
+               if t.Trailer != nil {
+                       if err := t.Trailer.Write(w); err != nil {
+                               return err
+                       }
+               }
                // Last chunk, empty trailer
                _, err = io.WriteString(w, "\r\n")
        }
-
-       return
+       return err
 }
 
 type transferReader struct {
@@ -253,6 +270,22 @@ func bodyAllowedForStatus(status int) bool {
        return true
 }
 
+var (
+       suppressedHeaders304    = []string{"Content-Type", "Content-Length", "Transfer-Encoding"}
+       suppressedHeadersNoBody = []string{"Content-Length", "Transfer-Encoding"}
+)
+
+func suppressedHeaders(status int) []string {
+       switch {
+       case status == 304:
+               // RFC 2616 section 10.3.5: "the response MUST NOT include other entity-headers"
+               return suppressedHeaders304
+       case !bodyAllowedForStatus(status):
+               return suppressedHeadersNoBody
+       }
+       return nil
+}
+
 // msg is *Request or *Response.
 func readTransfer(msg interface{}, r *bufio.Reader) (err error) {
        t := &transferReader{RequestMethod: "GET"}
@@ -499,7 +532,7 @@ func fixTrailer(header Header, te []string) (Header, error) {
                case "Transfer-Encoding", "Trailer", "Content-Length":
                        return nil, &badStringError{"bad trailer key", key}
                }
-               trailer.Del(key)
+               trailer[key] = nil
        }
        if len(trailer) == 0 {
                return nil, nil
@@ -631,13 +664,23 @@ func (b *body) readTrailer() error {
        }
        switch rr := b.hdr.(type) {
        case *Request:
-               rr.Trailer = Header(hdr)
+               mergeSetHeader(&rr.Trailer, Header(hdr))
        case *Response:
-               rr.Trailer = Header(hdr)
+               mergeSetHeader(&rr.Trailer, Header(hdr))
        }
        return nil
 }
 
+func mergeSetHeader(dst *Header, src Header) {
+       if *dst == nil {
+               *dst = src
+               return
+       }
+       for k, vv := range src {
+               (*dst)[k] = vv
+       }
+}
+
 func (b *body) Close() error {
        b.mu.Lock()
        defer b.mu.Unlock()
index 2c312a77a02e1418f6f0d335cf62ee6a56b063b0..b1cc632a782da8a76f5a567d08bb491310136229 100644 (file)
@@ -30,7 +30,14 @@ import (
 // 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}
+var DefaultTransport RoundTripper = &Transport{
+       Proxy: ProxyFromEnvironment,
+       Dial: (&net.Dialer{
+               Timeout:   30 * time.Second,
+               KeepAlive: 30 * time.Second,
+       }).Dial,
+       TLSHandshakeTimeout: 10 * time.Second,
+}
 
 // DefaultMaxIdleConnsPerHost is the default value of Transport's
 // MaxIdleConnsPerHost.
@@ -40,13 +47,13 @@ const DefaultMaxIdleConnsPerHost = 2
 // https, and http proxies (for either http or https with CONNECT).
 // Transport can also cache connections for future re-use.
 type Transport struct {
-       idleMu     sync.Mutex
-       idleConn   map[connectMethodKey][]*persistConn
-       idleConnCh map[connectMethodKey]chan *persistConn
-       reqMu      sync.Mutex
-       reqConn    map[*Request]*persistConn
-       altMu      sync.RWMutex
-       altProto   map[string]RoundTripper // nil or map of URI scheme => RoundTripper
+       idleMu      sync.Mutex
+       idleConn    map[connectMethodKey][]*persistConn
+       idleConnCh  map[connectMethodKey]chan *persistConn
+       reqMu       sync.Mutex
+       reqCanceler map[*Request]func()
+       altMu       sync.RWMutex
+       altProto    map[string]RoundTripper // nil or map of URI scheme => RoundTripper
 
        // Proxy specifies a function to return a proxy for a given
        // Request. If the function returns a non-nil error, the
@@ -63,6 +70,10 @@ type Transport struct {
        // tls.Client. If nil, the default configuration is used.
        TLSClientConfig *tls.Config
 
+       // TLSHandshakeTimeout specifies the maximum amount of time waiting to
+       // wait for a TLS handshake. Zero means no timeout.
+       TLSHandshakeTimeout time.Duration
+
        // DisableKeepAlives, if true, prevents re-use of TCP connections
        // between different HTTP requests.
        DisableKeepAlives bool
@@ -98,6 +109,9 @@ type Transport struct {
 // An error is returned if the proxy environment is invalid.
 // A nil URL and nil error are returned if no proxy is defined in the
 // environment, or a proxy should not be used for the given request.
+//
+// As a special case, if req.URL.Host is "localhost" (with or without
+// a port number), then a nil URL and nil error will be returned.
 func ProxyFromEnvironment(req *Request) (*url.URL, error) {
        proxy := httpProxyEnv.Get()
        if proxy == "" {
@@ -149,9 +163,11 @@ func (tr *transportRequest) extraHeaders() Header {
 // and redirects), see Get, Post, and the Client type.
 func (t *Transport) RoundTrip(req *Request) (resp *Response, err error) {
        if req.URL == nil {
+               req.closeBody()
                return nil, errors.New("http: nil Request.URL")
        }
        if req.Header == nil {
+               req.closeBody()
                return nil, errors.New("http: nil Request.Header")
        }
        if req.URL.Scheme != "http" && req.URL.Scheme != "https" {
@@ -162,16 +178,19 @@ func (t *Transport) RoundTrip(req *Request) (resp *Response, err error) {
                }
                t.altMu.RUnlock()
                if rt == nil {
+                       req.closeBody()
                        return nil, &badStringError{"unsupported protocol scheme", req.URL.Scheme}
                }
                return rt.RoundTrip(req)
        }
        if req.URL.Host == "" {
+               req.closeBody()
                return nil, errors.New("http: no Host in request URL")
        }
        treq := &transportRequest{Request: req}
        cm, err := t.connectMethodForRequest(treq)
        if err != nil {
+               req.closeBody()
                return nil, err
        }
 
@@ -179,8 +198,10 @@ func (t *Transport) RoundTrip(req *Request) (resp *Response, err error) {
        // host (for http or https), the http proxy, or the http proxy
        // pre-CONNECTed to https server.  In any case, we'll be ready
        // to send it requests.
-       pconn, err := t.getConn(cm)
+       pconn, err := t.getConn(req, cm)
        if err != nil {
+               t.setReqCanceler(req, nil)
+               req.closeBody()
                return nil, err
        }
 
@@ -218,9 +239,6 @@ func (t *Transport) CloseIdleConnections() {
        t.idleConn = nil
        t.idleConnCh = nil
        t.idleMu.Unlock()
-       if m == nil {
-               return
-       }
        for _, conns := range m {
                for _, pconn := range conns {
                        pconn.close()
@@ -232,10 +250,10 @@ func (t *Transport) CloseIdleConnections() {
 // connection.
 func (t *Transport) CancelRequest(req *Request) {
        t.reqMu.Lock()
-       pc := t.reqConn[req]
+       cancel := t.reqCanceler[req]
        t.reqMu.Unlock()
-       if pc != nil {
-               pc.conn.Close()
+       if cancel != nil {
+               cancel()
        }
 }
 
@@ -406,16 +424,16 @@ func (t *Transport) getIdleConn(cm connectMethod) (pconn *persistConn) {
        }
 }
 
-func (t *Transport) setReqConn(r *Request, pc *persistConn) {
+func (t *Transport) setReqCanceler(r *Request, fn func()) {
        t.reqMu.Lock()
        defer t.reqMu.Unlock()
-       if t.reqConn == nil {
-               t.reqConn = make(map[*Request]*persistConn)
+       if t.reqCanceler == nil {
+               t.reqCanceler = make(map[*Request]func())
        }
-       if pc != nil {
-               t.reqConn[r] = pc
+       if fn != nil {
+               t.reqCanceler[r] = fn
        } else {
-               delete(t.reqConn, r)
+               delete(t.reqCanceler, r)
        }
 }
 
@@ -430,7 +448,7 @@ func (t *Transport) dial(network, addr string) (c net.Conn, err error) {
 // specified in the connectMethod.  This includes doing a proxy CONNECT
 // and/or setting up TLS.  If this doesn't return an error, the persistConn
 // is ready to write requests to.
-func (t *Transport) getConn(cm connectMethod) (*persistConn, error) {
+func (t *Transport) getConn(req *Request, cm connectMethod) (*persistConn, error) {
        if pc := t.getIdleConn(cm); pc != nil {
                return pc, nil
        }
@@ -440,6 +458,16 @@ func (t *Transport) getConn(cm connectMethod) (*persistConn, error) {
                err error
        }
        dialc := make(chan dialRes)
+
+       handlePendingDial := func() {
+               if v := <-dialc; v.err == nil {
+                       t.putIdleConn(v.pc)
+               }
+       }
+
+       cancelc := make(chan struct{})
+       t.setReqCanceler(req, func() { close(cancelc) })
+
        go func() {
                pc, err := t.dialConn(cm)
                dialc <- dialRes{pc, err}
@@ -456,12 +484,11 @@ func (t *Transport) getConn(cm connectMethod) (*persistConn, error) {
                // else's dial that they didn't use.
                // But our dial is still going, so give it away
                // when it finishes:
-               go func() {
-                       if v := <-dialc; v.err == nil {
-                               t.putIdleConn(v.pc)
-                       }
-               }()
+               go handlePendingDial()
                return pc, nil
+       case <-cancelc:
+               go handlePendingDial()
+               return nil, errors.New("net/http: request canceled while waiting for connection")
        }
 }
 
@@ -477,12 +504,13 @@ func (t *Transport) dialConn(cm connectMethod) (*persistConn, error) {
        pa := cm.proxyAuth()
 
        pconn := &persistConn{
-               t:        t,
-               cacheKey: cm.key(),
-               conn:     conn,
-               reqch:    make(chan requestAndChan, 50),
-               writech:  make(chan writeRequest, 50),
-               closech:  make(chan struct{}),
+               t:          t,
+               cacheKey:   cm.key(),
+               conn:       conn,
+               reqch:      make(chan requestAndChan, 1),
+               writech:    make(chan writeRequest, 1),
+               closech:    make(chan struct{}),
+               writeErrCh: make(chan error, 1),
        }
 
        switch {
@@ -536,19 +564,38 @@ func (t *Transport) dialConn(cm connectMethod) (*persistConn, error) {
                                cfg = &clone
                        }
                }
-               conn = tls.Client(conn, cfg)
-               if err = conn.(*tls.Conn).Handshake(); err != nil {
+               plainConn := conn
+               tlsConn := tls.Client(plainConn, cfg)
+               errc := make(chan error, 2)
+               var timer *time.Timer // for canceling TLS handshake
+               if d := t.TLSHandshakeTimeout; d != 0 {
+                       timer = time.AfterFunc(d, func() {
+                               errc <- tlsHandshakeTimeoutError{}
+                       })
+               }
+               go func() {
+                       err := tlsConn.Handshake()
+                       if timer != nil {
+                               timer.Stop()
+                       }
+                       errc <- err
+               }()
+               if err := <-errc; err != nil {
+                       plainConn.Close()
                        return nil, err
                }
                if !cfg.InsecureSkipVerify {
-                       if err = conn.(*tls.Conn).VerifyHostname(cfg.ServerName); err != nil {
+                       if err := tlsConn.VerifyHostname(cfg.ServerName); err != nil {
+                               plainConn.Close()
                                return nil, err
                        }
                }
-               pconn.conn = conn
+               cs := tlsConn.ConnectionState()
+               pconn.tlsState = &cs
+               pconn.conn = tlsConn
        }
 
-       pconn.br = bufio.NewReader(pconn.conn)
+       pconn.br = bufio.NewReader(noteEOFReader{pconn.conn, &pconn.sawEOF})
        pconn.bw = bufio.NewWriter(pconn.conn)
        go pconn.readLoop()
        go pconn.writeLoop()
@@ -615,8 +662,8 @@ func useProxy(addr string) bool {
 //
 // Cache key form                Description
 // -----------------             -------------------------
-// ||http|foo.com                http directly to server, no proxy
-// ||https|foo.com               https directly to server, no proxy
+// |http|foo.com                 http directly to server, no proxy
+// |https|foo.com                https directly to server, no proxy
 // http://proxy.com|https|foo.com  http to proxy, then CONNECT to foo.com
 // http://proxy.com|http           http to proxy, http to anywhere after that
 //
@@ -680,16 +727,23 @@ type persistConn struct {
        t        *Transport
        cacheKey connectMethodKey
        conn     net.Conn
-       closed   bool                // whether conn has been closed
+       tlsState *tls.ConnectionState
        br       *bufio.Reader       // from conn
+       sawEOF   bool                // whether we've seen EOF from conn; owned by readLoop
        bw       *bufio.Writer       // to conn
        reqch    chan requestAndChan // written by roundTrip; read by readLoop
        writech  chan writeRequest   // written by roundTrip; read by writeLoop
-       closech  chan struct{}       // broadcast close when readLoop (TCP connection) closes
+       closech  chan struct{}       // closed when conn closed
        isProxy  bool
+       // writeErrCh passes the request write error (usually nil)
+       // from the writeLoop goroutine to the readLoop which passes
+       // it off to the res.Body reader, which then uses it to decide
+       // whether or not a connection can be reused. Issue 7569.
+       writeErrCh chan error
 
-       lk                   sync.Mutex // guards following fields
+       lk                   sync.Mutex // guards following fields
        numExpectedResponses int
+       closed               bool // whether conn has been closed
        broken               bool // an error has happened on this connection; marked broken so it's not reused.
        // mutateHeaderFunc is an optional func to modify extra
        // headers on each outbound request before it's written. (the
@@ -697,6 +751,7 @@ type persistConn struct {
        mutateHeaderFunc func(Header)
 }
 
+// isBroken reports whether this connection is in a known broken state.
 func (pc *persistConn) isBroken() bool {
        pc.lk.Lock()
        b := pc.broken
@@ -704,6 +759,10 @@ func (pc *persistConn) isBroken() bool {
        return b
 }
 
+func (pc *persistConn) cancelRequest() {
+       pc.conn.Close()
+}
+
 var remoteSideClosedFunc func(error) bool // or nil to use default
 
 func remoteSideClosed(err error) bool {
@@ -717,7 +776,6 @@ func remoteSideClosed(err error) bool {
 }
 
 func (pc *persistConn) readLoop() {
-       defer close(pc.closech)
        alive := true
 
        for alive {
@@ -725,12 +783,14 @@ func (pc *persistConn) readLoop() {
 
                pc.lk.Lock()
                if pc.numExpectedResponses == 0 {
-                       pc.closeLocked()
-                       pc.lk.Unlock()
-                       if len(pb) > 0 {
-                               log.Printf("Unsolicited response received on idle HTTP channel starting with %q; err=%v",
-                                       string(pb), err)
+                       if !pc.closed {
+                               pc.closeLocked()
+                               if len(pb) > 0 {
+                                       log.Printf("Unsolicited response received on idle HTTP channel starting with %q; err=%v",
+                                               string(pb), err)
+                               }
                        }
+                       pc.lk.Unlock()
                        return
                }
                pc.lk.Unlock()
@@ -749,6 +809,11 @@ func (pc *persistConn) readLoop() {
                                resp, err = ReadResponse(pc.br, rc.req)
                        }
                }
+
+               if resp != nil {
+                       resp.TLS = pc.tlsState
+               }
+
                hasBody := resp != nil && rc.req.Method != "HEAD" && resp.ContentLength != 0
 
                if err != nil {
@@ -758,13 +823,7 @@ func (pc *persistConn) readLoop() {
                                resp.Header.Del("Content-Encoding")
                                resp.Header.Del("Content-Length")
                                resp.ContentLength = -1
-                               gzReader, zerr := gzip.NewReader(resp.Body)
-                               if zerr != nil {
-                                       pc.close()
-                                       err = zerr
-                               } else {
-                                       resp.Body = &readerAndCloser{gzReader, resp.Body}
-                               }
+                               resp.Body = &gzipReader{body: resp.Body}
                        }
                        resp.Body = &bodyEOFSignal{body: resp.Body}
                }
@@ -787,24 +846,18 @@ func (pc *persistConn) readLoop() {
                                return nil
                        }
                        resp.Body.(*bodyEOFSignal).fn = func(err error) {
-                               alive1 := alive
-                               if err != nil {
-                                       alive1 = false
-                               }
-                               if alive1 && !pc.t.putIdleConn(pc) {
-                                       alive1 = false
-                               }
-                               if !alive1 || pc.isBroken() {
-                                       pc.close()
-                               }
-                               waitForBodyRead <- alive1
+                               waitForBodyRead <- alive &&
+                                       err == nil &&
+                                       !pc.sawEOF &&
+                                       pc.wroteRequest() &&
+                                       pc.t.putIdleConn(pc)
                        }
                }
 
                if alive && !hasBody {
-                       if !pc.t.putIdleConn(pc) {
-                               alive = false
-                       }
+                       alive = !pc.sawEOF &&
+                               pc.wroteRequest() &&
+                               pc.t.putIdleConn(pc)
                }
 
                rc.ch <- responseAndError{resp, err}
@@ -812,10 +865,14 @@ 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 {
-                       alive = <-waitForBodyRead
+                       select {
+                       case alive = <-waitForBodyRead:
+                       case <-pc.closech:
+                               alive = false
+                       }
                }
 
-               pc.t.setReqConn(rc.req, nil)
+               pc.t.setReqCanceler(rc.req, nil)
 
                if !alive {
                        pc.close()
@@ -837,14 +894,44 @@ func (pc *persistConn) writeLoop() {
                        }
                        if err != nil {
                                pc.markBroken()
+                               wr.req.Request.closeBody()
                        }
-                       wr.ch <- err
+                       pc.writeErrCh <- err // to the body reader, which might recycle us
+                       wr.ch <- err         // to the roundTrip function
                case <-pc.closech:
                        return
                }
        }
 }
 
+// wroteRequest is a check before recycling a connection that the previous write
+// (from writeLoop above) happened and was successful.
+func (pc *persistConn) wroteRequest() bool {
+       select {
+       case err := <-pc.writeErrCh:
+               // Common case: the write happened well before the response, so
+               // avoid creating a timer.
+               return err == nil
+       default:
+               // Rare case: the request was written in writeLoop above but
+               // before it could send to pc.writeErrCh, the reader read it
+               // all, processed it, and called us here. In this case, give the
+               // write goroutine a bit of time to finish its send.
+               //
+               // Less rare case: We also get here in the legitimate case of
+               // Issue 7569, where the writer is still writing (or stalled),
+               // but the server has already replied. In this case, we don't
+               // want to wait too long, and we want to return false so this
+               // connection isn't re-used.
+               select {
+               case err := <-pc.writeErrCh:
+                       return err == nil
+               case <-time.After(50 * time.Millisecond):
+                       return false
+               }
+       }
+}
+
 type responseAndError struct {
        res *Response
        err error
@@ -882,7 +969,7 @@ var errTimeout error = &httpError{err: "net/http: timeout awaiting response head
 var errClosed error = &httpError{err: "net/http: transport closed before response was received"}
 
 func (pc *persistConn) roundTrip(req *transportRequest) (resp *Response, err error) {
-       pc.t.setReqConn(req.Request, pc)
+       pc.t.setReqCanceler(req.Request, pc.cancelRequest)
        pc.lk.Lock()
        pc.numExpectedResponses++
        headerFn := pc.mutateHeaderFunc
@@ -967,7 +1054,7 @@ WaitResponse:
        pc.lk.Unlock()
 
        if re.err != nil {
-               pc.t.setReqConn(req.Request, nil)
+               pc.t.setReqCanceler(req.Request, nil)
        }
        return re.res, re.err
 }
@@ -992,6 +1079,7 @@ func (pc *persistConn) closeLocked() {
        if !pc.closed {
                pc.conn.Close()
                pc.closed = true
+               close(pc.closech)
        }
        pc.mutateHeaderFunc = nil
 }
@@ -1074,7 +1162,47 @@ func (es *bodyEOFSignal) condfn(err error) {
        es.fn = nil
 }
 
+// gzipReader wraps a response body so it can lazily
+// call gzip.NewReader on the first call to Read
+type gzipReader struct {
+       body io.ReadCloser // underlying Response.Body
+       zr   io.Reader     // lazily-initialized gzip reader
+}
+
+func (gz *gzipReader) Read(p []byte) (n int, err error) {
+       if gz.zr == nil {
+               gz.zr, err = gzip.NewReader(gz.body)
+               if err != nil {
+                       return 0, err
+               }
+       }
+       return gz.zr.Read(p)
+}
+
+func (gz *gzipReader) Close() error {
+       return gz.body.Close()
+}
+
 type readerAndCloser struct {
        io.Reader
        io.Closer
 }
+
+type tlsHandshakeTimeoutError struct{}
+
+func (tlsHandshakeTimeoutError) Timeout() bool   { return true }
+func (tlsHandshakeTimeoutError) Temporary() bool { return true }
+func (tlsHandshakeTimeoutError) Error() string   { return "net/http: TLS handshake timeout" }
+
+type noteEOFReader struct {
+       r      io.Reader
+       sawEOF *bool
+}
+
+func (nr noteEOFReader) Read(p []byte) (n int, err error) {
+       n, err = nr.r.Read(p)
+       if err == io.EOF {
+               *nr.sawEOF = true
+       }
+       return
+}
index 2678d71b1dec19b0e9257ecadd69c61d3648c3ff..964ca0fca5434ff06f536d94a203ab96bdc28b33 100644 (file)
@@ -11,9 +11,12 @@ import (
        "bytes"
        "compress/gzip"
        "crypto/rand"
+       "crypto/tls"
+       "errors"
        "fmt"
        "io"
        "io/ioutil"
+       "log"
        "net"
        "net/http"
        . "net/http"
@@ -54,21 +57,21 @@ func (c *testCloseConn) Close() error {
 // been closed.
 type testConnSet struct {
        t      *testing.T
+       mu     sync.Mutex // guards closed and list
        closed map[net.Conn]bool
        list   []net.Conn // in order created
-       mutex  sync.Mutex
 }
 
 func (tcs *testConnSet) insert(c net.Conn) {
-       tcs.mutex.Lock()
-       defer tcs.mutex.Unlock()
+       tcs.mu.Lock()
+       defer tcs.mu.Unlock()
        tcs.closed[c] = false
        tcs.list = append(tcs.list, c)
 }
 
 func (tcs *testConnSet) remove(c net.Conn) {
-       tcs.mutex.Lock()
-       defer tcs.mutex.Unlock()
+       tcs.mu.Lock()
+       defer tcs.mu.Unlock()
        tcs.closed[c] = true
 }
 
@@ -91,11 +94,19 @@ func makeTestDial(t *testing.T) (*testConnSet, func(n, addr string) (net.Conn, e
 }
 
 func (tcs *testConnSet) check(t *testing.T) {
-       tcs.mutex.Lock()
-       defer tcs.mutex.Unlock()
-
-       for i, c := range tcs.list {
-               if !tcs.closed[c] {
+       tcs.mu.Lock()
+       defer tcs.mu.Unlock()
+       for i := 4; i >= 0; i-- {
+               for i, c := range tcs.list {
+                       if tcs.closed[c] {
+                               continue
+                       }
+                       if i != 0 {
+                               tcs.mu.Unlock()
+                               time.Sleep(50 * time.Millisecond)
+                               tcs.mu.Lock()
+                               continue
+                       }
                        t.Errorf("TCP connection #%d, %p (of %d total) was not closed", i+1, c, len(tcs.list))
                }
        }
@@ -347,10 +358,11 @@ func TestTransportMaxPerHostIdleConns(t *testing.T) {
                resp, err := c.Get(ts.URL)
                if err != nil {
                        t.Error(err)
+                       return
                }
-               _, err = ioutil.ReadAll(resp.Body)
-               if err != nil {
-                       t.Fatalf("ReadAll: %v", err)
+               if _, err := ioutil.ReadAll(resp.Body); err != nil {
+                       t.Errorf("ReadAll: %v", err)
+                       return
                }
                donech <- true
        }
@@ -791,6 +803,33 @@ func TestTransportGzipRecursive(t *testing.T) {
        }
 }
 
+// golang.org/issue/7750: request fails when server replies with
+// a short gzip body
+func TestTransportGzipShort(t *testing.T) {
+       defer afterTest(t)
+       ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+               w.Header().Set("Content-Encoding", "gzip")
+               w.Write([]byte{0x1f, 0x8b})
+       }))
+       defer ts.Close()
+
+       tr := &Transport{}
+       defer tr.CloseIdleConnections()
+       c := &Client{Transport: tr}
+       res, err := c.Get(ts.URL)
+       if err != nil {
+               t.Fatal(err)
+       }
+       defer res.Body.Close()
+       _, err = ioutil.ReadAll(res.Body)
+       if err == nil {
+               t.Fatal("Expect an error from reading a body.")
+       }
+       if err != io.ErrUnexpectedEOF {
+               t.Errorf("ReadAll error = %v; want io.ErrUnexpectedEOF", err)
+       }
+}
+
 // tests that persistent goroutine connections shut down when no longer desired.
 func TestTransportPersistConnLeak(t *testing.T) {
        if runtime.GOOS == "plan9" {
@@ -1211,9 +1250,13 @@ func TestTransportResponseHeaderTimeout(t *testing.T) {
        if testing.Short() {
                t.Skip("skipping timeout test in -short mode")
        }
+       inHandler := make(chan bool, 1)
        mux := NewServeMux()
-       mux.HandleFunc("/fast", func(w ResponseWriter, r *Request) {})
+       mux.HandleFunc("/fast", func(w ResponseWriter, r *Request) {
+               inHandler <- true
+       })
        mux.HandleFunc("/slow", func(w ResponseWriter, r *Request) {
+               inHandler <- true
                time.Sleep(2 * time.Second)
        })
        ts := httptest.NewServer(mux)
@@ -1236,6 +1279,12 @@ func TestTransportResponseHeaderTimeout(t *testing.T) {
        }
        for i, tt := range tests {
                res, err := c.Get(ts.URL + tt.path)
+               select {
+               case <-inHandler:
+               case <-time.After(5 * time.Second):
+                       t.Errorf("never entered handler for test index %d, %s", i, tt.path)
+                       continue
+               }
                if err != nil {
                        uerr, ok := err.(*url.Error)
                        if !ok {
@@ -1321,6 +1370,60 @@ func TestTransportCancelRequest(t *testing.T) {
        }
 }
 
+func TestTransportCancelRequestInDial(t *testing.T) {
+       defer afterTest(t)
+       if testing.Short() {
+               t.Skip("skipping test in -short mode")
+       }
+       var logbuf bytes.Buffer
+       eventLog := log.New(&logbuf, "", 0)
+
+       unblockDial := make(chan bool)
+       defer close(unblockDial)
+
+       inDial := make(chan bool)
+       tr := &Transport{
+               Dial: func(network, addr string) (net.Conn, error) {
+                       eventLog.Println("dial: blocking")
+                       inDial <- true
+                       <-unblockDial
+                       return nil, errors.New("nope")
+               },
+       }
+       cl := &Client{Transport: tr}
+       gotres := make(chan bool)
+       req, _ := NewRequest("GET", "http://something.no-network.tld/", nil)
+       go func() {
+               _, err := cl.Do(req)
+               eventLog.Printf("Get = %v", err)
+               gotres <- true
+       }()
+
+       select {
+       case <-inDial:
+       case <-time.After(5 * time.Second):
+               t.Fatal("timeout; never saw blocking dial")
+       }
+
+       eventLog.Printf("canceling")
+       tr.CancelRequest(req)
+
+       select {
+       case <-gotres:
+       case <-time.After(5 * time.Second):
+               panic("hang. events are: " + logbuf.String())
+       }
+
+       got := logbuf.String()
+       want := `dial: blocking
+canceling
+Get = Get http://something.no-network.tld/: net/http: request canceled while waiting for connection
+`
+       if got != want {
+               t.Errorf("Got events:\n%s\nWant:\n%s", got, want)
+       }
+}
+
 // golang.org/issue/3672 -- Client can't close HTTP stream
 // Calling Close on a Response.Body used to just read until EOF.
 // Now it actually closes the TCP connection.
@@ -1450,8 +1553,10 @@ func TestTransportSocketLateBinding(t *testing.T) {
        dialGate := make(chan bool, 1)
        tr := &Transport{
                Dial: func(n, addr string) (net.Conn, error) {
-                       <-dialGate
-                       return net.Dial(n, addr)
+                       if <-dialGate {
+                               return net.Dial(n, addr)
+                       }
+                       return nil, errors.New("manually closed")
                },
                DisableKeepAlives: false,
        }
@@ -1486,7 +1591,7 @@ func TestTransportSocketLateBinding(t *testing.T) {
                t.Fatalf("/foo came from conn %q; /bar came from %q instead", fooAddr, barAddr)
        }
        barRes.Body.Close()
-       dialGate <- true
+       dialGate <- false
 }
 
 // Issue 2184
@@ -1637,10 +1742,7 @@ var proxyFromEnvTests = []proxyFromEnvTest{
 }
 
 func TestProxyFromEnvironment(t *testing.T) {
-       os.Setenv("HTTP_PROXY", "")
-       os.Setenv("http_proxy", "")
-       os.Setenv("NO_PROXY", "")
-       os.Setenv("no_proxy", "")
+       ResetProxyEnv()
        for _, tt := range proxyFromEnvTests {
                os.Setenv("HTTP_PROXY", tt.env)
                os.Setenv("NO_PROXY", tt.noenv)
@@ -1722,6 +1824,308 @@ func TestTransportClosesRequestBody(t *testing.T) {
        }
 }
 
+func TestTransportTLSHandshakeTimeout(t *testing.T) {
+       defer afterTest(t)
+       if testing.Short() {
+               t.Skip("skipping in short mode")
+       }
+       ln := newLocalListener(t)
+       defer ln.Close()
+       testdonec := make(chan struct{})
+       defer close(testdonec)
+
+       go func() {
+               c, err := ln.Accept()
+               if err != nil {
+                       t.Error(err)
+                       return
+               }
+               <-testdonec
+               c.Close()
+       }()
+
+       getdonec := make(chan struct{})
+       go func() {
+               defer close(getdonec)
+               tr := &Transport{
+                       Dial: func(_, _ string) (net.Conn, error) {
+                               return net.Dial("tcp", ln.Addr().String())
+                       },
+                       TLSHandshakeTimeout: 250 * time.Millisecond,
+               }
+               cl := &Client{Transport: tr}
+               _, err := cl.Get("https://dummy.tld/")
+               if err == nil {
+                       t.Error("expected error")
+                       return
+               }
+               ue, ok := err.(*url.Error)
+               if !ok {
+                       t.Errorf("expected url.Error; got %#v", err)
+                       return
+               }
+               ne, ok := ue.Err.(net.Error)
+               if !ok {
+                       t.Errorf("expected net.Error; got %#v", err)
+                       return
+               }
+               if !ne.Timeout() {
+                       t.Errorf("expected timeout error; got %v", err)
+               }
+               if !strings.Contains(err.Error(), "handshake timeout") {
+                       t.Errorf("expected 'handshake timeout' in error; got %v", err)
+               }
+       }()
+       select {
+       case <-getdonec:
+       case <-time.After(5 * time.Second):
+               t.Error("test timeout; TLS handshake hung?")
+       }
+}
+
+// Trying to repro golang.org/issue/3514
+func TestTLSServerClosesConnection(t *testing.T) {
+       defer afterTest(t)
+       if runtime.GOOS == "windows" {
+               t.Skip("skipping flaky test on Windows; golang.org/issue/7634")
+       }
+       closedc := make(chan bool, 1)
+       ts := httptest.NewTLSServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+               if strings.Contains(r.URL.Path, "/keep-alive-then-die") {
+                       conn, _, _ := w.(Hijacker).Hijack()
+                       conn.Write([]byte("HTTP/1.1 200 OK\r\nContent-Length: 3\r\n\r\nfoo"))
+                       conn.Close()
+                       closedc <- true
+                       return
+               }
+               fmt.Fprintf(w, "hello")
+       }))
+       defer ts.Close()
+       tr := &Transport{
+               TLSClientConfig: &tls.Config{
+                       InsecureSkipVerify: true,
+               },
+       }
+       defer tr.CloseIdleConnections()
+       client := &Client{Transport: tr}
+
+       var nSuccess = 0
+       var errs []error
+       const trials = 20
+       for i := 0; i < trials; i++ {
+               tr.CloseIdleConnections()
+               res, err := client.Get(ts.URL + "/keep-alive-then-die")
+               if err != nil {
+                       t.Fatal(err)
+               }
+               <-closedc
+               slurp, err := ioutil.ReadAll(res.Body)
+               if err != nil {
+                       t.Fatal(err)
+               }
+               if string(slurp) != "foo" {
+                       t.Errorf("Got %q, want foo", slurp)
+               }
+
+               // Now try again and see if we successfully
+               // pick a new connection.
+               res, err = client.Get(ts.URL + "/")
+               if err != nil {
+                       errs = append(errs, err)
+                       continue
+               }
+               slurp, err = ioutil.ReadAll(res.Body)
+               if err != nil {
+                       errs = append(errs, err)
+                       continue
+               }
+               nSuccess++
+       }
+       if nSuccess > 0 {
+               t.Logf("successes = %d of %d", nSuccess, trials)
+       } else {
+               t.Errorf("All runs failed:")
+       }
+       for _, err := range errs {
+               t.Logf("  err: %v", err)
+       }
+}
+
+// byteFromChanReader is an io.Reader that reads a single byte at a
+// time from the channel.  When the channel is closed, the reader
+// returns io.EOF.
+type byteFromChanReader chan byte
+
+func (c byteFromChanReader) Read(p []byte) (n int, err error) {
+       if len(p) == 0 {
+               return
+       }
+       b, ok := <-c
+       if !ok {
+               return 0, io.EOF
+       }
+       p[0] = b
+       return 1, nil
+}
+
+// Verifies that the Transport doesn't reuse a connection in the case
+// where the server replies before the request has been fully
+// written. We still honor that reply (see TestIssue3595), but don't
+// send future requests on the connection because it's then in a
+// questionable state.
+// golang.org/issue/7569
+func TestTransportNoReuseAfterEarlyResponse(t *testing.T) {
+       defer afterTest(t)
+       var sconn struct {
+               sync.Mutex
+               c net.Conn
+       }
+       var getOkay bool
+       closeConn := func() {
+               sconn.Lock()
+               defer sconn.Unlock()
+               if sconn.c != nil {
+                       sconn.c.Close()
+                       sconn.c = nil
+                       if !getOkay {
+                               t.Logf("Closed server connection")
+                       }
+               }
+       }
+       defer closeConn()
+
+       ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+               if r.Method == "GET" {
+                       io.WriteString(w, "bar")
+                       return
+               }
+               conn, _, _ := w.(Hijacker).Hijack()
+               sconn.Lock()
+               sconn.c = conn
+               sconn.Unlock()
+               conn.Write([]byte("HTTP/1.1 200 OK\r\nContent-Length: 3\r\n\r\nfoo")) // keep-alive
+               go io.Copy(ioutil.Discard, conn)
+       }))
+       defer ts.Close()
+       tr := &Transport{}
+       defer tr.CloseIdleConnections()
+       client := &Client{Transport: tr}
+
+       const bodySize = 256 << 10
+       finalBit := make(byteFromChanReader, 1)
+       req, _ := NewRequest("POST", ts.URL, io.MultiReader(io.LimitReader(neverEnding('x'), bodySize-1), finalBit))
+       req.ContentLength = bodySize
+       res, err := client.Do(req)
+       if err := wantBody(res, err, "foo"); err != nil {
+               t.Errorf("POST response: %v", err)
+       }
+       donec := make(chan bool)
+       go func() {
+               defer close(donec)
+               res, err = client.Get(ts.URL)
+               if err := wantBody(res, err, "bar"); err != nil {
+                       t.Errorf("GET response: %v", err)
+                       return
+               }
+               getOkay = true // suppress test noise
+       }()
+       time.AfterFunc(5*time.Second, closeConn)
+       select {
+       case <-donec:
+               finalBit <- 'x' // unblock the writeloop of the first Post
+               close(finalBit)
+       case <-time.After(7 * time.Second):
+               t.Fatal("timeout waiting for GET request to finish")
+       }
+}
+
+type errorReader struct {
+       err error
+}
+
+func (e errorReader) Read(p []byte) (int, error) { return 0, e.err }
+
+type closerFunc func() error
+
+func (f closerFunc) Close() error { return f() }
+
+// Issue 6981
+func TestTransportClosesBodyOnError(t *testing.T) {
+       if runtime.GOOS == "plan9" {
+               t.Skip("skipping test; see http://golang.org/issue/7782")
+       }
+       defer afterTest(t)
+       readBody := make(chan error, 1)
+       ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
+               _, err := ioutil.ReadAll(r.Body)
+               readBody <- err
+       }))
+       defer ts.Close()
+       fakeErr := errors.New("fake error")
+       didClose := make(chan bool, 1)
+       req, _ := NewRequest("POST", ts.URL, struct {
+               io.Reader
+               io.Closer
+       }{
+               io.MultiReader(io.LimitReader(neverEnding('x'), 1<<20), errorReader{fakeErr}),
+               closerFunc(func() error {
+                       select {
+                       case didClose <- true:
+                       default:
+                       }
+                       return nil
+               }),
+       })
+       res, err := DefaultClient.Do(req)
+       if res != nil {
+               defer res.Body.Close()
+       }
+       if err == nil || !strings.Contains(err.Error(), fakeErr.Error()) {
+               t.Fatalf("Do error = %v; want something containing %q", err, fakeErr.Error())
+       }
+       select {
+       case err := <-readBody:
+               if err == nil {
+                       t.Errorf("Unexpected success reading request body from handler; want 'unexpected EOF reading trailer'")
+               }
+       case <-time.After(5 * time.Second):
+               t.Error("timeout waiting for server handler to complete")
+       }
+       select {
+       case <-didClose:
+       default:
+               t.Errorf("didn't see Body.Close")
+       }
+}
+
+func wantBody(res *http.Response, err error, want string) error {
+       if err != nil {
+               return err
+       }
+       slurp, err := ioutil.ReadAll(res.Body)
+       if err != nil {
+               return fmt.Errorf("error reading body: %v", err)
+       }
+       if string(slurp) != want {
+               return fmt.Errorf("body = %q; want %q", slurp, want)
+       }
+       if err := res.Body.Close(); err != nil {
+               return fmt.Errorf("body Close = %v", err)
+       }
+       return nil
+}
+
+func newLocalListener(t *testing.T) net.Listener {
+       ln, err := net.Listen("tcp", "127.0.0.1:0")
+       if err != nil {
+               ln, err = net.Listen("tcp6", "[::1]:0")
+       }
+       if err != nil {
+               t.Fatal(err)
+       }
+       return ln
+}
+
 type countCloseReader struct {
        n *int
        io.Reader
index 0713e9cd6a973b7b1082b83a5f19958040027e07..2e9f1ebc6791f9ab7b4cbbab3b694ef4e43b4414 100644 (file)
@@ -7,11 +7,11 @@ package net
 import "errors"
 
 var (
-       errInvalidInterface         = errors.New("net: invalid interface")
-       errInvalidInterfaceIndex    = errors.New("net: invalid interface index")
-       errInvalidInterfaceName     = errors.New("net: invalid interface name")
-       errNoSuchInterface          = errors.New("net: no such interface")
-       errNoSuchMulticastInterface = errors.New("net: no such multicast interface")
+       errInvalidInterface         = errors.New("invalid network interface")
+       errInvalidInterfaceIndex    = errors.New("invalid network interface index")
+       errInvalidInterfaceName     = errors.New("invalid network interface name")
+       errNoSuchInterface          = errors.New("no such network interface")
+       errNoSuchMulticastInterface = errors.New("no such multicast network interface")
 )
 
 // Interface represents a mapping between network interface name
index a4eb731da441ed606cd9cab8ec34ca8b8a9c83ff..c38fb7f7651d9f584de7e0a272dad1aeca79d98d 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.
 
-// +build plan9
+// +build nacl plan9 solaris
 
 package net
 
index ea183f1d3ebe36d741ad701c5a2890d1f5b800f3..0632dafc65e642cfef1ab535386f224fb0600080 100644 (file)
@@ -247,7 +247,7 @@ var ipConnLocalNameTests = []struct {
 
 func TestIPConnLocalName(t *testing.T) {
        switch runtime.GOOS {
-       case "plan9", "windows":
+       case "nacl", "plan9", "windows":
                t.Skipf("skipping test on %q", runtime.GOOS)
        default:
                if os.Getuid() != 0 {
@@ -277,7 +277,7 @@ func TestIPConnRemoteName(t *testing.T) {
                }
        }
 
-       raddr := &IPAddr{IP: IPv4(127, 0, 0, 10).To4()}
+       raddr := &IPAddr{IP: IPv4(127, 0, 0, 1).To4()}
        c, err := DialIP("ip:tcp", &IPAddr{IP: IPv4(127, 0, 0, 1)}, raddr)
        if err != nil {
                t.Fatalf("DialIP failed: %v", err)
index a1a008ac413c6d12fed4b31fa982169dfe651a3c..bbb3f3ed66c364948f86bd8ee2eb3632bf8e08ab 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.
 
-// +build darwin dragonfly freebsd linux netbsd openbsd windows
+// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows
 
 package net
 
@@ -79,7 +79,7 @@ func (c *IPConn) ReadFromIP(b []byte) (int, *IPAddr, error) {
        // TODO(cw,rsc): consider using readv if we know the family
        // type to avoid the header trim/copy
        var addr *IPAddr
-       n, sa, err := c.fd.ReadFrom(b)
+       n, sa, err := c.fd.readFrom(b)
        switch sa := sa.(type) {
        case *syscall.SockaddrInet4:
                addr = &IPAddr{IP: sa.Addr[0:]}
@@ -112,7 +112,7 @@ func (c *IPConn) ReadMsgIP(b, oob []byte) (n, oobn, flags int, addr *IPAddr, err
                return 0, 0, 0, nil, syscall.EINVAL
        }
        var sa syscall.Sockaddr
-       n, oobn, flags, sa, err = c.fd.ReadMsg(b, oob)
+       n, oobn, flags, sa, err = c.fd.readMsg(b, oob)
        switch sa := sa.(type) {
        case *syscall.SockaddrInet4:
                addr = &IPAddr{IP: sa.Addr[0:]}
@@ -133,6 +133,9 @@ func (c *IPConn) WriteToIP(b []byte, addr *IPAddr) (int, error) {
        if !c.ok() {
                return 0, syscall.EINVAL
        }
+       if c.fd.isConnected {
+               return 0, &OpError{Op: "write", Net: c.fd.net, Addr: addr, Err: ErrWriteToConnected}
+       }
        if addr == nil {
                return 0, &OpError{Op: "write", Net: c.fd.net, Addr: nil, Err: errMissingAddress}
        }
@@ -140,7 +143,7 @@ func (c *IPConn) WriteToIP(b []byte, addr *IPAddr) (int, error) {
        if err != nil {
                return 0, &OpError{"write", c.fd.net, addr, err}
        }
-       return c.fd.WriteTo(b, sa)
+       return c.fd.writeTo(b, sa)
 }
 
 // WriteTo implements the PacketConn WriteTo method.
@@ -162,6 +165,9 @@ func (c *IPConn) WriteMsgIP(b, oob []byte, addr *IPAddr) (n, oobn int, err error
        if !c.ok() {
                return 0, 0, syscall.EINVAL
        }
+       if c.fd.isConnected {
+               return 0, 0, &OpError{Op: "write", Net: c.fd.net, Addr: addr, Err: ErrWriteToConnected}
+       }
        if addr == nil {
                return 0, 0, &OpError{Op: "write", Net: c.fd.net, Addr: nil, Err: errMissingAddress}
        }
@@ -169,7 +175,7 @@ func (c *IPConn) WriteMsgIP(b, oob []byte, addr *IPAddr) (n, oobn int, err error
        if err != nil {
                return 0, 0, &OpError{"write", c.fd.net, addr, err}
        }
-       return c.fd.WriteMsg(b, oob, sa)
+       return c.fd.writeMsg(b, oob, sa)
 }
 
 // DialIP connects to the remote address raddr on the network protocol
index 8b586ef7c3e5d472b3de333cb6ce874712135c64..dda857803082fae372c1297f084f7849c2e7b0a9 100644 (file)
@@ -16,7 +16,7 @@ var (
        // networking functionality.
        supportsIPv4 bool
 
-       // supportsIPv6 reports whether the platfrom supports IPv6
+       // supportsIPv6 reports whether the platform supports IPv6
        // networking functionality.
        supportsIPv6 bool
 
@@ -207,7 +207,7 @@ missingBrackets:
 }
 
 func splitHostZone(s string) (host, zone string) {
-       // The IPv6 scoped addressing zone identifer starts after the
+       // The IPv6 scoped addressing zone identifier starts after the
        // last percent sign.
        if i := last(s, '%'); i > 0 {
                host, zone = s[:i], s[i+1:]
@@ -232,7 +232,7 @@ func JoinHostPort(host, port string) string {
 // address or a DNS name and returns an internet protocol family
 // address. It returns a list that contains a pair of different
 // address family addresses when addr is a DNS name and the name has
-// mutiple address family records. The result contains at least one
+// multiple address family records. The result contains at least one
 // address when error is nil.
 func resolveInternetAddr(net, addr string, deadline time.Time) (netaddr, error) {
        var (
index 914ed50826f8c402ecb769460d16fe6996c1665a..94ceea31b0350c645f524ed626f4f42d1f46ee74 100644 (file)
@@ -60,12 +60,12 @@ func parsePlan9Addr(s string) (ip IP, iport int, err error) {
        if i >= 0 {
                addr = ParseIP(s[:i])
                if addr == nil {
-                       return nil, 0, errors.New("net: parsing IP failed")
+                       return nil, 0, errors.New("parsing IP failed")
                }
        }
        p, _, ok := dtoi(s[i+1:], 0)
        if !ok {
-               return nil, 0, errors.New("net: parsing port failed")
+               return nil, 0, errors.New("parsing port failed")
        }
        if p < 0 || p > 0xFFFF {
                return nil, 0, &AddrError{"invalid port", string(p)}
index a83e5256174fd84910063a2172c044dbfd1a8ef3..2ba4c8efd530a393e424c5bc01fc12861de57714 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.
 
-// +build darwin dragonfly freebsd linux netbsd openbsd windows
+// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows
 
 // Internet protocol family sockets for POSIX
 
@@ -40,12 +40,13 @@ func probeIPv4Stack() bool {
 func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) {
        var probes = []struct {
                laddr TCPAddr
+               value int
                ok    bool
        }{
                // IPv6 communication capability
-               {TCPAddr{IP: ParseIP("::1")}, false},
+               {laddr: TCPAddr{IP: ParseIP("::1")}, value: 1},
                // IPv6 IPv4-mapped address communication capability
-               {TCPAddr{IP: IPv4(127, 0, 0, 1)}, false},
+               {laddr: TCPAddr{IP: IPv4(127, 0, 0, 1)}, value: 0},
        }
 
        for i := range probes {
@@ -54,7 +55,7 @@ func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) {
                        continue
                }
                defer closesocket(s)
-               syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, 0)
+               syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, probes[i].value)
                sa, err := probes[i].laddr.sockaddr(syscall.AF_INET6)
                if err != nil {
                        continue
index 2ccd997c2cba1b7efb15a798b6da6f22cebce83d..b80ac10e0d9d0ef155c123e11fc26a031f200873 100644 (file)
@@ -63,7 +63,7 @@ func queryCS1(net string, ip IP, port int) (clone, dest string, err error) {
        }
        f := getFields(lines[0])
        if len(f) < 2 {
-               return "", "", errors.New("net: bad response from ndb/cs")
+               return "", "", errors.New("bad response from ndb/cs")
        }
        clone, dest = f[0], f[1]
        return
@@ -199,7 +199,7 @@ func lookupCNAME(name string) (cname string, err error) {
                        return f[2] + ".", nil
                }
        }
-       return "", errors.New("net: bad response from ndb/dns")
+       return "", errors.New("bad response from ndb/dns")
 }
 
 func lookupSRV(service, proto, name string) (cname string, addrs []*SRV, err error) {
index 59e9f63210c835e4956e589bc543991838f1efd6..b1d2f8f31a9e6eea37a9659464e970e12699e6ff 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.
 
-// +build darwin dragonfly freebsd linux netbsd openbsd
+// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
 
 package net
 
index 4b332c1b5be6e8ffd2cf95e1eaf1246f341f9ee8..ba0778caa73af448b960f71557b239f9f2debfd4 100644 (file)
@@ -363,7 +363,7 @@ func (p *addrParser) consumePhrase() (phrase string, err error) {
        // Ignore any error if we got at least one word.
        if err != nil && len(words) == 0 {
                debug.Printf("consumePhrase: hit err: %v", err)
-               return "", errors.New("mail: missing word in phrase")
+               return "", fmt.Errorf("mail: missing word in phrase: %v", err)
        }
        phrase = strings.Join(words, " ")
        return phrase, nil
@@ -442,11 +442,11 @@ func (p *addrParser) len() int {
 func decodeRFC2047Word(s string) (string, error) {
        fields := strings.Split(s, "?")
        if len(fields) != 5 || fields[0] != "=" || fields[4] != "=" {
-               return "", errors.New("mail: address not RFC 2047 encoded")
+               return "", errors.New("address not RFC 2047 encoded")
        }
        charset, enc := strings.ToLower(fields[1]), strings.ToLower(fields[2])
        if charset != "iso-8859-1" && charset != "utf-8" {
-               return "", fmt.Errorf("mail: charset not supported: %q", charset)
+               return "", fmt.Errorf("charset not supported: %q", charset)
        }
 
        in := bytes.NewBufferString(fields[3])
@@ -457,7 +457,7 @@ func decodeRFC2047Word(s string) (string, error) {
        case "q":
                r = qDecoder{r: in}
        default:
-               return "", fmt.Errorf("mail: RFC 2047 encoding not supported: %q", enc)
+               return "", fmt.Errorf("RFC 2047 encoding not supported: %q", enc)
        }
 
        dec, err := ioutil.ReadAll(r)
index 1bb4e8bc40137e6c72d9049b03d7b55c1014ea04..eb9c8cbdc9b61ccc09aabea0d7b2586c50067a4c 100644 (file)
@@ -8,6 +8,7 @@ import (
        "bytes"
        "io/ioutil"
        "reflect"
+       "strings"
        "testing"
        "time"
 )
@@ -116,6 +117,14 @@ func TestDateParsing(t *testing.T) {
        }
 }
 
+func TestAddressParsingError(t *testing.T) {
+       const txt = "=?iso-8859-2?Q?Bogl=E1rka_Tak=E1cs?= <unknown@gmail.com>"
+       _, err := ParseAddress(txt)
+       if err == nil || !strings.Contains(err.Error(), "charset not supported") {
+               t.Errorf(`mail.ParseAddress(%q) err: %q, want ".*charset not supported.*"`, txt, err)
+       }
+}
+
 func TestAddressParsing(t *testing.T) {
        tests := []struct {
                addrsStr string
index 5660fd42f8c712feea6a4c8050444b094bc2349e..63dbce88e9a5550b18e1bba1dacba3befac90fd0 100644 (file)
@@ -25,8 +25,10 @@ var ipv4MulticastListenerTests = []struct {
 // port.
 func TestIPv4MulticastListener(t *testing.T) {
        switch runtime.GOOS {
-       case "plan9":
+       case "nacl", "plan9":
                t.Skipf("skipping test on %q", runtime.GOOS)
+       case "solaris":
+               t.Skipf("skipping test on solaris, see issue 7399")
        }
 
        closer := func(cs []*UDPConn) {
@@ -93,8 +95,10 @@ var ipv6MulticastListenerTests = []struct {
 // port.
 func TestIPv6MulticastListener(t *testing.T) {
        switch runtime.GOOS {
-       case "plan9", "solaris":
+       case "plan9":
                t.Skipf("skipping test on %q", runtime.GOOS)
+       case "solaris":
+               t.Skipf("skipping test on solaris, see issue 7399")
        }
        if !supportsIPv6 {
                t.Skip("ipv6 is not supported")
index 2e6db5551430cb55e0d07da6706e6f8b1153013c..ca56af54fc6f899c4510934e7a3cb76177eecab5 100644 (file)
@@ -275,7 +275,16 @@ type Listener interface {
        Addr() Addr
 }
 
-var errMissingAddress = errors.New("missing address")
+// Various errors contained in OpError.
+var (
+       // For connection setup and write operations.
+       errMissingAddress = errors.New("missing address")
+
+       // For both read and write operations.
+       errTimeout          error = &timeoutError{}
+       errClosing                = errors.New("use of closed network connection")
+       ErrWriteToConnected       = errors.New("use of WriteTo with pre-connected connection")
+)
 
 // OpError is the error type usually returned by functions in the net
 // package. It describes the operation, network type, and address of
@@ -337,10 +346,6 @@ func (e *timeoutError) Error() string   { return "i/o timeout" }
 func (e *timeoutError) Timeout() bool   { return true }
 func (e *timeoutError) Temporary() bool { return true }
 
-var errTimeout error = &timeoutError{}
-
-var errClosing = errors.New("use of closed network connection")
-
 type AddrError struct {
        Err  string
        Addr string
index c9fb433ec91b10edf57c3bbb8c3f265307ac5a71..bfed4d657fdde064f741076c88f001d948620068 100644 (file)
@@ -28,12 +28,14 @@ func TestShutdown(t *testing.T) {
                defer ln.Close()
                c, err := ln.Accept()
                if err != nil {
-                       t.Fatalf("Accept: %v", err)
+                       t.Errorf("Accept: %v", err)
+                       return
                }
                var buf [10]byte
                n, err := c.Read(buf[:])
                if n != 0 || err != io.EOF {
-                       t.Fatalf("server Read = %d, %v; want 0, io.EOF", n, err)
+                       t.Errorf("server Read = %d, %v; want 0, io.EOF", n, err)
+                       return
                }
                c.Write([]byte("response"))
                c.Close()
@@ -62,7 +64,7 @@ func TestShutdown(t *testing.T) {
 
 func TestShutdownUnix(t *testing.T) {
        switch runtime.GOOS {
-       case "windows", "plan9":
+       case "nacl", "plan9", "windows":
                t.Skipf("skipping test on %q", runtime.GOOS)
        }
        f, err := ioutil.TempFile("", "go_net_unixtest")
@@ -84,12 +86,14 @@ func TestShutdownUnix(t *testing.T) {
        go func() {
                c, err := ln.Accept()
                if err != nil {
-                       t.Fatalf("Accept: %v", err)
+                       t.Errorf("Accept: %v", err)
+                       return
                }
                var buf [10]byte
                n, err := c.Read(buf[:])
                if n != 0 || err != io.EOF {
-                       t.Fatalf("server Read = %d, %v; want 0, io.EOF", n, err)
+                       t.Errorf("server Read = %d, %v; want 0, io.EOF", n, err)
+                       return
                }
                c.Write([]byte("response"))
                c.Close()
@@ -196,7 +200,8 @@ func TestTCPClose(t *testing.T) {
        go func() {
                c, err := Dial("tcp", l.Addr().String())
                if err != nil {
-                       t.Fatal(err)
+                       t.Errorf("Dial: %v", err)
+                       return
                }
 
                go read(c)
index 945003f67ad93dd745bb15276a461734f8d3ae7e..b6e4e76f93045a9d70847c1d551d53d89193491b 100644 (file)
@@ -15,12 +15,6 @@ import (
        "time"
 )
 
-func strfunc(s string) func() string {
-       return func() string {
-               return s
-       }
-}
-
 func packetConnTestData(t *testing.T, net string, i int) ([]byte, func()) {
        switch net {
        case "udp":
@@ -46,7 +40,7 @@ func packetConnTestData(t *testing.T, net string, i int) ([]byte, func()) {
                return b, nil
        case "unixgram":
                switch runtime.GOOS {
-               case "plan9", "windows":
+               case "nacl", "plan9", "windows":
                        return nil, func() {
                                t.Logf("skipping %q test on %q", net, runtime.GOOS)
                        }
@@ -62,12 +56,12 @@ func packetConnTestData(t *testing.T, net string, i int) ([]byte, func()) {
 
 var packetConnTests = []struct {
        net   string
-       addr1 func() string
-       addr2 func() string
+       addr1 string
+       addr2 string
 }{
-       {"udp", strfunc("127.0.0.1:0"), strfunc("127.0.0.1:0")},
-       {"ip:icmp", strfunc("127.0.0.1"), strfunc("127.0.0.1")},
-       {"unixgram", testUnixAddr, testUnixAddr},
+       {"udp", "127.0.0.1:0", "127.0.0.1:0"},
+       {"ip:icmp", "127.0.0.1", "127.0.0.1"},
+       {"unixgram", testUnixAddr(), testUnixAddr()},
 }
 
 func TestPacketConn(t *testing.T) {
@@ -88,22 +82,21 @@ func TestPacketConn(t *testing.T) {
                        continue
                }
 
-               addr1, addr2 := tt.addr1(), tt.addr2()
-               c1, err := ListenPacket(tt.net, addr1)
+               c1, err := ListenPacket(tt.net, tt.addr1)
                if err != nil {
                        t.Fatalf("ListenPacket failed: %v", err)
                }
-               defer closer(c1, netstr[0], addr1, addr2)
+               defer closer(c1, netstr[0], tt.addr1, tt.addr2)
                c1.LocalAddr()
                c1.SetDeadline(time.Now().Add(100 * time.Millisecond))
                c1.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
                c1.SetWriteDeadline(time.Now().Add(100 * time.Millisecond))
 
-               c2, err := ListenPacket(tt.net, addr2)
+               c2, err := ListenPacket(tt.net, tt.addr2)
                if err != nil {
                        t.Fatalf("ListenPacket failed: %v", err)
                }
-               defer closer(c2, netstr[0], addr1, addr2)
+               defer closer(c2, netstr[0], tt.addr1, tt.addr2)
                c2.LocalAddr()
                c2.SetDeadline(time.Now().Add(100 * time.Millisecond))
                c2.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
@@ -145,12 +138,11 @@ func TestConnAndPacketConn(t *testing.T) {
                        continue
                }
 
-               addr1, addr2 := tt.addr1(), tt.addr2()
-               c1, err := ListenPacket(tt.net, addr1)
+               c1, err := ListenPacket(tt.net, tt.addr1)
                if err != nil {
                        t.Fatalf("ListenPacket failed: %v", err)
                }
-               defer closer(c1, netstr[0], addr1, addr2)
+               defer closer(c1, netstr[0], tt.addr1, tt.addr2)
                c1.LocalAddr()
                c1.SetDeadline(time.Now().Add(100 * time.Millisecond))
                c1.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
index 3cd9ca2aa711122f23c1ff7dea96867c5a1bdc60..89558c1f029bc7221f6f54dad192a00e7c495ee4 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.
 
-// +build darwin dragonfly freebsd linux netbsd openbsd
+// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
 
 // Read system port mappings from /etc/services
 
@@ -10,12 +10,16 @@ package net
 
 import "sync"
 
-var services map[string]map[string]int
+// services contains minimal mappings between services names and port
+// numbers for platforms that don't have a complete list of port numbers
+// (some Solaris distros).
+var services = map[string]map[string]int{
+       "tcp": {"http": 80},
+}
 var servicesError error
 var onceReadServices sync.Once
 
 func readServices() {
-       services = make(map[string]map[string]int)
        var file *file
        if file, servicesError = open("/etc/services"); servicesError != nil {
                return
@@ -29,7 +33,7 @@ func readServices() {
                if len(f) < 2 {
                        continue
                }
-               portnet := f[1] // "tcp/80"
+               portnet := f[1] // "80/tcp"
                port, j, ok := dtoi(portnet, 0)
                if !ok || port <= 0 || j >= len(portnet) || portnet[j] != '/' {
                        continue
index 5a8958b086646545e356b03e5f81002fc33ac3ca..12856b6c311cf038a6988be5c5e8ea38787efd96 100644 (file)
@@ -19,7 +19,7 @@ import (
 // also uses /tmp directory in case it is prohibited to create UNIX
 // sockets in TMPDIR.
 func testUnixAddr() string {
-       f, err := ioutil.TempFile("/tmp", "nettest")
+       f, err := ioutil.TempFile("", "nettest")
        if err != nil {
                panic(err)
        }
@@ -236,7 +236,7 @@ func TestIPConnSpecificMethods(t *testing.T) {
 
 func TestUnixListenerSpecificMethods(t *testing.T) {
        switch runtime.GOOS {
-       case "plan9", "windows":
+       case "nacl", "plan9", "windows":
                t.Skipf("skipping test on %q", runtime.GOOS)
        }
 
@@ -278,7 +278,7 @@ func TestUnixListenerSpecificMethods(t *testing.T) {
 
 func TestUnixConnSpecificMethods(t *testing.T) {
        switch runtime.GOOS {
-       case "plan9", "windows":
+       case "nacl", "plan9", "windows":
                t.Skipf("skipping test on %q", runtime.GOOS)
        }
 
index c524d0a0a2de37204d255e02f35aee369093009e..21f79b06844638e485f055b3a24c0407be310c64 100644 (file)
@@ -39,14 +39,16 @@ type Call struct {
 // with a single Client, and a Client may be used by
 // multiple goroutines simultaneously.
 type Client struct {
-       mutex    sync.Mutex // protects pending, seq, request
-       sending  sync.Mutex
+       codec ClientCodec
+
+       sending sync.Mutex
+
+       mutex    sync.Mutex // protects following
        request  Request
        seq      uint64
-       codec    ClientCodec
        pending  map[uint64]*Call
-       closing  bool
-       shutdown bool
+       closing  bool // user has called Close
+       shutdown bool // server has told us to stop
 }
 
 // A ClientCodec implements writing of RPC requests and
@@ -274,7 +276,7 @@ func Dial(network, address string) (*Client, error) {
 
 func (client *Client) Close() error {
        client.mutex.Lock()
-       if client.shutdown || client.closing {
+       if client.closing {
                client.mutex.Unlock()
                return ErrShutdown
        }
diff --git a/libgo/go/net/rpc/client_test.go b/libgo/go/net/rpc/client_test.go
new file mode 100644 (file)
index 0000000..bbfc1ec
--- /dev/null
@@ -0,0 +1,36 @@
+// Copyright 2014 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 rpc
+
+import (
+       "errors"
+       "testing"
+)
+
+type shutdownCodec struct {
+       responded chan int
+       closed    bool
+}
+
+func (c *shutdownCodec) WriteRequest(*Request, interface{}) error { return nil }
+func (c *shutdownCodec) ReadResponseBody(interface{}) error       { return nil }
+func (c *shutdownCodec) ReadResponseHeader(*Response) error {
+       c.responded <- 1
+       return errors.New("shutdownCodec ReadResponseHeader")
+}
+func (c *shutdownCodec) Close() error {
+       c.closed = true
+       return nil
+}
+
+func TestCloseCodec(t *testing.T) {
+       codec := &shutdownCodec{responded: make(chan int)}
+       client := NewClientWithCodec(codec)
+       <-codec.responded
+       client.Close()
+       if !codec.closed {
+               t.Error("client.Close did not close codec")
+       }
+}
index 40d4b82d7f2b4e4a38af1d0ba99364a7b4739161..a433a365e88849be9a6d1f41575ab26fddfc0635 100644 (file)
@@ -5,6 +5,7 @@
 package jsonrpc
 
 import (
+       "bytes"
        "encoding/json"
        "errors"
        "fmt"
@@ -12,6 +13,7 @@ import (
        "io/ioutil"
        "net"
        "net/rpc"
+       "strings"
        "testing"
 )
 
@@ -202,6 +204,39 @@ func TestMalformedOutput(t *testing.T) {
        }
 }
 
+func TestServerErrorHasNullResult(t *testing.T) {
+       var out bytes.Buffer
+       sc := NewServerCodec(struct {
+               io.Reader
+               io.Writer
+               io.Closer
+       }{
+               Reader: strings.NewReader(`{"method": "Arith.Add", "id": "123", "params": []}`),
+               Writer: &out,
+               Closer: ioutil.NopCloser(nil),
+       })
+       r := new(rpc.Request)
+       if err := sc.ReadRequestHeader(r); err != nil {
+               t.Fatal(err)
+       }
+       const valueText = "the value we don't want to see"
+       const errorText = "some error"
+       err := sc.WriteResponse(&rpc.Response{
+               ServiceMethod: "Method",
+               Seq:           1,
+               Error:         errorText,
+       }, valueText)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if !strings.Contains(out.String(), errorText) {
+               t.Fatalf("Response didn't contain expected error %q: %s", errorText, &out)
+       }
+       if strings.Contains(out.String(), valueText) {
+               t.Errorf("Response contains both an error and value: %s", &out)
+       }
+}
+
 func TestUnexpectedError(t *testing.T) {
        cli, srv := myPipe()
        go cli.PipeWriter.CloseWithError(errors.New("unexpected error!")) // reader will get this error
index 16ec0fe9ad5a05402935ee164085e60a1a0540ce..e6d37cfa64f3d72e4c51d4bf4b28ccee15430c27 100644 (file)
@@ -100,7 +100,6 @@ func (c *serverCodec) ReadRequestBody(x interface{}) error {
 var null = json.RawMessage([]byte("null"))
 
 func (c *serverCodec) WriteResponse(r *rpc.Response, x interface{}) error {
-       var resp serverResponse
        c.mutex.Lock()
        b, ok := c.pending[r.Seq]
        if !ok {
@@ -114,10 +113,9 @@ func (c *serverCodec) WriteResponse(r *rpc.Response, x interface{}) error {
                // Invalid request so no id.  Use JSON null.
                b = &null
        }
-       resp.Id = b
-       resp.Result = x
+       resp := serverResponse{Id: b}
        if r.Error == "" {
-               resp.Error = nil
+               resp.Result = x
        } else {
                resp.Error = r.Error
        }
index 3b9a88380cfa25f1d032e771c04464d9dcb0cc73..0dc4ddc2de01cb0e130b0f569e13f16e1c21646f 100644 (file)
@@ -594,7 +594,6 @@ func TestErrorAfterClientClose(t *testing.T) {
 }
 
 func benchmarkEndToEnd(dial func() (*Client, error), b *testing.B) {
-       b.StopTimer()
        once.Do(startServer)
        client, err := dial()
        if err != nil {
@@ -604,33 +603,24 @@ func benchmarkEndToEnd(dial func() (*Client, error), b *testing.B) {
 
        // Synchronous calls
        args := &Args{7, 8}
-       procs := runtime.GOMAXPROCS(-1)
-       N := int32(b.N)
-       var wg sync.WaitGroup
-       wg.Add(procs)
-       b.StartTimer()
-
-       for p := 0; p < procs; p++ {
-               go func() {
-                       reply := new(Reply)
-                       for atomic.AddInt32(&N, -1) >= 0 {
-                               err := client.Call("Arith.Add", args, reply)
-                               if err != nil {
-                                       b.Fatalf("rpc error: Add: expected no error but got string %q", err.Error())
-                               }
-                               if reply.C != args.A+args.B {
-                                       b.Fatalf("rpc error: Add: expected %d got %d", reply.C, args.A+args.B)
-                               }
+       b.ResetTimer()
+
+       b.RunParallel(func(pb *testing.PB) {
+               reply := new(Reply)
+               for pb.Next() {
+                       err := client.Call("Arith.Add", args, reply)
+                       if err != nil {
+                               b.Fatalf("rpc error: Add: expected no error but got string %q", err.Error())
                        }
-                       wg.Done()
-               }()
-       }
-       wg.Wait()
+                       if reply.C != args.A+args.B {
+                               b.Fatalf("rpc error: Add: expected %d got %d", reply.C, args.A+args.B)
+                       }
+               }
+       })
 }
 
 func benchmarkEndToEndAsync(dial func() (*Client, error), b *testing.B) {
        const MaxConcurrentCalls = 100
-       b.StopTimer()
        once.Do(startServer)
        client, err := dial()
        if err != nil {
@@ -647,7 +637,7 @@ func benchmarkEndToEndAsync(dial func() (*Client, error), b *testing.B) {
        wg.Add(procs)
        gate := make(chan bool, MaxConcurrentCalls)
        res := make(chan *Call, MaxConcurrentCalls)
-       b.StartTimer()
+       b.ResetTimer()
 
        for p := 0; p < procs; p++ {
                go func() {
index a2219c163374f4eebc1fd30b971f12b287f33191..bc88fd3b907aa7fb7791f166ac065ce17ff6e637 100644 (file)
@@ -23,7 +23,7 @@ const maxSendfileSize int = 4 << 20
 // if handled == false, sendFile performed no work.
 func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) {
        // DragonFly uses 0 as the "until EOF" value. If you pass in more bytes than the
-       // file contains, it will loop back to the beginning ad nauseum until it's sent
+       // file contains, it will loop back to the beginning ad nauseam until it's sent
        // exactly the number of bytes told to. As such, we need to know exactly how many
        // bytes to send.
        var remain int64 = 0
index 42fe799efbd0e68e5cc54fd50ee480bba652cb03..ffc147262a8def15b616f2b57e8a31ab73c31bc1 100644 (file)
@@ -23,7 +23,7 @@ const maxSendfileSize int = 4 << 20
 // if handled == false, sendFile performed no work.
 func sendFile(c *netFD, r io.Reader) (written int64, err error, handled bool) {
        // FreeBSD uses 0 as the "until EOF" value. If you pass in more bytes than the
-       // file contains, it will loop back to the beginning ad nauseum until it's sent
+       // file contains, it will loop back to the beginning ad nauseam until it's sent
        // exactly the number of bytes told to. As such, we need to know exactly how many
        // bytes to send.
        var remain int64 = 0
index 3660849c182c5cc4ee93a86eee699ed9955182f6..03426ef0df17897e9966d95b7c44b5cd08e3759d 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.
 
-// +build darwin netbsd openbsd
+// +build darwin nacl netbsd openbsd solaris
 
 package net
 
index 9194a8ec24dd077fcd7b7defa15e78970528f594..6a2bb924329d37fd65653cb01bd7b9ef917e0502 100644 (file)
@@ -9,21 +9,20 @@ import (
        "io"
        "os"
        "runtime"
-       "strconv"
        "testing"
        "time"
 )
 
-func skipServerTest(net, unixsotype, addr string, ipv6, ipv4map, linuxonly bool) bool {
+func skipServerTest(net, unixsotype, addr string, ipv6, ipv4map, linuxOnly bool) bool {
        switch runtime.GOOS {
        case "linux":
-       case "plan9", "windows":
+       case "nacl", "plan9", "windows":
                // "unix" sockets are not supported on Windows and Plan 9.
                if net == unixsotype {
                        return true
                }
        default:
-               if net == unixsotype && linuxonly {
+               if net == unixsotype && linuxOnly {
                        return true
                }
        }
@@ -42,21 +41,15 @@ func skipServerTest(net, unixsotype, addr string, ipv6, ipv4map, linuxonly bool)
        return false
 }
 
-func tempfile(filename string) string {
-       // use /tmp in case it is prohibited to create
-       // UNIX sockets in TMPDIR
-       return "/tmp/" + filename + "." + strconv.Itoa(os.Getpid())
-}
-
 var streamConnServerTests = []struct {
-       snet    string // server side
-       saddr   string
-       cnet    string // client side
-       caddr   string
-       ipv6    bool // test with underlying AF_INET6 socket
-       ipv4map bool // test with IPv6 IPv4-mapping functionality
-       empty   bool // test with empty data
-       linux   bool // test with abstract unix domain socket, a Linux-ism
+       snet      string // server side
+       saddr     string
+       cnet      string // client side
+       caddr     string
+       ipv6      bool // test with underlying AF_INET6 socket
+       ipv4map   bool // test with IPv6 IPv4-mapping functionality
+       empty     bool // test with empty data
+       linuxOnly bool // test with abstract unix domain socket, a Linux-ism
 }{
        {snet: "tcp", saddr: "", cnet: "tcp", caddr: "127.0.0.1"},
        {snet: "tcp", saddr: "0.0.0.0", cnet: "tcp", caddr: "127.0.0.1"},
@@ -93,13 +86,13 @@ var streamConnServerTests = []struct {
 
        {snet: "tcp6", saddr: "[::1]", cnet: "tcp6", caddr: "[::1]", ipv6: true},
 
-       {snet: "unix", saddr: tempfile("gotest1.net"), cnet: "unix", caddr: tempfile("gotest1.net.local")},
-       {snet: "unix", saddr: "@gotest2/net", cnet: "unix", caddr: "@gotest2/net.local", linux: true},
+       {snet: "unix", saddr: testUnixAddr(), cnet: "unix", caddr: testUnixAddr()},
+       {snet: "unix", saddr: "@gotest2/net", cnet: "unix", caddr: "@gotest2/net.local", linuxOnly: true},
 }
 
 func TestStreamConnServer(t *testing.T) {
        for _, tt := range streamConnServerTests {
-               if skipServerTest(tt.snet, "unix", tt.saddr, tt.ipv6, tt.ipv4map, tt.linux) {
+               if skipServerTest(tt.snet, "unix", tt.saddr, tt.ipv6, tt.ipv4map, tt.linuxOnly) {
                        continue
                }
 
@@ -137,21 +130,28 @@ func TestStreamConnServer(t *testing.T) {
 }
 
 var seqpacketConnServerTests = []struct {
-       net   string
-       saddr string // server address
-       caddr string // client address
-       empty bool   // test with empty data
+       net       string
+       saddr     string // server address
+       caddr     string // client address
+       empty     bool   // test with empty data
+       linuxOnly bool   // test with abstract unix domain socket, a Linux-ism
 }{
-       {net: "unixpacket", saddr: tempfile("/gotest3.net"), caddr: tempfile("gotest3.net.local")},
-       {net: "unixpacket", saddr: "@gotest4/net", caddr: "@gotest4/net.local"},
+       {net: "unixpacket", saddr: testUnixAddr(), caddr: testUnixAddr()},
+       {net: "unixpacket", saddr: "@gotest4/net", caddr: "@gotest4/net.local", linuxOnly: true},
 }
 
 func TestSeqpacketConnServer(t *testing.T) {
-       if runtime.GOOS != "linux" {
+       switch runtime.GOOS {
+       case "darwin", "nacl", "openbsd", "plan9", "windows":
+               fallthrough
+       case "freebsd": // FreeBSD 8 doesn't support unixpacket
                t.Skipf("skipping test on %q", runtime.GOOS)
        }
 
        for _, tt := range seqpacketConnServerTests {
+               if runtime.GOOS != "linux" && tt.linuxOnly {
+                       continue
+               }
                listening := make(chan string)
                done := make(chan int)
                switch tt.net {
@@ -248,15 +248,15 @@ func runStreamConnClient(t *testing.T, net, taddr string, isEmpty bool) {
 var testDatagram = flag.Bool("datagram", false, "whether to test udp and unixgram")
 
 var datagramPacketConnServerTests = []struct {
-       snet    string // server side
-       saddr   string
-       cnet    string // client side
-       caddr   string
-       ipv6    bool // test with underlying AF_INET6 socket
-       ipv4map bool // test with IPv6 IPv4-mapping functionality
-       dial    bool // test with Dial or DialUnix
-       empty   bool // test with empty data
-       linux   bool // test with abstract unix domain socket, a Linux-ism
+       snet      string // server side
+       saddr     string
+       cnet      string // client side
+       caddr     string
+       ipv6      bool // test with underlying AF_INET6 socket
+       ipv4map   bool // test with IPv6 IPv4-mapping functionality
+       dial      bool // test with Dial or DialUnix
+       empty     bool // test with empty data
+       linuxOnly bool // test with abstract unix domain socket, a Linux-ism
 }{
        {snet: "udp", saddr: "", cnet: "udp", caddr: "127.0.0.1"},
        {snet: "udp", saddr: "0.0.0.0", cnet: "udp", caddr: "127.0.0.1"},
@@ -301,12 +301,12 @@ var datagramPacketConnServerTests = []struct {
        {snet: "udp", saddr: "[::1]", cnet: "udp", caddr: "[::1]", ipv6: true, empty: true},
        {snet: "udp", saddr: "[::1]", cnet: "udp", caddr: "[::1]", ipv6: true, dial: true, empty: true},
 
-       {snet: "unixgram", saddr: tempfile("gotest5.net"), cnet: "unixgram", caddr: tempfile("gotest5.net.local")},
-       {snet: "unixgram", saddr: tempfile("gotest5.net"), cnet: "unixgram", caddr: tempfile("gotest5.net.local"), dial: true},
-       {snet: "unixgram", saddr: tempfile("gotest5.net"), cnet: "unixgram", caddr: tempfile("gotest5.net.local"), empty: true},
-       {snet: "unixgram", saddr: tempfile("gotest5.net"), cnet: "unixgram", caddr: tempfile("gotest5.net.local"), dial: true, empty: true},
+       {snet: "unixgram", saddr: testUnixAddr(), cnet: "unixgram", caddr: testUnixAddr()},
+       {snet: "unixgram", saddr: testUnixAddr(), cnet: "unixgram", caddr: testUnixAddr(), dial: true},
+       {snet: "unixgram", saddr: testUnixAddr(), cnet: "unixgram", caddr: testUnixAddr(), empty: true},
+       {snet: "unixgram", saddr: testUnixAddr(), cnet: "unixgram", caddr: testUnixAddr(), dial: true, empty: true},
 
-       {snet: "unixgram", saddr: "@gotest6/net", cnet: "unixgram", caddr: "@gotest6/net.local", linux: true},
+       {snet: "unixgram", saddr: "@gotest6/net", cnet: "unixgram", caddr: "@gotest6/net.local", linuxOnly: true},
 }
 
 func TestDatagramPacketConnServer(t *testing.T) {
@@ -315,7 +315,7 @@ func TestDatagramPacketConnServer(t *testing.T) {
        }
 
        for _, tt := range datagramPacketConnServerTests {
-               if skipServerTest(tt.snet, "unixgram", tt.saddr, tt.ipv6, tt.ipv4map, tt.linux) {
+               if skipServerTest(tt.snet, "unixgram", tt.saddr, tt.ipv6, tt.ipv4map, tt.linuxOnly) {
                        continue
                }
 
index a0a478a85246460d5240bde853455bc7a667746b..87dea442c46e1f7ae841290074aaa64f4e721874 100644 (file)
@@ -264,6 +264,8 @@ func (c *Client) Data() (io.WriteCloser, error) {
        return &dataCloser{c, c.Text.DotWriter()}, nil
 }
 
+var testHookStartTLS func(*tls.Config) // nil, except for tests
+
 // SendMail connects to the server at addr, switches to TLS if
 // possible, authenticates with the optional mechanism a if possible,
 // and then sends an email from address from, to addresses to, with
@@ -278,7 +280,11 @@ func SendMail(addr string, a Auth, from string, to []string, msg []byte) error {
                return err
        }
        if ok, _ := c.Extension("STARTTLS"); ok {
-               if err = c.StartTLS(nil); err != nil {
+               config := &tls.Config{ServerName: c.serverName}
+               if testHookStartTLS != nil {
+                       testHookStartTLS(config)
+               }
+               if err = c.StartTLS(config); err != nil {
                        return err
                }
        }
index 2133dc7c7baeccdce64274854bd91583487dcaa8..3fba1ea5ae35ebf80f97f1354b7712ae09e7316f 100644 (file)
@@ -7,6 +7,8 @@ package smtp
 import (
        "bufio"
        "bytes"
+       "crypto/tls"
+       "crypto/x509"
        "io"
        "net"
        "net/textproto"
@@ -548,3 +550,145 @@ AUTH PLAIN AHVzZXIAcGFzcw==
 *
 QUIT
 `
+
+func TestTLSClient(t *testing.T) {
+       ln := newLocalListener(t)
+       defer ln.Close()
+       errc := make(chan error)
+       go func() {
+               errc <- sendMail(ln.Addr().String())
+       }()
+       conn, err := ln.Accept()
+       if err != nil {
+               t.Fatalf("failed to accept connection: %v", err)
+       }
+       defer conn.Close()
+       if err := serverHandle(conn, t); err != nil {
+               t.Fatalf("failed to handle connection: %v", err)
+       }
+       if err := <-errc; err != nil {
+               t.Fatalf("client error: %v", err)
+       }
+}
+
+func newLocalListener(t *testing.T) net.Listener {
+       ln, err := net.Listen("tcp", "127.0.0.1:0")
+       if err != nil {
+               ln, err = net.Listen("tcp6", "[::1]:0")
+       }
+       if err != nil {
+               t.Fatal(err)
+       }
+       return ln
+}
+
+type smtpSender struct {
+       w io.Writer
+}
+
+func (s smtpSender) send(f string) {
+       s.w.Write([]byte(f + "\r\n"))
+}
+
+// smtp server, finely tailored to deal with our own client only!
+func serverHandle(c net.Conn, t *testing.T) error {
+       send := smtpSender{c}.send
+       send("220 127.0.0.1 ESMTP service ready")
+       s := bufio.NewScanner(c)
+       for s.Scan() {
+               switch s.Text() {
+               case "EHLO localhost":
+                       send("250-127.0.0.1 ESMTP offers a warm hug of welcome")
+                       send("250-STARTTLS")
+                       send("250 Ok")
+               case "STARTTLS":
+                       send("220 Go ahead")
+                       keypair, err := tls.X509KeyPair(localhostCert, localhostKey)
+                       if err != nil {
+                               return err
+                       }
+                       config := &tls.Config{Certificates: []tls.Certificate{keypair}}
+                       c = tls.Server(c, config)
+                       defer c.Close()
+                       return serverHandleTLS(c, t)
+               default:
+                       t.Fatalf("unrecognized command: %q", s.Text())
+               }
+       }
+       return s.Err()
+}
+
+func serverHandleTLS(c net.Conn, t *testing.T) error {
+       send := smtpSender{c}.send
+       s := bufio.NewScanner(c)
+       for s.Scan() {
+               switch s.Text() {
+               case "EHLO localhost":
+                       send("250 Ok")
+               case "MAIL FROM:<joe1@example.com>":
+                       send("250 Ok")
+               case "RCPT TO:<joe2@example.com>":
+                       send("250 Ok")
+               case "DATA":
+                       send("354 send the mail data, end with .")
+                       send("250 Ok")
+               case "Subject: test":
+               case "":
+               case "howdy!":
+               case ".":
+               case "QUIT":
+                       send("221 127.0.0.1 Service closing transmission channel")
+                       return nil
+               default:
+                       t.Fatalf("unrecognized command during TLS: %q", s.Text())
+               }
+       }
+       return s.Err()
+}
+
+func init() {
+       testRootCAs := x509.NewCertPool()
+       testRootCAs.AppendCertsFromPEM(localhostCert)
+       testHookStartTLS = func(config *tls.Config) {
+               config.RootCAs = testRootCAs
+       }
+}
+
+func sendMail(hostPort string) error {
+       host, _, err := net.SplitHostPort(hostPort)
+       if err != nil {
+               return err
+       }
+       auth := PlainAuth("", "", "", host)
+       from := "joe1@example.com"
+       to := []string{"joe2@example.com"}
+       return SendMail(hostPort, auth, from, to, []byte("Subject: test\n\nhowdy!"))
+}
+
+// (copied from net/http/httptest)
+// localhostCert is a PEM-encoded TLS cert with SAN IPs
+// "127.0.0.1" and "[::1]", expiring at the last second of 2049 (the end
+// of ASN.1 time).
+// generated from src/pkg/crypto/tls:
+// go run generate_cert.go  --rsa-bits 512 --host 127.0.0.1,::1,example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h
+var localhostCert = []byte(`-----BEGIN CERTIFICATE-----
+MIIBdzCCASOgAwIBAgIBADALBgkqhkiG9w0BAQUwEjEQMA4GA1UEChMHQWNtZSBD
+bzAeFw03MDAxMDEwMDAwMDBaFw00OTEyMzEyMzU5NTlaMBIxEDAOBgNVBAoTB0Fj
+bWUgQ28wWjALBgkqhkiG9w0BAQEDSwAwSAJBAN55NcYKZeInyTuhcCwFMhDHCmwa
+IUSdtXdcbItRB/yfXGBhiex00IaLXQnSU+QZPRZWYqeTEbFSgihqi1PUDy8CAwEA
+AaNoMGYwDgYDVR0PAQH/BAQDAgCkMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA8GA1Ud
+EwEB/wQFMAMBAf8wLgYDVR0RBCcwJYILZXhhbXBsZS5jb22HBH8AAAGHEAAAAAAA
+AAAAAAAAAAAAAAEwCwYJKoZIhvcNAQEFA0EAAoQn/ytgqpiLcZu9XKbCJsJcvkgk
+Se6AbGXgSlq+ZCEVo0qIwSgeBqmsJxUu7NCSOwVJLYNEBO2DtIxoYVk+MA==
+-----END CERTIFICATE-----`)
+
+// localhostKey is the private key for localhostCert.
+var localhostKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
+MIIBPAIBAAJBAN55NcYKZeInyTuhcCwFMhDHCmwaIUSdtXdcbItRB/yfXGBhiex0
+0IaLXQnSU+QZPRZWYqeTEbFSgihqi1PUDy8CAwEAAQJBAQdUx66rfh8sYsgfdcvV
+NoafYpnEcB5s4m/vSVe6SU7dCK6eYec9f9wpT353ljhDUHq3EbmE4foNzJngh35d
+AekCIQDhRQG5Li0Wj8TM4obOnnXUXf1jRv0UkzE9AHWLG5q3AwIhAPzSjpYUDjVW
+MCUXgckTpKCuGwbJk7424Nb8bLzf3kllAiA5mUBgjfr/WtFSJdWcPQ4Zt9KTMNKD
+EUO0ukpTwEIl6wIhAMbGqZK3zAAFdq8DD2jPx+UJXnh0rnOkZBzDtJ6/iN69AiEA
+1Aq8MJgTaYsDQWyU/hDq5YkDJc9e9DSCvUIzqxQWMQE=
+-----END RSA PRIVATE KEY-----`)
index 6c37109f5e4b02d84924719372e957bfb357bb13..48fb7852757c23687f3445f96ce46248bb7a1eb7 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.
 
-// +build darwin dragonfly freebsd netbsd openbsd
+// +build darwin dragonfly freebsd nacl netbsd openbsd
 
 package net
 
index 3f22cd8f570480c26c8b85d8af932e8b5d30e1ea..dec81855b689bf6678640e482d55d2cec2de58ac 100644 (file)
@@ -5,7 +5,7 @@
 // This file implements sysSocket and accept for platforms that
 // provide a fast path for setting SetNonblock and CloseOnExec.
 
-// +build linux
+// +build freebsd linux
 
 package net
 
@@ -13,18 +13,20 @@ import "syscall"
 
 // Wrapper around the socket system call that marks the returned file
 // descriptor as nonblocking and close-on-exec.
-func sysSocket(f, t, p int) (int, error) {
-       s, err := syscall.Socket(f, t|syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC, p)
-       // The SOCK_NONBLOCK and SOCK_CLOEXEC flags were introduced in
-       // Linux 2.6.27.  If we get an EINVAL error, fall back to
-       // using socket without them.
-       if err == nil || err != syscall.EINVAL {
+func sysSocket(family, sotype, proto int) (int, error) {
+       s, err := syscall.Socket(family, sotype|syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC, proto)
+       // On Linux the SOCK_NONBLOCK and SOCK_CLOEXEC flags were
+       // introduced in 2.6.27 kernel and on FreeBSD both flags were
+       // introduced in 10 kernel. If we get an EINVAL error on Linux
+       // or EPROTONOSUPPORT error on FreeBSD, fall back to using
+       // socket without them.
+       if err == nil || (err != syscall.EPROTONOSUPPORT && err != syscall.EINVAL) {
                return s, err
        }
 
        // See ../syscall/exec_unix.go for description of ForkLock.
        syscall.ForkLock.RLock()
-       s, err = syscall.Socket(f, t, p)
+       s, err = syscall.Socket(family, sotype, proto)
        if err == nil {
                syscall.CloseOnExec(s)
        }
@@ -41,12 +43,19 @@ func sysSocket(f, t, p int) (int, error) {
 
 // Wrapper around the accept system call that marks the returned file
 // descriptor as nonblocking and close-on-exec.
-func accept(fd int) (int, syscall.Sockaddr, error) {
-       nfd, sa, err := syscall.Accept4(fd, syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC)
-       // The accept4 system call was introduced in Linux 2.6.28.  If
-       // we get an ENOSYS or EINVAL error, fall back to using accept.
-       if err == nil || (err != syscall.ENOSYS && err != syscall.EINVAL) {
-               return nfd, sa, err
+func accept(s int) (int, syscall.Sockaddr, error) {
+       ns, sa, err := syscall.Accept4(s, syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC)
+       // On Linux the accept4 system call was introduced in 2.6.28
+       // kernel and on FreeBSD it was introduced in 10 kernel. If we
+       // get an ENOSYS error on both Linux and FreeBSD, or EINVAL
+       // error on Linux, fall back to using accept.
+       switch err {
+       default: // nil and errors other than the ones listed
+               return ns, sa, err
+       case syscall.ENOSYS: // syscall missing
+       case syscall.EINVAL: // some Linux use this instead of ENOSYS
+       case syscall.EACCES: // some Linux use this instead of ENOSYS
+       case syscall.EFAULT: // some Linux use this instead of ENOSYS
        }
 
        // See ../syscall/exec_unix.go for description of ForkLock.
@@ -54,16 +63,16 @@ func accept(fd int) (int, syscall.Sockaddr, error) {
        // because we have put fd.sysfd into non-blocking mode.
        // However, a call to the File method will put it back into
        // blocking mode. We can't take that risk, so no use of ForkLock here.
-       nfd, sa, err = syscall.Accept(fd)
+       ns, sa, err = syscall.Accept(s)
        if err == nil {
-               syscall.CloseOnExec(nfd)
+               syscall.CloseOnExec(ns)
        }
        if err != nil {
                return -1, nil, err
        }
-       if err = syscall.SetNonblock(nfd, true); err != nil {
-               syscall.Close(nfd)
+       if err = syscall.SetNonblock(ns, true); err != nil {
+               syscall.Close(ns)
                return -1, nil, err
        }
-       return nfd, sa, nil
+       return ns, sa, nil
 }
index c2d343c5858076c5653c5b48b875b7ae0ca7adaa..a6ef874c9fda0b3304dd503f2c1b836d2b5e200f 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.
 
-// +build darwin dragonfly freebsd linux netbsd openbsd windows
+// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows
 
 package net
 
index 484e1fe461ae6407565122640e15f69fb1404256..90fe9de894cd0522937d65a4dd1cc561bcce6c2d 100644 (file)
@@ -1,18 +1,13 @@
-// Copyright 2012 The Go Authors.  All rights reserved.
+// Copyright 2009 The Go Authors.  All rights reserved.
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build solaris
-
-// Sockets for Solaris
-
 package net
 
-import (
-       "syscall"
-)
+import "syscall"
 
 func maxListenerBacklog() int {
-       // The kernel does not track the limit.
+       // TODO: Implement this
+       // NOTE: Never return a number bigger than 1<<16 - 1. See issue 5030.
        return syscall.SOMAXCONN
 }
index 4b9c2f9afbe6f96e78d2497a7c126d4d1feab3ad..2fa3b6f1d36f7d21e318acea8302d5fc9f994f2a 100644 (file)
@@ -2,16 +2,29 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// +build darwin dragonfly freebsd netbsd openbsd
+// +build darwin dragonfly freebsd nacl netbsd openbsd
 
 package net
 
 import (
        "os"
+       "runtime"
        "syscall"
 )
 
 func setDefaultSockopts(s, family, sotype int, ipv6only bool) error {
+       if runtime.GOOS == "dragonfly" && sotype != syscall.SOCK_RAW {
+               // On DragonFly BSD, we adjust the ephemeral port
+               // range because unlike other BSD systems its default
+               // port range doesn't conform to IANA recommendation
+               // as described in RFC 6355 and is pretty narrow.
+               switch family {
+               case syscall.AF_INET:
+                       syscall.SetsockoptInt(s, syscall.IPPROTO_IP, syscall.IP_PORTRANGE, syscall.IP_PORTRANGE_HIGH)
+               case syscall.AF_INET6:
+                       syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_PORTRANGE, syscall.IPV6_PORTRANGE_HIGH)
+               }
+       }
        if family == syscall.AF_INET6 && sotype != syscall.SOCK_RAW {
                // Allow both IP versions even if the OS default
                // is otherwise.  Note that some operating systems
index ff3bc6899401d3786dcce3b7a27165e9138caad7..921918c37f543b42dbb17569d82c3e2b2fcc45b5 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.
 
-// +build darwin dragonfly freebsd linux netbsd openbsd windows
+// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows
 
 package net
 
diff --git a/libgo/go/net/sockopt_solaris.go b/libgo/go/net/sockopt_solaris.go
new file mode 100644 (file)
index 0000000..54c20b1
--- /dev/null
@@ -0,0 +1,32 @@
+// 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 net
+
+import (
+       "os"
+       "syscall"
+)
+
+func setDefaultSockopts(s, family, sotype int, ipv6only bool) error {
+       if family == syscall.AF_INET6 && sotype != syscall.SOCK_RAW {
+               // Allow both IP versions even if the OS default
+               // is otherwise.  Note that some operating systems
+               // never admit this option.
+               syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, boolint(ipv6only))
+       }
+       // Allow broadcast.
+       return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_BROADCAST, 1))
+}
+
+func setDefaultListenerSockopts(s int) error {
+       // Allow reuse of recently-used addresses.
+       return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1))
+}
+
+func setDefaultMulticastSockopts(s int) error {
+       // Allow multicast UDP and raw IP datagram sockets to listen
+       // concurrently across multiple listeners.
+       return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1))
+}
index 2199e480d423bd5c167d8ff2f3212111b298bf8e..87132f0f4610cc3ee9700d2896b48fdf40723c4c 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.
 
-// +build darwin dragonfly freebsd netbsd openbsd
+// +build darwin dragonfly freebsd nacl netbsd openbsd
 
 package net
 
index c2579be9114fa4796ebaf7acbe07f542ca20d30d..b5c80e4490996609c002c1e6de86325d007fa5af 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.
 
-// +build darwin dragonfly freebsd linux netbsd openbsd windows
+// +build darwin dragonfly freebsd linux nacl netbsd openbsd windows
 
 package net
 
diff --git a/libgo/go/net/sockoptip_stub.go b/libgo/go/net/sockoptip_stub.go
new file mode 100644 (file)
index 0000000..dcd3a22
--- /dev/null
@@ -0,0 +1,39 @@
+// 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.
+
+// +build solaris
+
+package net
+
+import "syscall"
+
+func setIPv4MulticastInterface(fd *netFD, ifi *Interface) error {
+       // See golang.org/issue/7399.
+       return syscall.EINVAL
+}
+
+func setIPv4MulticastLoopback(fd *netFD, v bool) error {
+       // See golang.org/issue/7399.
+       return syscall.EINVAL
+}
+
+func joinIPv4Group(fd *netFD, ifi *Interface, ip IP) error {
+       // See golang.org/issue/7399.
+       return syscall.EINVAL
+}
+
+func setIPv6MulticastInterface(fd *netFD, ifi *Interface) error {
+       // See golang.org/issue/7399.
+       return syscall.EINVAL
+}
+
+func setIPv6MulticastLoopback(fd *netFD, v bool) error {
+       // See golang.org/issue/7399.
+       return syscall.EINVAL
+}
+
+func joinIPv6Group(fd *netFD, ifi *Interface, ip IP) error {
+       // See golang.org/issue/7399.
+       return syscall.EINVAL
+}
index bbfcc1a4fc42dc478e54dd603e5456425f2fac92..898fb7c0c2c5545bfca54a423253a00a56a2c6b2 100644 (file)
@@ -5,7 +5,7 @@
 // This file implements sysSocket and accept for platforms that do not
 // provide a fast path for setting SetNonblock and CloseOnExec.
 
-// +build darwin dragonfly freebsd netbsd openbsd
+// +build darwin dragonfly nacl netbsd openbsd solaris
 
 package net
 
@@ -13,10 +13,10 @@ import "syscall"
 
 // Wrapper around the socket system call that marks the returned file
 // descriptor as nonblocking and close-on-exec.
-func sysSocket(f, t, p int) (int, error) {
+func sysSocket(family, sotype, proto int) (int, error) {
        // See ../syscall/exec_unix.go for description of ForkLock.
        syscall.ForkLock.RLock()
-       s, err := syscall.Socket(f, t, p)
+       s, err := syscall.Socket(family, sotype, proto)
        if err == nil {
                syscall.CloseOnExec(s)
        }
@@ -33,22 +33,22 @@ func sysSocket(f, t, p int) (int, error) {
 
 // Wrapper around the accept system call that marks the returned file
 // descriptor as nonblocking and close-on-exec.
-func accept(fd int) (int, syscall.Sockaddr, error) {
+func accept(s int) (int, syscall.Sockaddr, error) {
        // See ../syscall/exec_unix.go for description of ForkLock.
        // It is probably okay to hold the lock across syscall.Accept
        // because we have put fd.sysfd into non-blocking mode.
        // However, a call to the File method will put it back into
        // blocking mode. We can't take that risk, so no use of ForkLock here.
-       nfd, sa, err := syscall.Accept(fd)
+       ns, sa, err := syscall.Accept(s)
        if err == nil {
-               syscall.CloseOnExec(nfd)
+               syscall.CloseOnExec(ns)
        }
        if err != nil {
                return -1, nil, err
        }
-       if err = syscall.SetNonblock(nfd, true); err != nil {
-               syscall.Close(nfd)
+       if err = syscall.SetNonblock(ns, true); err != nil {
+               syscall.Close(ns)
                return -1, nil, err
        }
-       return nfd, sa, nil
+       return ns, sa, nil
 }
index 62fd99f5c0bb2442e1423207a3041e3b19f279ce..c04198ea000b6f0b81196071f3f225b1d4dddb7c 100644 (file)
@@ -97,6 +97,7 @@ func benchmarkTCP(b *testing.B, persistent, timeout bool, laddr string) {
                b.Fatalf("Listen failed: %v", err)
        }
        defer ln.Close()
+       serverSem := make(chan bool, numConcurrent)
        // Acceptor.
        go func() {
                for {
@@ -104,9 +105,13 @@ func benchmarkTCP(b *testing.B, persistent, timeout bool, laddr string) {
                        if err != nil {
                                break
                        }
+                       serverSem <- true
                        // Server connection.
                        go func(c Conn) {
-                               defer c.Close()
+                               defer func() {
+                                       c.Close()
+                                       <-serverSem
+                               }()
                                if timeout {
                                        c.SetDeadline(time.Now().Add(time.Hour)) // Not intended to fire.
                                }
@@ -119,13 +124,13 @@ func benchmarkTCP(b *testing.B, persistent, timeout bool, laddr string) {
                        }(c)
                }
        }()
-       sem := make(chan bool, numConcurrent)
+       clientSem := make(chan bool, numConcurrent)
        for i := 0; i < conns; i++ {
-               sem <- true
+               clientSem <- true
                // Client connection.
                go func() {
                        defer func() {
-                               <-sem
+                               <-clientSem
                        }()
                        c, err := Dial("tcp", ln.Addr().String())
                        if err != nil {
@@ -144,8 +149,9 @@ func benchmarkTCP(b *testing.B, persistent, timeout bool, laddr string) {
                        }
                }()
        }
-       for i := 0; i < cap(sem); i++ {
-               sem <- true
+       for i := 0; i < numConcurrent; i++ {
+               clientSem <- true
+               serverSem <- true
        }
 }
 
@@ -185,7 +191,8 @@ func benchmarkTCPConcurrentReadWrite(b *testing.B, laddr string) {
                for p := 0; p < P; p++ {
                        s, err := ln.Accept()
                        if err != nil {
-                               b.Fatalf("Accept failed: %v", err)
+                               b.Errorf("Accept failed: %v", err)
+                               return
                        }
                        servers[p] = s
                }
@@ -217,7 +224,8 @@ func benchmarkTCPConcurrentReadWrite(b *testing.B, laddr string) {
                                buf[0] = v
                                _, err := c.Write(buf[:])
                                if err != nil {
-                                       b.Fatalf("Write failed: %v", err)
+                                       b.Errorf("Write failed: %v", err)
+                                       return
                                }
                        }
                }(clients[p])
@@ -232,7 +240,8 @@ func benchmarkTCPConcurrentReadWrite(b *testing.B, laddr string) {
                        for i := 0; i < N; i++ {
                                _, err := s.Read(buf[:])
                                if err != nil {
-                                       b.Fatalf("Read failed: %v", err)
+                                       b.Errorf("Read failed: %v", err)
+                                       return
                                }
                                pipe <- buf[0]
                        }
@@ -250,7 +259,8 @@ func benchmarkTCPConcurrentReadWrite(b *testing.B, laddr string) {
                                buf[0] = v
                                _, err := s.Write(buf[:])
                                if err != nil {
-                                       b.Fatalf("Write failed: %v", err)
+                                       b.Errorf("Write failed: %v", err)
+                                       return
                                }
                        }
                        s.Close()
@@ -263,7 +273,8 @@ func benchmarkTCPConcurrentReadWrite(b *testing.B, laddr string) {
                        for i := 0; i < N; i++ {
                                _, err := c.Read(buf[:])
                                if err != nil {
-                                       b.Fatalf("Read failed: %v", err)
+                                       b.Errorf("Read failed: %v", err)
+                                       return
                                }
                        }
                        c.Close()
@@ -388,7 +399,7 @@ func TestIPv6LinkLocalUnicastTCP(t *testing.T) {
                {"tcp6", "[" + laddr + "%" + ifi.Name + "]:0", false},
        }
        switch runtime.GOOS {
-       case "darwin", "freebsd", "opensbd", "netbsd":
+       case "darwin", "freebsd", "openbsd", "netbsd":
                tests = append(tests, []test{
                        {"tcp", "[localhost%" + ifi.Name + "]:0", true},
                        {"tcp6", "[localhost%" + ifi.Name + "]:0", true},
@@ -460,15 +471,25 @@ func TestTCPConcurrentAccept(t *testing.T) {
                        wg.Done()
                }()
        }
-       for i := 0; i < 10*N; i++ {
-               c, err := Dial("tcp", ln.Addr().String())
+       attempts := 10 * N
+       fails := 0
+       d := &Dialer{Timeout: 200 * time.Millisecond}
+       for i := 0; i < attempts; i++ {
+               c, err := d.Dial("tcp", ln.Addr().String())
                if err != nil {
-                       t.Fatalf("Dial failed: %v", err)
+                       fails++
+               } else {
+                       c.Close()
                }
-               c.Close()
        }
        ln.Close()
        wg.Wait()
+       if fails > attempts/9 { // see issues 7400 and 7541
+               t.Fatalf("too many Dial failed: %v", fails)
+       }
+       if fails > 0 {
+               t.Logf("# of failed Dials: %v", fails)
+       }
 }
 
 func TestTCPReadWriteMallocs(t *testing.T) {
index 6e1a8b9a1928f1168364ba43890365af425b87ae..52019d7b4eb28e0522dac1f686742eb553ba70a6 100644 (file)
@@ -32,7 +32,7 @@ func (c *TCPConn) CloseRead() error {
        if !c.ok() {
                return syscall.EINVAL
        }
-       return c.fd.CloseRead()
+       return c.fd.closeRead()
 }
 
 // CloseWrite shuts down the writing side of the TCP connection.
@@ -41,20 +41,21 @@ func (c *TCPConn) CloseWrite() error {
        if !c.ok() {
                return syscall.EINVAL
        }
-       return c.fd.CloseWrite()
+       return c.fd.closeWrite()
 }
 
-// SetLinger sets the behavior of Close() on a connection which still
+// SetLinger sets the behavior of Close on a connection which still
 // has data waiting to be sent or to be acknowledged.
 //
-// If sec < 0 (the default), Close returns immediately and the
-// operating system finishes sending the data in the background.
+// If sec < 0 (the default), the operating system finishes sending the
+// data in the background.
 //
-// If sec == 0, Close returns immediately and the operating system
-// discards any unsent or unacknowledged data.
+// If sec == 0, the operating system discards any unsent or
+// unacknowledged data.
 //
-// If sec > 0, Close blocks for at most sec seconds waiting for data
-// to be sent and acknowledged.
+// If sec > 0, the data is sent in the background as with sec < 0. On
+// some operating systems after sec seconds have elapsed any remaining
+// unsent data may be discarded.
 func (c *TCPConn) SetLinger(sec int) error {
        return syscall.EPLAN9
 }
index 00c692e423377d12d7ca4bce9d29018f0b1498e3..b79b115ca5b8d4e71b5ccc4d4bae544d358d7cd2 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.
 
-// +build darwin dragonfly freebsd linux netbsd openbsd windows
+// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows
 
 package net
 
@@ -78,7 +78,7 @@ func (c *TCPConn) CloseRead() error {
        if !c.ok() {
                return syscall.EINVAL
        }
-       return c.fd.CloseRead()
+       return c.fd.closeRead()
 }
 
 // CloseWrite shuts down the writing side of the TCP connection.
@@ -87,20 +87,21 @@ func (c *TCPConn) CloseWrite() error {
        if !c.ok() {
                return syscall.EINVAL
        }
-       return c.fd.CloseWrite()
+       return c.fd.closeWrite()
 }
 
-// SetLinger sets the behavior of Close() on a connection which still
+// SetLinger sets the behavior of Close on a connection which still
 // has data waiting to be sent or to be acknowledged.
 //
-// If sec < 0 (the default), Close returns immediately and the
-// operating system finishes sending the data in the background.
+// If sec < 0 (the default), the operating system finishes sending the
+// data in the background.
 //
-// If sec == 0, Close returns immediately and the operating system
-// discards any unsent or unacknowledged data.
+// If sec == 0, the operating system discards any unsent or
+// unacknowledged data.
 //
-// If sec > 0, Close blocks for at most sec seconds waiting for data
-// to be sent and acknowledged.
+// If sec > 0, the data is sent in the background as with sec < 0. On
+// some operating systems after sec seconds have elapsed any remaining
+// unsent data may be discarded.
 func (c *TCPConn) SetLinger(sec int) error {
        if !c.ok() {
                return syscall.EINVAL
diff --git a/libgo/go/net/tcpsockopt_dragonfly.go b/libgo/go/net/tcpsockopt_dragonfly.go
new file mode 100644 (file)
index 0000000..d10a777
--- /dev/null
@@ -0,0 +1,29 @@
+// Copyright 2009 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package net
+
+import (
+       "os"
+       "syscall"
+       "time"
+)
+
+// Set keep alive period.
+func setKeepAlivePeriod(fd *netFD, d time.Duration) error {
+       if err := fd.incref(); err != nil {
+               return err
+       }
+       defer fd.decref()
+
+       // The kernel expects milliseconds so round to next highest millisecond.
+       d += (time.Millisecond - time.Nanosecond)
+       msecs := int(time.Duration(d.Nanoseconds()) / time.Millisecond)
+
+       err := os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.TCP_KEEPINTVL, msecs))
+       if err != nil {
+               return err
+       }
+       return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.TCP_KEEPIDLE, msecs))
+}
index e03476ac6346aa5ad63b1207231d4ce340b437f8..6484bad4b45d6f3a4856095cd8a0f42e8cc1a2e4 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.
 
-// +build darwin dragonfly freebsd linux netbsd openbsd windows
+// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows
 
 package net
 
diff --git a/libgo/go/net/tcpsockopt_solaris.go b/libgo/go/net/tcpsockopt_solaris.go
new file mode 100644 (file)
index 0000000..eaab6b6
--- /dev/null
@@ -0,0 +1,27 @@
+// Copyright 2013 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.
+
+// TCP socket options for solaris
+
+package net
+
+import (
+       "os"
+       "syscall"
+       "time"
+)
+
+// Set keep alive period.
+func setKeepAlivePeriod(fd *netFD, d time.Duration) error {
+       if err := fd.incref(); err != nil {
+               return err
+       }
+       defer fd.decref()
+
+       // The kernel expects seconds so round to next highest second.
+       d += (time.Second - time.Nanosecond)
+       secs := int(d.Seconds())
+
+       return os.NewSyscallError("setsockopt", syscall.SetsockoptInt(fd.sysfd, syscall.IPPROTO_TCP, syscall.SO_KEEPALIVE, secs))
+}
index 89d9143b52e2bfb54ac301decf8112b86e163cda..2693a541d209fa14af05ee4d4389b7299d7509de 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.
 
-// +build dragonfly freebsd linux netbsd
+// +build freebsd linux nacl netbsd
 
 package net
 
index b0c07413c19dbf23c269cb06e5033fba77e8c568..eea9207f2521a60fdf897931365bfc1e791df1db 100644 (file)
@@ -562,19 +562,12 @@ const toLower = 'a' - 'A'
 // allowed to mutate the provided byte slice before returning the
 // string.
 func canonicalMIMEHeaderKey(a []byte) string {
-       // Look for it in commonHeaders , so that we can avoid an
-       // allocation by sharing the strings among all users
-       // of textproto. If we don't find it, a has been canonicalized
-       // so just return string(a).
        upper := true
-       lo := 0
-       hi := len(commonHeaders)
-       for i := 0; i < len(a); i++ {
+       for i, c := range a {
                // Canonicalize: first letter upper case
                // and upper case after each dash.
                // (Host, User-Agent, If-Modified-Since).
                // MIME headers are ASCII only, so no Unicode issues.
-               c := a[i]
                if c == ' ' {
                        c = '-'
                } else if upper && 'a' <= c && c <= 'z' {
@@ -584,60 +577,61 @@ func canonicalMIMEHeaderKey(a []byte) string {
                }
                a[i] = c
                upper = c == '-' // for next time
-
-               if lo < hi {
-                       for lo < hi && (len(commonHeaders[lo]) <= i || commonHeaders[lo][i] < c) {
-                               lo++
-                       }
-                       for hi > lo && commonHeaders[hi-1][i] > c {
-                               hi--
-                       }
-               }
        }
-       if lo < hi && len(commonHeaders[lo]) == len(a) {
-               return commonHeaders[lo]
+       // The compiler recognizes m[string(byteSlice)] as a special
+       // case, so a copy of a's bytes into a new string does not
+       // happen in this map lookup:
+       if v := commonHeader[string(a)]; v != "" {
+               return v
        }
        return string(a)
 }
 
-var commonHeaders = []string{
-       "Accept",
-       "Accept-Charset",
-       "Accept-Encoding",
-       "Accept-Language",
-       "Accept-Ranges",
-       "Cache-Control",
-       "Cc",
-       "Connection",
-       "Content-Id",
-       "Content-Language",
-       "Content-Length",
-       "Content-Transfer-Encoding",
-       "Content-Type",
-       "Cookie",
-       "Date",
-       "Dkim-Signature",
-       "Etag",
-       "Expires",
-       "From",
-       "Host",
-       "If-Modified-Since",
-       "If-None-Match",
-       "In-Reply-To",
-       "Last-Modified",
-       "Location",
-       "Message-Id",
-       "Mime-Version",
-       "Pragma",
-       "Received",
-       "Return-Path",
-       "Server",
-       "Set-Cookie",
-       "Subject",
-       "To",
-       "User-Agent",
-       "Via",
-       "X-Forwarded-For",
-       "X-Imforwards",
-       "X-Powered-By",
+// commonHeader interns common header strings.
+var commonHeader = make(map[string]string)
+
+func init() {
+       for _, v := range []string{
+               "Accept",
+               "Accept-Charset",
+               "Accept-Encoding",
+               "Accept-Language",
+               "Accept-Ranges",
+               "Cache-Control",
+               "Cc",
+               "Connection",
+               "Content-Id",
+               "Content-Language",
+               "Content-Length",
+               "Content-Transfer-Encoding",
+               "Content-Type",
+               "Cookie",
+               "Date",
+               "Dkim-Signature",
+               "Etag",
+               "Expires",
+               "From",
+               "Host",
+               "If-Modified-Since",
+               "If-None-Match",
+               "In-Reply-To",
+               "Last-Modified",
+               "Location",
+               "Message-Id",
+               "Mime-Version",
+               "Pragma",
+               "Received",
+               "Return-Path",
+               "Server",
+               "Set-Cookie",
+               "Subject",
+               "To",
+               "User-Agent",
+               "Via",
+               "X-Forwarded-For",
+               "X-Imforwards",
+               "X-Powered-By",
+       } {
+               commonHeader[v] = v
+       }
 }
index cc12912b634bea67ad0013aeec4838739545fd0d..c89566635e12942112e5374daaa93f53aacb6e3c 100644 (file)
@@ -247,24 +247,21 @@ func TestRFC959Lines(t *testing.T) {
 }
 
 func TestCommonHeaders(t *testing.T) {
-       // need to disable the commonHeaders-based optimization
-       // during this check, or we'd not be testing anything
-       oldch := commonHeaders
-       commonHeaders = []string{}
-       defer func() { commonHeaders = oldch }()
-
-       last := ""
-       for _, h := range oldch {
-               if last > h {
-                       t.Errorf("%v is out of order", h)
-               }
-               if last == h {
-                       t.Errorf("%v is duplicated", h)
+       for h := range commonHeader {
+               if h != CanonicalMIMEHeaderKey(h) {
+                       t.Errorf("Non-canonical header %q in commonHeader", h)
                }
-               if canon := CanonicalMIMEHeaderKey(h); h != canon {
-                       t.Errorf("%v is not canonical", h)
+       }
+       t.Skip("gccgo escape analysis")
+       b := []byte("content-Length")
+       want := "Content-Length"
+       n := testing.AllocsPerRun(200, func() {
+               if x := canonicalMIMEHeaderKey(b); x != want {
+                       t.Fatalf("canonicalMIMEHeaderKey(%q) = %q; want %q", b, x, want)
                }
-               last = h
+       })
+       if n > 0 {
+               t.Errorf("canonicalMIMEHeaderKey allocs = %v; want 0", n)
        }
 }
 
index 35d427a69c08ef2240acde01f1150eda3cbd0421..9ef0c4d15cc1f77fab13200f7dd241ae0ed20de3 100644 (file)
@@ -120,6 +120,9 @@ func TestReadTimeout(t *testing.T) {
                        t.Fatalf("Read: expected err %v, got %v", errClosing, err)
                }
        default:
+               if err == io.EOF && runtime.GOOS == "nacl" { // close enough; golang.org/issue/8044
+                       break
+               }
                if err != errClosing {
                        t.Fatalf("Read: expected err %v, got %v", errClosing, err)
                }
@@ -348,7 +351,8 @@ func TestReadWriteDeadline(t *testing.T) {
        go func() {
                c, err := ln.Accept()
                if err != nil {
-                       t.Fatalf("Accept: %v", err)
+                       t.Errorf("Accept: %v", err)
+                       return
                }
                defer c.Close()
                lnquit <- true
@@ -493,10 +497,7 @@ func testVariousDeadlines(t *testing.T, maxProcs int) {
                                clientc <- copyRes{n, err, d}
                        }()
 
-                       tooLong := 2 * time.Second
-                       if runtime.GOOS == "windows" {
-                               tooLong = 5 * time.Second
-                       }
+                       tooLong := 5 * time.Second
                        select {
                        case res := <-clientc:
                                if isTimeout(res.err) {
@@ -536,7 +537,8 @@ func TestReadDeadlineDataAvailable(t *testing.T) {
        go func() {
                c, err := ln.Accept()
                if err != nil {
-                       t.Fatalf("Accept: %v", err)
+                       t.Errorf("Accept: %v", err)
+                       return
                }
                defer c.Close()
                n, err := c.Write([]byte(msg))
@@ -574,7 +576,8 @@ func TestWriteDeadlineBufferAvailable(t *testing.T) {
        go func() {
                c, err := ln.Accept()
                if err != nil {
-                       t.Fatalf("Accept: %v", err)
+                       t.Errorf("Accept: %v", err)
+                       return
                }
                defer c.Close()
                c.SetWriteDeadline(time.Now().Add(-5 * time.Second)) // in the past
@@ -610,7 +613,8 @@ func TestAcceptDeadlineConnectionAvailable(t *testing.T) {
        go func() {
                c, err := Dial("tcp", ln.Addr().String())
                if err != nil {
-                       t.Fatalf("Dial: %v", err)
+                       t.Errorf("Dial: %v", err)
+                       return
                }
                defer c.Close()
                var buf [1]byte
@@ -669,7 +673,8 @@ func TestProlongTimeout(t *testing.T) {
                s, err := ln.Accept()
                connected <- true
                if err != nil {
-                       t.Fatalf("ln.Accept: %v", err)
+                       t.Errorf("ln.Accept: %v", err)
+                       return
                }
                defer s.Close()
                s.SetDeadline(time.Now().Add(time.Hour))
@@ -706,7 +711,7 @@ func TestProlongTimeout(t *testing.T) {
 
 func TestDeadlineRace(t *testing.T) {
        switch runtime.GOOS {
-       case "plan9":
+       case "nacl", "plan9":
                t.Skipf("skipping test on %q", runtime.GOOS)
        }
 
index 6f4d2152c3cd54ccd12c6530baf40165a326b451..e1778779cf591f29bdfd294eb6216d04d869cc4c 100644 (file)
@@ -201,6 +201,10 @@ func TestIPv6LinkLocalUnicastUDP(t *testing.T) {
                {"udp", "[" + laddr + "%" + ifi.Name + "]:0", false},
                {"udp6", "[" + laddr + "%" + ifi.Name + "]:0", false},
        }
+       // The first udp test fails on DragonFly - see issue 7473.
+       if runtime.GOOS == "dragonfly" {
+               tests = tests[1:]
+       }
        switch runtime.GOOS {
        case "darwin", "dragonfly", "freebsd", "openbsd", "netbsd":
                tests = append(tests, []test{
index 0dd0dbd711444b6a07cc26c9916be58df88845d3..4c99ae4af6837e8c01ae03dcec0ca2111b2f0ac2 100644 (file)
@@ -4,10 +4,6 @@
 
 package net
 
-import "errors"
-
-var ErrWriteToConnected = errors.New("use of WriteTo with pre-connected UDP")
-
 // UDPAddr represents the address of a UDP end point.
 type UDPAddr struct {
        IP   IP
index 142da8186f1634dd21907dfc176ba117ed06bdb3..5dfba94e9a6d25f698f7977fee3ed9e8a54f9a2f 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.
 
-// +build darwin dragonfly freebsd linux netbsd openbsd windows
+// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows
 
 package net
 
@@ -64,7 +64,7 @@ func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *UDPAddr, err error) {
        if !c.ok() {
                return 0, nil, syscall.EINVAL
        }
-       n, sa, err := c.fd.ReadFrom(b)
+       n, sa, err := c.fd.readFrom(b)
        switch sa := sa.(type) {
        case *syscall.SockaddrInet4:
                addr = &UDPAddr{IP: sa.Addr[0:], Port: sa.Port}
@@ -93,7 +93,7 @@ func (c *UDPConn) ReadMsgUDP(b, oob []byte) (n, oobn, flags int, addr *UDPAddr,
                return 0, 0, 0, nil, syscall.EINVAL
        }
        var sa syscall.Sockaddr
-       n, oobn, flags, sa, err = c.fd.ReadMsg(b, oob)
+       n, oobn, flags, sa, err = c.fd.readMsg(b, oob)
        switch sa := sa.(type) {
        case *syscall.SockaddrInet4:
                addr = &UDPAddr{IP: sa.Addr[0:], Port: sa.Port}
@@ -124,7 +124,7 @@ func (c *UDPConn) WriteToUDP(b []byte, addr *UDPAddr) (int, error) {
        if err != nil {
                return 0, &OpError{"write", c.fd.net, addr, err}
        }
-       return c.fd.WriteTo(b, sa)
+       return c.fd.writeTo(b, sa)
 }
 
 // WriteTo implements the PacketConn WriteTo method.
@@ -156,7 +156,7 @@ func (c *UDPConn) WriteMsgUDP(b, oob []byte, addr *UDPAddr) (n, oobn int, err er
        if err != nil {
                return 0, 0, &OpError{"write", c.fd.net, addr, err}
        }
-       return c.fd.WriteMsg(b, oob, sa)
+       return c.fd.writeMsg(b, oob, sa)
 }
 
 // DialUDP connects to the remote address raddr on the network net,
index 91df3ff88769bc53454a9d4c7765a5aa349cbf5c..05643ddf9aeae6dcdb1989f22a1c8b6d29b80cb8 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.
 
-// +build !plan9,!windows
+// +build !nacl,!plan9,!windows
 
 package net
 
@@ -151,6 +151,73 @@ func TestUnixAutobindClose(t *testing.T) {
        ln.Close()
 }
 
+func TestUnixgramWrite(t *testing.T) {
+       addr := testUnixAddr()
+       laddr, err := ResolveUnixAddr("unixgram", addr)
+       if err != nil {
+               t.Fatalf("ResolveUnixAddr failed: %v", err)
+       }
+       c, err := ListenPacket("unixgram", addr)
+       if err != nil {
+               t.Fatalf("ListenPacket failed: %v", err)
+       }
+       defer os.Remove(addr)
+       defer c.Close()
+
+       testUnixgramWriteConn(t, laddr)
+       testUnixgramWritePacketConn(t, laddr)
+}
+
+func testUnixgramWriteConn(t *testing.T, raddr *UnixAddr) {
+       c, err := Dial("unixgram", raddr.String())
+       if err != nil {
+               t.Fatalf("Dial failed: %v", err)
+       }
+       defer c.Close()
+
+       if _, err := c.(*UnixConn).WriteToUnix([]byte("Connection-oriented mode socket"), raddr); err == nil {
+               t.Fatal("WriteToUnix should fail")
+       } else if err.(*OpError).Err != ErrWriteToConnected {
+               t.Fatalf("WriteToUnix should fail as ErrWriteToConnected: %v", err)
+       }
+       if _, err = c.(*UnixConn).WriteTo([]byte("Connection-oriented mode socket"), raddr); err == nil {
+               t.Fatal("WriteTo should fail")
+       } else if err.(*OpError).Err != ErrWriteToConnected {
+               t.Fatalf("WriteTo should fail as ErrWriteToConnected: %v", err)
+       }
+       if _, _, err = c.(*UnixConn).WriteMsgUnix([]byte("Connection-oriented mode socket"), nil, raddr); err == nil {
+               t.Fatal("WriteTo should fail")
+       } else if err.(*OpError).Err != ErrWriteToConnected {
+               t.Fatalf("WriteMsgUnix should fail as ErrWriteToConnected: %v", err)
+       }
+       if _, err := c.Write([]byte("Connection-oriented mode socket")); err != nil {
+               t.Fatalf("Write failed: %v", err)
+       }
+}
+
+func testUnixgramWritePacketConn(t *testing.T, raddr *UnixAddr) {
+       addr := testUnixAddr()
+       c, err := ListenPacket("unixgram", addr)
+       if err != nil {
+               t.Fatalf("ListenPacket failed: %v", err)
+       }
+       defer os.Remove(addr)
+       defer c.Close()
+
+       if _, err := c.(*UnixConn).WriteToUnix([]byte("Connectionless mode socket"), raddr); err != nil {
+               t.Fatalf("WriteToUnix failed: %v", err)
+       }
+       if _, err := c.WriteTo([]byte("Connectionless mode socket"), raddr); err != nil {
+               t.Fatalf("WriteTo failed: %v", err)
+       }
+       if _, _, err := c.(*UnixConn).WriteMsgUnix([]byte("Connectionless mode socket"), nil, raddr); err != nil {
+               t.Fatalf("WriteMsgUnix failed: %v", err)
+       }
+       if _, err := c.(*UnixConn).Write([]byte("Connectionless mode socket")); err == nil {
+               t.Fatal("Write should fail")
+       }
+}
+
 func TestUnixConnLocalAndRemoteNames(t *testing.T) {
        for _, laddr := range []string{"", testUnixAddr()} {
                laddr := laddr
index 54d9d16c99e69702b534533a440ff4a3f6b96de8..2610779bfd28ac69cad7ce695a4db9b5463e0f78 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.
 
-// +build darwin dragonfly freebsd linux netbsd openbsd windows
+// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows
 
 package net
 
@@ -124,7 +124,7 @@ func (c *UnixConn) ReadFromUnix(b []byte) (n int, addr *UnixAddr, err error) {
        if !c.ok() {
                return 0, nil, syscall.EINVAL
        }
-       n, sa, err := c.fd.ReadFrom(b)
+       n, sa, err := c.fd.readFrom(b)
        switch sa := sa.(type) {
        case *syscall.SockaddrUnix:
                if sa.Name != "" {
@@ -151,7 +151,7 @@ func (c *UnixConn) ReadMsgUnix(b, oob []byte) (n, oobn, flags int, addr *UnixAdd
        if !c.ok() {
                return 0, 0, 0, nil, syscall.EINVAL
        }
-       n, oobn, flags, sa, err := c.fd.ReadMsg(b, oob)
+       n, oobn, flags, sa, err := c.fd.readMsg(b, oob)
        switch sa := sa.(type) {
        case *syscall.SockaddrUnix:
                if sa.Name != "" {
@@ -171,6 +171,9 @@ func (c *UnixConn) WriteToUnix(b []byte, addr *UnixAddr) (n int, err error) {
        if !c.ok() {
                return 0, syscall.EINVAL
        }
+       if c.fd.isConnected {
+               return 0, &OpError{Op: "write", Net: c.fd.net, Addr: addr, Err: ErrWriteToConnected}
+       }
        if addr == nil {
                return 0, &OpError{Op: "write", Net: c.fd.net, Addr: nil, Err: errMissingAddress}
        }
@@ -178,7 +181,7 @@ func (c *UnixConn) WriteToUnix(b []byte, addr *UnixAddr) (n int, err error) {
                return 0, syscall.EAFNOSUPPORT
        }
        sa := &syscall.SockaddrUnix{Name: addr.Name}
-       return c.fd.WriteTo(b, sa)
+       return c.fd.writeTo(b, sa)
 }
 
 // WriteTo implements the PacketConn WriteTo method.
@@ -200,14 +203,17 @@ func (c *UnixConn) WriteMsgUnix(b, oob []byte, addr *UnixAddr) (n, oobn int, err
        if !c.ok() {
                return 0, 0, syscall.EINVAL
        }
+       if c.fd.sotype == syscall.SOCK_DGRAM && c.fd.isConnected {
+               return 0, 0, &OpError{Op: "write", Net: c.fd.net, Addr: addr, Err: ErrWriteToConnected}
+       }
        if addr != nil {
                if addr.Net != sotypeToNet(c.fd.sotype) {
                        return 0, 0, syscall.EAFNOSUPPORT
                }
                sa := &syscall.SockaddrUnix{Name: addr.Name}
-               return c.fd.WriteMsg(b, oob, sa)
+               return c.fd.writeMsg(b, oob, sa)
        }
-       return c.fd.WriteMsg(b, oob, nil)
+       return c.fd.writeMsg(b, oob, nil)
 }
 
 // CloseRead shuts down the reading side of the Unix domain connection.
@@ -216,7 +222,7 @@ func (c *UnixConn) CloseRead() error {
        if !c.ok() {
                return syscall.EINVAL
        }
-       return c.fd.CloseRead()
+       return c.fd.closeRead()
 }
 
 // CloseWrite shuts down the writing side of the Unix domain connection.
@@ -225,7 +231,7 @@ func (c *UnixConn) CloseWrite() error {
        if !c.ok() {
                return syscall.EINVAL
        }
-       return c.fd.CloseWrite()
+       return c.fd.closeWrite()
 }
 
 // DialUnix connects to the remote address raddr on the network net,
index 3b3787202b7dca6c2f6d4df9afed6b9eb9e96a41..75f650a2756b53f79dccd4e093782afabc8a0d39 100644 (file)
@@ -502,7 +502,7 @@ func (v Values) Set(key, value string) {
        v[key] = []string{value}
 }
 
-// Add adds the key to value. It appends to any existing
+// Add adds the value to key. It appends to any existing
 // values associated with key.
 func (v Values) Add(key, value string) {
        v[key] = append(v[key], value)
index 7578eb15b907ba0bce2b3f207be6cc818b1eee36..cad758f238520bd58529e96ecd2e2bc6b47fb05d 100644 (file)
@@ -251,6 +251,17 @@ var urltests = []URLTest{
                },
                "file:///home/adg/rabbits",
        },
+       // "Windows" paths are no exception to the rule.
+       // See golang.org/issue/6027, especially comment #9.
+       {
+               "file:///C:/FooBar/Baz.txt",
+               &URL{
+                       Scheme: "file",
+                       Host:   "",
+                       Path:   "/C:/FooBar/Baz.txt",
+               },
+               "file:///C:/FooBar/Baz.txt",
+       },
        // case-insensitive scheme
        {
                "MaIlTo:webmaster@golang.org",
index 67c390283c7336349d714bef3c36d13126088a97..d353e405e54178088608408609ede466248fcfbb 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.
 
-// +build darwin dragonfly freebsd linux netbsd openbsd solaris
+// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
 
 package os
 
index a954e313d135b78f2ebfc5a19c65c37170cf51f8..389a8eb14cb284db9a5190fda7dda7ab604ac2ed 100644 (file)
@@ -39,11 +39,14 @@ func (p *Process) Kill() error {
 // Wait waits for the Process to exit, and then returns a
 // ProcessState describing its status and an error, if any.
 // Wait releases any resources associated with the Process.
+// On most operating systems, the Process must be a child
+// of the current process or an error will be returned.
 func (p *Process) Wait() (*ProcessState, error) {
        return p.wait()
 }
 
 // Signal sends a signal to the Process.
+// Sending Interrupt on Windows is not implemented.
 func (p *Process) Signal(sig Signal) error {
        return p.signal(sig)
 }
index f281495e6b8e09ecc4eb144022f1fd4346282e28..f2aabbb45c43b5282ce97d1225cd08d568c826fd 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.
 
-// +build darwin dragonfly freebsd linux netbsd openbsd solaris
+// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
 
 package os
 
index 4680036fddcc6eefc19db1cbff5b1bbe59ceb0e9..a70ed0d20cbba995f25de65692138301d89c14ea 100644 (file)
@@ -13,7 +13,9 @@ import (
        "io"
        "os"
        "path/filepath"
+       "runtime"
        "strconv"
+       "strings"
        "sync"
        "syscall"
 )
@@ -237,13 +239,51 @@ func (c *Cmd) Run() error {
        return c.Wait()
 }
 
+// lookExtensions finds windows executable by its dir and path.
+// It uses LookPath to try appropriate extensions.
+// lookExtensions does not search PATH, instead it converts `prog` into `.\prog`.
+func lookExtensions(path, dir string) (string, error) {
+       if filepath.Base(path) == path {
+               path = filepath.Join(".", path)
+       }
+       if dir == "" {
+               return LookPath(path)
+       }
+       if filepath.VolumeName(path) != "" {
+               return LookPath(path)
+       }
+       if len(path) > 1 && os.IsPathSeparator(path[0]) {
+               return LookPath(path)
+       }
+       dirandpath := filepath.Join(dir, path)
+       // We assume that LookPath will only add file extension.
+       lp, err := LookPath(dirandpath)
+       if err != nil {
+               return "", err
+       }
+       ext := strings.TrimPrefix(lp, dirandpath)
+       return path + ext, nil
+}
+
 // Start starts the specified command but does not wait for it to complete.
+//
+// The Wait method will return the exit code and release associated resources
+// once the command exits.
 func (c *Cmd) Start() error {
        if c.lookPathErr != nil {
                c.closeDescriptors(c.closeAfterStart)
                c.closeDescriptors(c.closeAfterWait)
                return c.lookPathErr
        }
+       if runtime.GOOS == "windows" {
+               lp, err := lookExtensions(c.Path, c.Dir)
+               if err != nil {
+                       c.closeDescriptors(c.closeAfterStart)
+                       c.closeDescriptors(c.closeAfterWait)
+                       return err
+               }
+               c.Path = lp
+       }
        if c.Process != nil {
                return errors.New("exec: already started")
        }
@@ -304,6 +344,8 @@ func (e *ExitError) Error() string {
 // If the command fails to run or doesn't complete successfully, the
 // error is of type *ExitError. Other error types may be
 // returned for I/O problems.
+//
+// Wait releases any resources associated with the Cmd.
 func (c *Cmd) Wait() error {
        if c.Process == nil {
                return errors.New("exec: not started")
@@ -387,15 +429,17 @@ func (c *Cmd) StdinPipe() (io.WriteCloser, error) {
 type closeOnce struct {
        *os.File
 
-       close    sync.Once
-       closeErr error
+       once sync.Once
+       err  error
 }
 
 func (c *closeOnce) Close() error {
-       c.close.Do(func() {
-               c.closeErr = c.File.Close()
-       })
-       return c.closeErr
+       c.once.Do(c.close)
+       return c.err
+}
+
+func (c *closeOnce) close() {
+       c.err = c.File.Close()
 }
 
 // StdoutPipe returns a pipe that will be connected to the command's
index 06248925da28c3aa422ca71b50f78aa9eb368512..8521bfda3f968c43327537541dcea7293804fa92 100644 (file)
@@ -13,6 +13,7 @@ import (
        "fmt"
        "io"
        "io/ioutil"
+       "log"
        "net"
        "net/http"
        "net/http/httptest"
@@ -26,7 +27,10 @@ import (
        "time"
 )
 
-func helperCommand(s ...string) *exec.Cmd {
+func helperCommand(t *testing.T, s ...string) *exec.Cmd {
+       if runtime.GOOS == "nacl" {
+               t.Skip("skipping on nacl")
+       }
        cs := []string{"-test.run=TestHelperProcess", "--"}
        cs = append(cs, s...)
        cmd := exec.Command(os.Args[0], cs...)
@@ -39,7 +43,7 @@ func helperCommand(s ...string) *exec.Cmd {
 }
 
 func TestEcho(t *testing.T) {
-       bs, err := helperCommand("echo", "foo bar", "baz").Output()
+       bs, err := helperCommand(t, "echo", "foo bar", "baz").Output()
        if err != nil {
                t.Errorf("echo: %v", err)
        }
@@ -78,7 +82,7 @@ func TestCommandRelativeName(t *testing.T) {
 func TestCatStdin(t *testing.T) {
        // Cat, testing stdin and stdout.
        input := "Input string\nLine 2"
-       p := helperCommand("cat")
+       p := helperCommand(t, "cat")
        p.Stdin = strings.NewReader(input)
        bs, err := p.Output()
        if err != nil {
@@ -92,7 +96,7 @@ func TestCatStdin(t *testing.T) {
 
 func TestCatGoodAndBadFile(t *testing.T) {
        // Testing combined output and error values.
-       bs, err := helperCommand("cat", "/bogus/file.foo", "exec_test.go").CombinedOutput()
+       bs, err := helperCommand(t, "cat", "/bogus/file.foo", "exec_test.go").CombinedOutput()
        if _, ok := err.(*exec.ExitError); !ok {
                t.Errorf("expected *exec.ExitError from cat combined; got %T: %v", err, err)
        }
@@ -120,7 +124,7 @@ func TestNoExistBinary(t *testing.T) {
 
 func TestExitStatus(t *testing.T) {
        // Test that exit values are returned correctly
-       cmd := helperCommand("exit", "42")
+       cmd := helperCommand(t, "exit", "42")
        err := cmd.Run()
        want := "exit status 42"
        switch runtime.GOOS {
@@ -143,7 +147,7 @@ func TestPipes(t *testing.T) {
                }
        }
        // Cat, testing stdin and stdout.
-       c := helperCommand("pipetest")
+       c := helperCommand(t, "pipetest")
        stdin, err := c.StdinPipe()
        check("StdinPipe", err)
        stdout, err := c.StdoutPipe()
@@ -196,7 +200,7 @@ func TestStdinClose(t *testing.T) {
                        t.Fatalf("%s: %v", what, err)
                }
        }
-       cmd := helperCommand("stdinClose")
+       cmd := helperCommand(t, "stdinClose")
        stdin, err := cmd.StdinPipe()
        check("StdinPipe", err)
        // Check that we can access methods of the underlying os.File.`
@@ -217,7 +221,7 @@ func TestStdinClose(t *testing.T) {
 
 // Issue 5071
 func TestPipeLookPathLeak(t *testing.T) {
-       fd0 := numOpenFDS(t)
+       fd0, lsof0 := numOpenFDS(t)
        for i := 0; i < 4; i++ {
                cmd := exec.Command("something-that-does-not-exist-binary")
                cmd.StdoutPipe()
@@ -227,19 +231,30 @@ func TestPipeLookPathLeak(t *testing.T) {
                        t.Fatal("unexpected success")
                }
        }
-       fdGrowth := numOpenFDS(t) - fd0
-       if fdGrowth > 2 {
-               t.Errorf("leaked %d fds; want ~0", fdGrowth)
+       for triesLeft := 3; triesLeft >= 0; triesLeft-- {
+               open, lsof := numOpenFDS(t)
+               fdGrowth := open - fd0
+               if fdGrowth > 2 {
+                       if triesLeft > 0 {
+                               // Work around what appears to be a race with Linux's
+                               // proc filesystem (as used by lsof). It seems to only
+                               // be eventually consistent. Give it awhile to settle.
+                               // See golang.org/issue/7808
+                               time.Sleep(100 * time.Millisecond)
+                               continue
+                       }
+                       t.Errorf("leaked %d fds; want ~0; have:\n%s\noriginally:\n%s", fdGrowth, lsof, lsof0)
+               }
+               break
        }
 }
 
-func numOpenFDS(t *testing.T) int {
+func numOpenFDS(t *testing.T) (n int, lsof []byte) {
        lsof, err := exec.Command("lsof", "-n", "-p", strconv.Itoa(os.Getpid())).Output()
        if err != nil {
                t.Skip("skipping test; error finding or running lsof")
-               return 0
        }
-       return bytes.Count(lsof, []byte("\n"))
+       return bytes.Count(lsof, []byte("\n")), lsof
 }
 
 var testedAlreadyLeaked = false
@@ -305,7 +320,7 @@ func TestExtraFilesFDShuffle(t *testing.T) {
        // Moving this test case around within the overall tests may
        // affect the FDs obtained and hence the checks to catch these cases.
        npipes := 2
-       c := helperCommand("extraFilesAndPipes", strconv.Itoa(npipes+1))
+       c := helperCommand(t, "extraFilesAndPipes", strconv.Itoa(npipes+1))
        rd, wr, _ := os.Pipe()
        defer rd.Close()
        if rd.Fd() != 3 {
@@ -405,11 +420,15 @@ func TestExtraFiles(t *testing.T) {
 
        // Force TLS root certs to be loaded (which might involve
        // cgo), to make sure none of that potential C code leaks fds.
-       ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-               w.Write([]byte("Hello"))
-       }))
+       ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
+       // quiet expected TLS handshake error "remote error: bad certificate"
+       ts.Config.ErrorLog = log.New(ioutil.Discard, "", 0)
+       ts.StartTLS()
        defer ts.Close()
-       http.Get(ts.URL) // ignore result; just calling to force root cert loading
+       _, err = http.Get(ts.URL)
+       if err == nil {
+               t.Errorf("success trying to fetch %s; want an error", ts.URL)
+       }
 
        tf, err := ioutil.TempFile("", "")
        if err != nil {
@@ -428,7 +447,7 @@ func TestExtraFiles(t *testing.T) {
                t.Fatalf("Seek: %v", err)
        }
 
-       c := helperCommand("read3")
+       c := helperCommand(t, "read3")
        var stdout, stderr bytes.Buffer
        c.Stdout = &stdout
        c.Stderr = &stderr
@@ -471,10 +490,10 @@ func TestExtraFilesRace(t *testing.T) {
 
        for i := 0; i < 10; i++ {
                la := listen()
-               ca := helperCommand("describefiles")
+               ca := helperCommand(t, "describefiles")
                ca.ExtraFiles = []*os.File{listenerFile(la)}
                lb := listen()
-               cb := helperCommand("describefiles")
+               cb := helperCommand(t, "describefiles")
                cb.ExtraFiles = []*os.File{listenerFile(lb)}
                ares := make(chan string)
                bres := make(chan string)
@@ -686,6 +705,24 @@ func TestHelperProcess(*testing.T) {
                }
                fmt.Fprintf(os.Stderr, "child: %s", response)
                os.Exit(0)
+       case "exec":
+               cmd := exec.Command(args[1])
+               cmd.Dir = args[0]
+               output, err := cmd.CombinedOutput()
+               if err != nil {
+                       fmt.Fprintf(os.Stderr, "Child: %s %s", err, string(output))
+                       os.Exit(1)
+               }
+               fmt.Printf("%s", string(output))
+               os.Exit(0)
+       case "lookpath":
+               p, err := exec.LookPath(args[0])
+               if err != nil {
+                       fmt.Fprintf(os.Stderr, "LookPath failed: %v\n", err)
+                       os.Exit(1)
+               }
+               fmt.Print(p)
+               os.Exit(0)
        default:
                fmt.Fprintf(os.Stderr, "Unknown command %q\n", cmd)
                os.Exit(2)
index 7b9dec7e8b50894c4d1a1b0ff9848465c59c7087..3f895d5b3bacbe9d1001e21d1bba1adf9dfb80cb 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.
 
-// +build darwin dragonfly freebsd linux netbsd openbsd solaris
+// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
 
 package exec
 
index 2bd5b6888d98b07a0ec2acef6336e9c9ab638fa8..676be36ac775e10b269d6164a1dbe87fde26f701 100644 (file)
@@ -52,10 +52,6 @@ func (p *Process) signal(sig Signal) error {
        if p.done() {
                return errors.New("os: process already finished")
        }
-       if sig == Kill {
-               // Special-case the kill signal since it doesn't use /proc/$pid/note.
-               return p.Kill()
-       }
        if e := p.writeProcFile("note", sig.String()); e != nil {
                return NewSyscallError("signal", e)
        }
@@ -63,10 +59,7 @@ func (p *Process) signal(sig Signal) error {
 }
 
 func (p *Process) kill() error {
-       if e := p.writeProcFile("ctl", "kill"); e != nil {
-               return NewSyscallError("kill", e)
-       }
-       return nil
+       return p.signal(Kill)
 }
 
 func (p *Process) wait() (ps *ProcessState, err error) {
index 8a4d019d2f748ee4d45a513370b5a59c5a20f628..fb9d291e66453a936beb1fe3c01273c74dd938c1 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.
 
-// +build darwin dragonfly freebsd linux netbsd openbsd solaris windows
+// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows
 
 package os
 
index 3c05b8f080625136d1692f5e4a35b245194afb47..1b1e3350b840c9e498c65778d1afe03457255918 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.
 
-// +build darwin dragonfly freebsd linux netbsd openbsd solaris
+// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
 
 package os
 
@@ -38,6 +38,9 @@ func (p *Process) signal(sig Signal) error {
        if p.done() {
                return errors.New("os: process already finished")
        }
+       if p.Pid == -1 {
+               return errors.New("os: process already released")
+       }
        s, ok := sig.(syscall.Signal)
        if !ok {
                return errors.New("os: unsupported signal type")
index 441ad5384d70a20d3e75a7566789c69ed3d82d61..b3466b15cc8446477bec4ff602df29dd96a53953 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.
 
-// +build darwin dragonfly freebsd linux netbsd openbsd solaris windows
+// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows
 
 package os
 
index 33588421dc0df7f647d821ff636abab0ca7558c1..7959091995f0578cd1d599d85c0aff80b38a0e80 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.
 
-// +build darwin dragonfly freebsd linux netbsd openbsd solaris
+// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
 
 package os
 
@@ -80,12 +80,7 @@ func OpenFile(name string, flag int, perm FileMode) (file *File, err error) {
 
        // There's a race here with fork/exec, which we are
        // content to live with.  See ../syscall/exec_unix.go.
-       // On OS X 10.6, the O_CLOEXEC flag is not respected.
-       // On OS X 10.7, the O_CLOEXEC flag works.
-       // Without a cheap & reliable way to detect 10.6 vs 10.7 at
-       // runtime, we just always call syscall.CloseOnExec on Darwin.
-       // Once >=10.7 is prevalent, this extra call can removed.
-       if syscall.O_CLOEXEC == 0 || runtime.GOOS == "darwin" { // O_CLOEXEC not supported
+       if !supportsCloseOnExec {
                syscall.CloseOnExec(r)
        }
 
@@ -188,16 +183,31 @@ func (f *File) readdir(n int) (fi []FileInfo, err error) {
        return fi, err
 }
 
+// Darwin and FreeBSD can't read or write 2GB+ at a time,
+// even on 64-bit systems. See golang.org/issue/7812.
+// Use 1GB instead of, say, 2GB-1, to keep subsequent
+// reads aligned.
+const (
+       needsMaxRW = runtime.GOOS == "darwin" || runtime.GOOS == "freebsd"
+       maxRW      = 1 << 30
+)
+
 // read reads up to len(b) bytes from the File.
 // It returns the number of bytes read and an error, if any.
 func (f *File) read(b []byte) (n int, err error) {
+       if needsMaxRW && len(b) > maxRW {
+               b = b[:maxRW]
+       }
        return syscall.Read(f.fd, b)
 }
 
 // pread reads len(b) bytes from the File starting at byte offset off.
 // It returns the number of bytes read and the error, if any.
-// EOF is signaled by a zero count with err set to 0.
+// EOF is signaled by a zero count with err set to nil.
 func (f *File) pread(b []byte, off int64) (n int, err error) {
+       if needsMaxRW && len(b) > maxRW {
+               b = b[:maxRW]
+       }
        return syscall.Pread(f.fd, b, off)
 }
 
@@ -205,13 +215,22 @@ func (f *File) pread(b []byte, off int64) (n int, err error) {
 // It returns the number of bytes written and an error, if any.
 func (f *File) write(b []byte) (n int, err error) {
        for {
-               m, err := syscall.Write(f.fd, b)
+               bcap := b
+               if needsMaxRW && len(bcap) > maxRW {
+                       bcap = bcap[:maxRW]
+               }
+               m, err := syscall.Write(f.fd, bcap)
                n += m
 
                // If the syscall wrote some data but not all (short write)
                // or it returned EINTR, then assume it stopped early for
                // reasons that are uninteresting to the caller, and try again.
-               if 0 < m && m < len(b) || err == syscall.EINTR {
+               if 0 < m && m < len(bcap) || err == syscall.EINTR {
+                       b = b[m:]
+                       continue
+               }
+
+               if needsMaxRW && len(bcap) != len(b) && err == nil {
                        b = b[m:]
                        continue
                }
@@ -223,6 +242,9 @@ func (f *File) write(b []byte) (n int, err error) {
 // pwrite writes len(b) bytes to the File starting at byte offset off.
 // It returns the number of bytes written and an error, if any.
 func (f *File) pwrite(b []byte, off int64) (n int, err error) {
+       if needsMaxRW && len(b) > maxRW {
+               b = b[:maxRW]
+       }
        return syscall.Pwrite(f.fd, b, off)
 }
 
index 8c5ff7fca51a53ad64ab7e202e23d3061d095b6a..a72edeaee6e446cff958ef63a3830fd202e09a78 100644 (file)
@@ -22,7 +22,7 @@ var useSyscallwd = func(error) bool { return true }
 // current directory.  If the current directory can be
 // reached via multiple paths (due to symbolic links),
 // Getwd may return any one of them.
-func Getwd() (pwd string, err error) {
+func Getwd() (dir string, err error) {
        // If the operating system provides a Getwd call, use it.
        if syscall.ImplementsGetwd {
                s, e := syscall.Getwd()
@@ -39,22 +39,22 @@ func Getwd() (pwd string, err error) {
 
        // Clumsy but widespread kludge:
        // if $PWD is set and matches ".", use it.
-       pwd = Getenv("PWD")
-       if len(pwd) > 0 && pwd[0] == '/' {
-               d, err := Stat(pwd)
+       dir = Getenv("PWD")
+       if len(dir) > 0 && dir[0] == '/' {
+               d, err := Stat(dir)
                if err == nil && SameFile(dot, d) {
-                       return pwd, nil
+                       return dir, nil
                }
        }
 
        // Apply same kludge but to cached dir instead of $PWD.
        getwdCache.Lock()
-       pwd = getwdCache.dir
+       dir = getwdCache.dir
        getwdCache.Unlock()
-       if len(pwd) > 0 {
-               d, err := Stat(pwd)
+       if len(dir) > 0 {
+               d, err := Stat(dir)
                if err == nil && SameFile(dot, d) {
-                       return pwd, nil
+                       return dir, nil
                }
        }
 
@@ -71,8 +71,8 @@ func Getwd() (pwd string, err error) {
 
        // General algorithm: find name in parent
        // and then find name of parent.  Each iteration
-       // adds /name to the beginning of pwd.
-       pwd = ""
+       // adds /name to the beginning of dir.
+       dir = ""
        for parent := ".."; ; parent = "../" + parent {
                if len(parent) >= 1024 { // Sanity check
                        return "", syscall.ENAMETOOLONG
@@ -91,7 +91,7 @@ func Getwd() (pwd string, err error) {
                        for _, name := range names {
                                d, _ := Lstat(parent + "/" + name)
                                if SameFile(d, dot) {
-                                       pwd = "/" + name + pwd
+                                       dir = "/" + name + dir
                                        goto Found
                                }
                        }
@@ -112,8 +112,8 @@ func Getwd() (pwd string, err error) {
 
        // Save answer as hint to avoid the expensive path next time.
        getwdCache.Lock()
-       getwdCache.dir = pwd
+       getwdCache.dir = dir
        getwdCache.Unlock()
 
-       return pwd, nil
+       return dir, nil
 }
index 0392b06c4094a8cf7c9e5e5ef6f5c26a22414278..a34e328b814c6261dc959425b62a82558f398d33 100644 (file)
@@ -494,10 +494,10 @@ func TestHardLink(t *testing.T) {
        }
 }
 
-func TestSymLink(t *testing.T) {
-       // Symlinks are not supported under windows or Plan 9.
-       if runtime.GOOS == "windows" || runtime.GOOS == "plan9" {
-               return
+func TestSymlink(t *testing.T) {
+       switch runtime.GOOS {
+       case "windows", "plan9", "nacl":
+               t.Skipf("skipping on %s", runtime.GOOS)
        }
        from, to := "symlinktestfrom", "symlinktestto"
        Remove(from) // Just in case.
@@ -557,9 +557,9 @@ func TestSymLink(t *testing.T) {
 }
 
 func TestLongSymlink(t *testing.T) {
-       // Symlinks are not supported under windows or Plan 9.
-       if runtime.GOOS == "windows" || runtime.GOOS == "plan9" {
-               return
+       switch runtime.GOOS {
+       case "windows", "plan9", "nacl":
+               t.Skipf("skipping on %s", runtime.GOOS)
        }
        s := "0123456789abcdef"
        // Long, but not too long: a common limit is 255.
@@ -628,6 +628,10 @@ func exec(t *testing.T, dir, cmd string, args []string, expect string) {
 }
 
 func TestStartProcess(t *testing.T) {
+       if runtime.GOOS == "nacl" {
+               t.Skip("skipping on nacl")
+       }
+
        var dir, cmd string
        var args []string
        if runtime.GOOS == "windows" {
@@ -701,8 +705,10 @@ func TestFTruncate(t *testing.T) {
        checkSize(t, f, 1024)
        f.Truncate(0)
        checkSize(t, f, 0)
-       f.Write([]byte("surprise!"))
-       checkSize(t, f, 13+9) // wrote at offset past where hello, world was.
+       _, err := f.Write([]byte("surprise!"))
+       if err == nil {
+               checkSize(t, f, 13+9) // wrote at offset past where hello, world was.
+       }
 }
 
 func TestTruncate(t *testing.T) {
@@ -719,8 +725,10 @@ func TestTruncate(t *testing.T) {
        checkSize(t, f, 1024)
        Truncate(f.Name(), 0)
        checkSize(t, f, 0)
-       f.Write([]byte("surprise!"))
-       checkSize(t, f, 13+9) // wrote at offset past where hello, world was.
+       _, err := f.Write([]byte("surprise!"))
+       if err == nil {
+               checkSize(t, f, 13+9) // wrote at offset past where hello, world was.
+       }
 }
 
 // Use TempDir() to make sure we're on a local file system,
@@ -755,13 +763,13 @@ func TestChtimes(t *testing.T) {
        }
        postStat := st
 
-       /* Plan 9:
+       /* Plan 9, NaCl:
                Mtime is the time of the last change of content.  Similarly, atime is set whenever the
            contents are accessed; also, it is set whenever mtime is set.
        */
        pat := Atime(postStat)
        pmt := postStat.ModTime()
-       if !pat.Before(at) && runtime.GOOS != "plan9" {
+       if !pat.Before(at) && runtime.GOOS != "plan9" && runtime.GOOS != "nacl" {
                t.Errorf("AccessTime didn't go backwards; was=%d, after=%d", at, pat)
        }
 
@@ -963,8 +971,9 @@ func run(t *testing.T, cmd []string) string {
 func TestHostname(t *testing.T) {
        // There is no other way to fetch hostname on windows, but via winapi.
        // On Plan 9 it is can be taken from #c/sysname as Hostname() does.
-       if runtime.GOOS == "windows" || runtime.GOOS == "plan9" {
-               return
+       switch runtime.GOOS {
+       case "windows", "plan9", "nacl":
+               t.Skipf("skipping on %s", runtime.GOOS)
        }
 
        // Check internal Hostname() against the output of /bin/hostname.
@@ -1224,6 +1233,10 @@ func TestReadAtEOF(t *testing.T) {
 
 func testKillProcess(t *testing.T, processKiller func(p *Process)) {
        t.Skip("gccgo does not have a go command")
+       if runtime.GOOS == "nacl" {
+               t.Skip("skipping on nacl")
+       }
+
        dir, err := ioutil.TempDir("", "go-build")
        if err != nil {
                t.Fatalf("Failed to create temp directory: %v", err)
index 27abf59826f729469869df9a700fee8c3f13ce82..3af21cde9afbf15a2813a8c3d25a20201c592fbf 100644 (file)
@@ -167,8 +167,9 @@ func TestRemoveAll(t *testing.T) {
 }
 
 func TestMkdirAllWithSymlink(t *testing.T) {
-       if runtime.GOOS == "windows" || runtime.GOOS == "plan9" {
-               t.Skip("Skipping test: symlinks don't exist under Windows/Plan 9")
+       switch runtime.GOOS {
+       case "nacl", "plan9", "windows":
+               t.Skipf("skipping on %s", runtime.GOOS)
        }
 
        tmpDir, err := ioutil.TempDir("", "TestMkdirAllWithSymlink-")
index bdf9fe642187b58548a85e414fc8807251faea75..0211107ddfc42d3bb8a7610b043d5edb2ad0ed36 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.
 
-// +build darwin dragonfly freebsd linux netbsd openbsd solaris
+// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
 
 package os
 
index 0ea8e4b1f83cd531219e1085de4e41bf60e0dc6d..3b81ed20f1b72df2d0de968721a92909c187c3e0 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.
 
-// +build darwin dragonfly freebsd netbsd openbsd solaris
+// +build darwin dragonfly freebsd nacl netbsd openbsd solaris
 
 package os
 
index 80dc4304aaa4d625586ecb0c8f35bca5cf21b0e4..94b8ab3ddbf455795b5d222dcc6f1ca71b175e88 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.
 
-// +build darwin dragonfly freebsd linux netbsd openbsd solaris windows
+// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows
 
 package signal
 
diff --git a/libgo/go/os/stat_nacl.go b/libgo/go/os/stat_nacl.go
new file mode 100644 (file)
index 0000000..a503b59
--- /dev/null
@@ -0,0 +1,62 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package os
+
+import (
+       "syscall"
+       "time"
+)
+
+func sameFile(fs1, fs2 *fileStat) bool {
+       stat1 := fs1.sys.(*syscall.Stat_t)
+       stat2 := fs2.sys.(*syscall.Stat_t)
+       return stat1.Dev == stat2.Dev && stat1.Ino == stat2.Ino
+}
+
+func fileInfoFromStat(st *syscall.Stat_t, name string) FileInfo {
+       fs := &fileStat{
+               name:    basename(name),
+               size:    int64(st.Size),
+               modTime: timespecToTime(st.Mtime, st.MtimeNsec),
+               sys:     st,
+       }
+       fs.mode = FileMode(st.Mode & 0777)
+       switch st.Mode & syscall.S_IFMT {
+       case syscall.S_IFBLK:
+               fs.mode |= ModeDevice
+       case syscall.S_IFCHR:
+               fs.mode |= ModeDevice | ModeCharDevice
+       case syscall.S_IFDIR:
+               fs.mode |= ModeDir
+       case syscall.S_IFIFO:
+               fs.mode |= ModeNamedPipe
+       case syscall.S_IFLNK:
+               fs.mode |= ModeSymlink
+       case syscall.S_IFREG:
+               // nothing to do
+       case syscall.S_IFSOCK:
+               fs.mode |= ModeSocket
+       }
+       if st.Mode&syscall.S_ISGID != 0 {
+               fs.mode |= ModeSetgid
+       }
+       if st.Mode&syscall.S_ISUID != 0 {
+               fs.mode |= ModeSetuid
+       }
+       if st.Mode&syscall.S_ISVTX != 0 {
+               fs.mode |= ModeSticky
+       }
+       return fs
+}
+
+func timespecToTime(sec, nsec int64) time.Time {
+       return time.Unix(sec, nsec)
+}
+
+// For testing.
+func atime(fi FileInfo) time.Time {
+       st := fi.Sys().(*syscall.Stat_t)
+       return timespecToTime(st.Atime, st.AtimeNsec)
+}
index 9ad2f8546b99f2242f4f01403a0afb96008a3121..8ad5e21837100c395e3cc4cadb6cd70f00291d3e 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.
 
-// +build darwin dragonfly freebsd netbsd openbsd
+// +build darwin dragonfly freebsd nacl netbsd openbsd
 
 // os code shared between *BSD systems including OS X (Darwin)
 // and FreeBSD.
diff --git a/libgo/go/os/sys_darwin.go b/libgo/go/os/sys_darwin.go
new file mode 100644 (file)
index 0000000..7a8330a
--- /dev/null
@@ -0,0 +1,31 @@
+// Copyright 2014 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 os
+
+import "syscall"
+
+// supportsCloseOnExec reports whether the platform supports the
+// O_CLOEXEC flag.
+var supportsCloseOnExec bool
+
+func init() {
+       // Seems like kern.osreldate is veiled on latest OS X. We use
+       // kern.osrelease instead.
+       osver, err := syscall.Sysctl("kern.osrelease")
+       if err != nil {
+               return
+       }
+       var i int
+       for i = range osver {
+               if osver[i] != '.' {
+                       continue
+               }
+       }
+       // The O_CLOEXEC flag was introduced in OS X 10.7 (Darwin
+       // 11.0.0). See http://support.apple.com/kb/HT1633.
+       if i > 2 || i == 2 && osver[0] >= '1' && osver[1] >= '1' {
+               supportsCloseOnExec = true
+       }
+}
diff --git a/libgo/go/os/sys_freebsd.go b/libgo/go/os/sys_freebsd.go
new file mode 100644 (file)
index 0000000..273c2df
--- /dev/null
@@ -0,0 +1,23 @@
+// Copyright 2014 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 os
+
+import "syscall"
+
+// supportsCloseOnExec reports whether the platform supports the
+// O_CLOEXEC flag.
+var supportsCloseOnExec bool
+
+func init() {
+       osrel, err := syscall.SysctlUint32("kern.osreldate")
+       if err != nil {
+               return
+       }
+       // The O_CLOEXEC flag was introduced in FreeBSD 8.3.
+       // See http://www.freebsd.org/doc/en/books/porters-handbook/freebsd-versions.html.
+       if osrel >= 803000 {
+               supportsCloseOnExec = true
+       }
+}
diff --git a/libgo/go/os/sys_nacl.go b/libgo/go/os/sys_nacl.go
new file mode 100644 (file)
index 0000000..07907c8
--- /dev/null
@@ -0,0 +1,9 @@
+// Copyright 2014 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 os
+
+// supportsCloseOnExec reports whether the platform supports the
+// O_CLOEXEC flag.
+const supportsCloseOnExec = false
diff --git a/libgo/go/os/sys_unix.go b/libgo/go/os/sys_unix.go
new file mode 100644 (file)
index 0000000..39c20dc
--- /dev/null
@@ -0,0 +1,11 @@
+// Copyright 2014 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 dragonfly linux netbsd openbsd solaris
+
+package os
+
+// supportsCloseOnExec reports whether the platform supports the
+// O_CLOEXEC flag.
+const supportsCloseOnExec = true
index 3d84145d7f892574c1e132719221ca60f66fa5ff..a9bcc103c5512ffb30d288df10bd557735c58c67 100644 (file)
@@ -230,7 +230,7 @@ func getEsc(chunk string) (r rune, nchunk string, err error) {
 //
 func Glob(pattern string) (matches []string, err error) {
        if !hasMeta(pattern) {
-               if _, err = os.Stat(pattern); err != nil {
+               if _, err = os.Lstat(pattern); err != nil {
                        return nil, nil
                }
                return []string{pattern}, nil
index 6a2fd927e411f662b22c67672a252bafbe6c5bbe..9886620ade00c7d9f5dd29f11beb0a5abf92f48a 100644 (file)
@@ -2,9 +2,12 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package filepath
+package filepath_test
 
 import (
+       "io/ioutil"
+       "os"
+       . "path/filepath"
        "runtime"
        "strings"
        "testing"
@@ -154,3 +157,51 @@ func TestGlobError(t *testing.T) {
                t.Error("expected error for bad pattern; got none")
        }
 }
+
+var globSymlinkTests = []struct {
+       path, dest string
+       brokenLink bool
+}{
+       {"test1", "link1", false},
+       {"test2", "link2", true},
+}
+
+func TestGlobSymlink(t *testing.T) {
+       switch runtime.GOOS {
+       case "nacl", "plan9", "windows":
+               t.Skipf("skipping on %s", runtime.GOOS)
+       }
+
+       tmpDir, err := ioutil.TempDir("", "globsymlink")
+       if err != nil {
+               t.Fatal("creating temp dir:", err)
+       }
+       defer os.RemoveAll(tmpDir)
+
+       for _, tt := range globSymlinkTests {
+               path := Join(tmpDir, tt.path)
+               dest := Join(tmpDir, tt.dest)
+               f, err := os.Create(path)
+               if err != nil {
+                       t.Fatal(err)
+               }
+               if err := f.Close(); err != nil {
+                       t.Fatal(err)
+               }
+               err = os.Symlink(path, dest)
+               if err != nil {
+                       t.Fatal(err)
+               }
+               if tt.brokenLink {
+                       // Break the symlink.
+                       os.Remove(path)
+               }
+               matches, err := Glob(dest)
+               if err != nil {
+                       t.Errorf("GlobSymlink error for %q: %s", dest, err)
+               }
+               if !contains(matches, dest) {
+                       t.Errorf("Glob(%#q) = %#v want %v", dest, matches, dest)
+               }
+       }
+}
index 65d29bf9f9db6f149393501cc800c1e48b2ba951..71603cc5946ade3dd91dfc2aa4cb90e4b6cab23b 100644 (file)
@@ -67,7 +67,7 @@ const (
 //        along with the non-.. element that precedes it.
 //     4. Eliminate .. elements that begin a rooted path:
 //        that is, replace "/.." by "/" at the beginning of a path,
-//         assuming Separator is '/'.
+//        assuming Separator is '/'.
 //
 // The returned path ends in a slash only if it represents a root directory,
 // such as "/" on Unix or `C:\` on Windows.
index dc87d791037ae9746298e77632f7c69dc4529bde..6d1139432c355caa00e672c171172cb035e07feb 100644 (file)
@@ -694,8 +694,9 @@ func simpleJoin(dir, path string) string {
 }
 
 func TestEvalSymlinks(t *testing.T) {
-       if runtime.GOOS == "plan9" {
-               t.Skip("Skipping test: symlinks don't exist under Plan 9")
+       switch runtime.GOOS {
+       case "nacl", "plan9":
+               t.Skipf("skipping on %s", runtime.GOOS)
        }
 
        tmpDir, err := ioutil.TempDir("", "evalsymlink")
index 2be675c3af1dc0b89765a5787507c67c7394903e..7aba0ab5b9bb2e09488612c8236a91859c0157ef 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.
 
-// +build darwin dragonfly freebsd linux netbsd openbsd solaris
+// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
 
 package filepath
 
index 6c015ad029fc244d8521f5dd2b392eb6cc89daba..799bbea4a8a9012ad846dd97013f42d80822398f 100644 (file)
@@ -15,6 +15,7 @@ import (
        . "reflect"
        "runtime"
        "sort"
+       "strings"
        "sync"
        "testing"
        "time"
@@ -972,6 +973,31 @@ func TestMap(t *testing.T) {
        }
 }
 
+func TestNilMap(t *testing.T) {
+       var m map[string]int
+       mv := ValueOf(m)
+       keys := mv.MapKeys()
+       if len(keys) != 0 {
+               t.Errorf(">0 keys for nil map: %v", keys)
+       }
+
+       // Check that value for missing key is zero.
+       x := mv.MapIndex(ValueOf("hello"))
+       if x.Kind() != Invalid {
+               t.Errorf("m.MapIndex(\"hello\") for nil map = %v, want Invalid Value", x)
+       }
+
+       // Check big value too.
+       var mbig map[string][10 << 20]byte
+       x = ValueOf(mbig).MapIndex(ValueOf("hello"))
+       if x.Kind() != Invalid {
+               t.Errorf("mbig.MapIndex(\"hello\") for nil map = %v, want Invalid Value", x)
+       }
+
+       // Test that deletes from a nil map succeed.
+       mv.SetMapIndex(ValueOf("hi"), Value{})
+}
+
 func TestChan(t *testing.T) {
        for loop := 0; loop < 2; loop++ {
                var c chan int
@@ -1523,6 +1549,23 @@ func TestMakeFuncInterface(t *testing.T) {
        }
 }
 
+func TestMakeFuncVariadic(t *testing.T) {
+       // Test that variadic arguments are packed into a slice and passed as last arg
+       fn := func(_ int, is ...int) []int { return nil }
+       fv := MakeFunc(TypeOf(fn), func(in []Value) []Value { return in[1:2] })
+       ValueOf(&fn).Elem().Set(fv)
+
+       r := fv.Call([]Value{ValueOf(1), ValueOf(2), ValueOf(3)})[0].Interface().([]int)
+       if r[0] != 2 || r[1] != 3 {
+               t.Errorf("Call returned [%v, %v]; want 2, 3", r[0], r[1])
+       }
+
+       r = fv.CallSlice([]Value{ValueOf(1), ValueOf([]int{2, 3})})[0].Interface().([]int)
+       if r[0] != 2 || r[1] != 3 {
+               t.Errorf("Call returned [%v, %v]; want 2, 3", r[0], r[1])
+       }
+}
+
 type Point struct {
        x, y int
 }
@@ -3723,3 +3766,107 @@ func TestBigZero(t *testing.T) {
                }
        }
 }
+
+func TestFieldByIndexNil(t *testing.T) {
+       type P struct {
+               F int
+       }
+       type T struct {
+               *P
+       }
+       v := ValueOf(T{})
+
+       v.FieldByName("P") // should be fine
+
+       defer func() {
+               if err := recover(); err == nil {
+                       t.Fatalf("no error")
+               } else if !strings.Contains(fmt.Sprint(err), "nil pointer to embedded struct") {
+                       t.Fatalf(`err=%q, wanted error containing "nil pointer to embedded struct"`, err)
+               }
+       }()
+       v.FieldByName("F") // should panic
+
+       t.Fatalf("did not panic")
+}
+
+// Given
+//     type Outer struct {
+//             *Inner
+//             ...
+//     }
+// the compiler generates the implementation of (*Outer).M dispatching to the embedded Inner.
+// The implementation is logically:
+//     func (p *Outer) M() {
+//             (p.Inner).M()
+//     }
+// but since the only change here is the replacement of one pointer receiver with another,
+// the actual generated code overwrites the original receiver with the p.Inner pointer and
+// then jumps to the M method expecting the *Inner receiver.
+//
+// During reflect.Value.Call, we create an argument frame and the associated data structures
+// to describe it to the garbage collector, populate the frame, call reflect.call to
+// run a function call using that frame, and then copy the results back out of the frame.
+// The reflect.call function does a memmove of the frame structure onto the
+// stack (to set up the inputs), runs the call, and the memmoves the stack back to
+// the frame structure (to preserve the outputs).
+//
+// Originally reflect.call did not distinguish inputs from outputs: both memmoves
+// were for the full stack frame. However, in the case where the called function was
+// one of these wrappers, the rewritten receiver is almost certainly a different type
+// than the original receiver. This is not a problem on the stack, where we use the
+// program counter to determine the type information and understand that
+// during (*Outer).M the receiver is an *Outer while during (*Inner).M the receiver in the same
+// memory word is now an *Inner. But in the statically typed argument frame created
+// by reflect, the receiver is always an *Outer. Copying the modified receiver pointer
+// off the stack into the frame will store an *Inner there, and then if a garbage collection
+// happens to scan that argument frame before it is discarded, it will scan the *Inner
+// memory as if it were an *Outer. If the two have different memory layouts, the
+// collection will intepret the memory incorrectly.
+//
+// One such possible incorrect interpretation is to treat two arbitrary memory words
+// (Inner.P1 and Inner.P2 below) as an interface (Outer.R below). Because interpreting
+// an interface requires dereferencing the itab word, the misinterpretation will try to
+// deference Inner.P1, causing a crash during garbage collection.
+//
+// This came up in a real program in issue 7725.
+
+type Outer struct {
+       *Inner
+       R io.Reader
+}
+
+type Inner struct {
+       X  *Outer
+       P1 uintptr
+       P2 uintptr
+}
+
+func (pi *Inner) M() {
+       // Clear references to pi so that the only way the
+       // garbage collection will find the pointer is in the
+       // argument frame, typed as a *Outer.
+       pi.X.Inner = nil
+
+       // Set up an interface value that will cause a crash.
+       // P1 = 1 is a non-zero, so the interface looks non-nil.
+       // P2 = pi ensures that the data word points into the
+       // allocated heap; if not the collection skips the interface
+       // value as irrelevant, without dereferencing P1.
+       pi.P1 = 1
+       pi.P2 = uintptr(unsafe.Pointer(pi))
+}
+
+func TestCallMethodJump(t *testing.T) {
+       // In reflect.Value.Call, trigger a garbage collection after reflect.call
+       // returns but before the args frame has been discarded.
+       // This is a little clumsy but makes the failure repeatable.
+       *CallGC = true
+
+       p := &Outer{Inner: new(Inner)}
+       p.Inner.X = p
+       ValueOf(p).Method(0).Call(nil)
+
+       // Stop garbage collecting during reflect.call.
+       *CallGC = false
+}
index cd8cf2cf2c98b98768dddad971348839fbb855b6..0778ad37f5c0526ba4026df49901d167bd0c5a2f 100644 (file)
@@ -16,3 +16,4 @@ func IsRO(v Value) bool {
 }
 
 var ArrayOf = arrayOf
+var CallGC = &callGC
index b248743de70e247a43d8385ad8c17d99ad7a13af..a46e1d8649798eef886079f182f2b4681a0ab2a7 100644 (file)
@@ -113,7 +113,7 @@ func makeMethodValue(op string, v Value) Value {
        // Cause panic if method is not appropriate.
        // The panic would still happen during the call if we omit this,
        // but we want Interface() and other operations to fail early.
-       t, _ := methodReceiver(op, rcvr, int(v.flag)>>flagMethodShift)
+       _, t, _ := methodReceiver(op, rcvr, int(v.flag)>>flagMethodShift)
 
        fv := &makeFuncImpl{
                code:   code,
@@ -167,17 +167,9 @@ func (c *makeFuncImpl) call(in []Value) []Value {
        if c.method == -1 {
                return c.fn(in)
        } else if c.method == -2 {
-               if c.typ.IsVariadic() {
-                       return c.rcvr.CallSlice(in)
-               } else {
-                       return c.rcvr.Call(in)
-               }
+               return c.rcvr.Call(in)
        } else {
                m := c.rcvr.Method(c.method)
-               if c.typ.IsVariadic() {
-                       return m.CallSlice(in)
-               } else {
-                       return m.Call(in)
-               }
+               return m.Call(in)
        }
 }
index f8e2c59b614db17f8f46f2b1d26b4d9d1c78d6c3..74cf2946a016cbb80590c747f7bdb8c8b252dae2 100644 (file)
@@ -16,6 +16,7 @@
 package reflect
 
 import (
+       "runtime"
        "strconv"
        "sync"
        "unsafe"
@@ -1519,6 +1520,13 @@ func MapOf(key, elem Type) Type {
        mt.uncommonType = nil
        mt.ptrToThis = nil
        mt.zero = unsafe.Pointer(&make([]byte, mt.size)[0])
+       // mt.gc = unsafe.Pointer(&ptrGC{
+       //      width:  unsafe.Sizeof(uintptr(0)),
+       //      op:     _GC_PTR,
+       //      off:    0,
+       //      elemgc: mt.hmap.gc,
+       //      end:    _GC_END,
+       // })
 
        // INCORRECT. Uncomment to check that TestMapOfGC and TestMapOfGCValues
        // fail when mt.gc is wrong.
@@ -1551,6 +1559,10 @@ func bucketOf(ktyp, etyp *rtype) *rtype {
        gc = append(gc, _GC_PTR, offset, 0 /*self pointer set below*/) // overflow
        offset += ptrsize
 
+       if runtime.GOARCH == "amd64p32" {
+               offset += 4
+       }
+
        // keys
        if ktyp.kind&kindNoPointers == 0 {
                gc = append(gc, _GC_ARRAY_START, offset, _BUCKETSIZE, ktyp.size)
index 64081b9ea515567e5bccd4dd7ff931def396561d..cac083304ca0fad51aa601cd0ff64f123897bfba 100644 (file)
@@ -425,6 +425,8 @@ func (v Value) CallSlice(in []Value) []Value {
        return v.call("CallSlice", in)
 }
 
+var callGC bool // for testing; see TestCallMethodJump
+
 var makeFuncStubFn = makeFuncStub
 var makeFuncStubCode = **(**uintptr)(unsafe.Pointer(&makeFuncStubFn))
 
@@ -437,9 +439,8 @@ func (v Value) call(op string, in []Value) []Value {
                rcvrtype *rtype
        )
        if v.flag&flagMethod != 0 {
-               rcvrtype = t
                rcvr = v
-               t, fn = methodReceiver(op, v, int(v.flag)>>flagMethodShift)
+               rcvrtype, t, fn = methodReceiver(op, v, int(v.flag)>>flagMethodShift)
        } else if v.flag&flagIndir != 0 {
                fn = *(*unsafe.Pointer)(v.ptr)
        } else {
@@ -450,17 +451,6 @@ func (v Value) call(op string, in []Value) []Value {
                panic("reflect.Value.Call: call of nil function")
        }
 
-       // If target is makeFuncStub, short circuit the unpack onto stack /
-       // pack back into []Value for the args and return values.  Just do the
-       // call directly.
-       // We need to do this here because otherwise we have a situation where
-       // reflect.callXX calls makeFuncStub, neither of which knows the
-       // layout of the args.  That's bad for precise gc & stack copying.
-       x := (*makeFuncImpl)(fn)
-       if x.code == makeFuncStubCode {
-               return x.call(in)
-       }
-
        isSlice := op == "CallSlice"
        n := t.NumIn()
        if isSlice {
@@ -518,6 +508,17 @@ func (v Value) call(op string, in []Value) []Value {
        }
        nout := t.NumOut()
 
+       // If target is makeFuncStub, short circuit the unpack onto stack /
+       // pack back into []Value for the args and return values.  Just do the
+       // call directly.
+       // We need to do this here because otherwise we have a situation where
+       // reflect.callXX calls makeFuncStub, neither of which knows the
+       // layout of the args.  That's bad for precise gc & stack copying.
+       x := (*makeFuncImpl)(fn)
+       if x.code == makeFuncStubCode {
+               return x.call(in)
+       }
+
        if v.flag&flagMethod != 0 {
                nin++
        }
@@ -575,6 +576,11 @@ func (v Value) call(op string, in []Value) []Value {
 
        call(t, fn, v.flag&flagMethod != 0, firstPointer, pp, pr)
 
+       // For testing; see TestCallMethodJump.
+       if callGC {
+               runtime.GC()
+       }
+
        return ret
 }
 
@@ -582,9 +588,10 @@ func (v Value) call(op string, in []Value) []Value {
 // described by v. The Value v may or may not have the
 // flagMethod bit set, so the kind cached in v.flag should
 // not be used.
+// The return value rcvrtype gives the method's actual receiver type.
 // The return value t gives the method type signature (without the receiver).
 // The return value fn is a pointer to the method code.
-func methodReceiver(op string, v Value, methodIndex int) (t *rtype, fn unsafe.Pointer) {
+func methodReceiver(op string, v Value, methodIndex int) (rcvrtype, t *rtype, fn unsafe.Pointer) {
        i := methodIndex
        if v.typ.Kind() == Interface {
                tt := (*interfaceType)(unsafe.Pointer(v.typ))
@@ -599,9 +606,11 @@ func methodReceiver(op string, v Value, methodIndex int) (t *rtype, fn unsafe.Po
                if iface.itab == nil {
                        panic("reflect: " + op + " of method on nil interface value")
                }
+               rcvrtype = iface.itab.typ
                fn = unsafe.Pointer(&iface.itab.fun[i])
                t = m.typ
        } else {
+               rcvrtype = v.typ
                ut := v.typ.uncommon()
                if ut == nil || i < 0 || i >= len(ut.methods) {
                        panic("reflect: internal error: invalid method index")
@@ -786,7 +795,10 @@ func (v Value) FieldByIndex(index []int) Value {
        v.mustBe(Struct)
        for i, x := range index {
                if i > 0 {
-                       if v.Kind() == Ptr && v.Elem().Kind() == Struct {
+                       if v.Kind() == Ptr && v.typ.Elem().Kind() == Struct {
+                               if v.IsNil() {
+                                       panic("reflect: indirection through nil pointer to embedded struct")
+                               }
                                v = v.Elem()
                        }
                }
@@ -1516,6 +1528,7 @@ func (v Value) SetCap(n int) {
 // SetMapIndex sets the value associated with key in the map v to val.
 // It panics if v's Kind is not Map.
 // If val is the zero Value, SetMapIndex deletes the key from the map.
+// Otherwise if v holds a nil map, SetMapIndex will panic.
 // As in Go, key's value must be assignable to the map's key type,
 // and val's value must be assignable to the map's value type.
 func (v Value) SetMapIndex(key, val Value) {
@@ -2198,7 +2211,7 @@ func Zero(typ Type) Value {
 }
 
 // New returns a Value representing a pointer to a new zero value
-// for the specified type.  That is, the returned Value's Type is PtrTo(t).
+// for the specified type.  That is, the returned Value's Type is PtrTo(typ).
 func New(typ Type) Value {
        if typ == nil {
                panic("reflect: New(nil)")
index e914a7ccb48b191ba6699cf3858ca1ba2dd95fbd..301a1dfcd8348f8cf3929097c49f8013ecb97225 100644 (file)
@@ -473,6 +473,11 @@ func TestSplit(t *testing.T) {
        }
 }
 
+// This ran out of stack before issue 7608 was fixed.
+func TestOnePassCutoff(t *testing.T) {
+       MustCompile(`^(?:x{1,1000}){1,1000}$`)
+}
+
 func BenchmarkLiteral(b *testing.B) {
        x := strings.Repeat("x", 50) + "y"
        b.StopTimer()
@@ -578,3 +583,63 @@ func BenchmarkAnchoredLongMatch(b *testing.B) {
                re.Match(x)
        }
 }
+
+func BenchmarkOnePassShortA(b *testing.B) {
+       b.StopTimer()
+       x := []byte("abcddddddeeeededd")
+       re := MustCompile("^.bc(d|e)*$")
+       b.StartTimer()
+       for i := 0; i < b.N; i++ {
+               re.Match(x)
+       }
+}
+
+func BenchmarkNotOnePassShortA(b *testing.B) {
+       b.StopTimer()
+       x := []byte("abcddddddeeeededd")
+       re := MustCompile(".bc(d|e)*$")
+       b.StartTimer()
+       for i := 0; i < b.N; i++ {
+               re.Match(x)
+       }
+}
+
+func BenchmarkOnePassShortB(b *testing.B) {
+       b.StopTimer()
+       x := []byte("abcddddddeeeededd")
+       re := MustCompile("^.bc(?:d|e)*$")
+       b.StartTimer()
+       for i := 0; i < b.N; i++ {
+               re.Match(x)
+       }
+}
+
+func BenchmarkNotOnePassShortB(b *testing.B) {
+       b.StopTimer()
+       x := []byte("abcddddddeeeededd")
+       re := MustCompile(".bc(?:d|e)*$")
+       b.StartTimer()
+       for i := 0; i < b.N; i++ {
+               re.Match(x)
+       }
+}
+
+func BenchmarkOnePassLongPrefix(b *testing.B) {
+       b.StopTimer()
+       x := []byte("abcdefghijklmnopqrstuvwxyz")
+       re := MustCompile("^abcdefghijklmnopqrstuvwxyz.*$")
+       b.StartTimer()
+       for i := 0; i < b.N; i++ {
+               re.Match(x)
+       }
+}
+
+func BenchmarkOnePassLongNotPrefix(b *testing.B) {
+       b.StopTimer()
+       x := []byte("abcdefghijklmnopqrstuvwxyz")
+       re := MustCompile("^.bcdefghijklmnopqrstuvwxyz.*$")
+       b.StartTimer()
+       for i := 0; i < b.N; i++ {
+               re.Match(x)
+       }
+}
index 333ca25542832c2fa67fe018e60ee284057ea25b..c4cb201f64251402978118a6f1b6d831954fe412 100644 (file)
@@ -37,6 +37,7 @@ type thread struct {
 type machine struct {
        re       *Regexp      // corresponding Regexp
        p        *syntax.Prog // compiled program
+       op       *onePassProg // compiled onepass program, or notOnePass
        q0, q1   queue        // two queues for runq, nextq
        pool     []*thread    // pool of available threads
        matched  bool         // whether a match was found
@@ -66,8 +67,8 @@ func (m *machine) newInputReader(r io.RuneReader) input {
 }
 
 // progMachine returns a new machine running the prog p.
-func progMachine(p *syntax.Prog) *machine {
-       m := &machine{p: p}
+func progMachine(p *syntax.Prog, op *onePassProg) *machine {
+       m := &machine{p: p, op: op}
        n := len(m.p.Inst)
        m.q0 = queue{make([]uint32, n), make([]entry, 0, n)}
        m.q1 = queue{make([]uint32, n), make([]entry, 0, n)}
@@ -312,6 +313,105 @@ func (m *machine) add(q *queue, pc uint32, pos int, cap []int, cond syntax.Empty
        return t
 }
 
+// onepass runs the machine over the input starting at pos.
+// It reports whether a match was found.
+// If so, m.matchcap holds the submatch information.
+func (m *machine) onepass(i input, pos int) bool {
+       startCond := m.re.cond
+       if startCond == ^syntax.EmptyOp(0) { // impossible
+               return false
+       }
+       m.matched = false
+       for i := range m.matchcap {
+               m.matchcap[i] = -1
+       }
+       r, r1 := endOfText, endOfText
+       width, width1 := 0, 0
+       r, width = i.step(pos)
+       if r != endOfText {
+               r1, width1 = i.step(pos + width)
+       }
+       var flag syntax.EmptyOp
+       if pos == 0 {
+               flag = syntax.EmptyOpContext(-1, r)
+       } else {
+               flag = i.context(pos)
+       }
+       pc := m.op.Start
+       inst := m.op.Inst[pc]
+       // If there is a simple literal prefix, skip over it.
+       if pos == 0 && syntax.EmptyOp(inst.Arg)&^flag == 0 &&
+               len(m.re.prefix) > 0 && i.canCheckPrefix() {
+               // Match requires literal prefix; fast search for it.
+               if i.hasPrefix(m.re) {
+                       pos += len(m.re.prefix)
+                       r, width = i.step(pos)
+                       r1, width1 = i.step(pos + width)
+                       flag = i.context(pos)
+                       pc = int(m.re.prefixEnd)
+               } else {
+                       return m.matched
+               }
+       }
+       for {
+               inst = m.op.Inst[pc]
+               pc = int(inst.Out)
+               switch inst.Op {
+               default:
+                       panic("bad inst")
+               case syntax.InstMatch:
+                       m.matched = true
+                       if len(m.matchcap) > 0 {
+                               m.matchcap[0] = 0
+                               m.matchcap[1] = pos
+                       }
+                       return m.matched
+               case syntax.InstRune:
+                       if !inst.MatchRune(r) {
+                               return m.matched
+                       }
+               case syntax.InstRune1:
+                       if r != inst.Rune[0] {
+                               return m.matched
+                       }
+               case syntax.InstRuneAny:
+                       // Nothing
+               case syntax.InstRuneAnyNotNL:
+                       if r == '\n' {
+                               return m.matched
+                       }
+               // peek at the input rune to see which branch of the Alt to take
+               case syntax.InstAlt, syntax.InstAltMatch:
+                       pc = int(onePassNext(&inst, r))
+                       continue
+               case syntax.InstFail:
+                       return m.matched
+               case syntax.InstNop:
+                       continue
+               case syntax.InstEmptyWidth:
+                       if syntax.EmptyOp(inst.Arg)&^flag != 0 {
+                               return m.matched
+                       }
+                       continue
+               case syntax.InstCapture:
+                       if int(inst.Arg) < len(m.matchcap) {
+                               m.matchcap[inst.Arg] = pos
+                       }
+                       continue
+               }
+               if width == 0 {
+                       break
+               }
+               flag = syntax.EmptyOpContext(r, r1)
+               pos += width
+               r, width = r1, width1
+               if r != endOfText {
+                       r1, width1 = i.step(pos + width)
+               }
+       }
+       return m.matched
+}
+
 // empty is a non-nil 0-element slice,
 // so doExecute can avoid an allocation
 // when 0 captures are requested from a successful match.
@@ -329,16 +429,23 @@ func (re *Regexp) doExecute(r io.RuneReader, b []byte, s string, pos int, ncap i
        } else {
                i = m.newInputString(s)
        }
-       m.init(ncap)
-       if !m.match(i, pos) {
-               re.put(m)
-               return nil
+       if m.op != notOnePass {
+               if !m.onepass(i, pos) {
+                       re.put(m)
+                       return nil
+               }
+       } else {
+               m.init(ncap)
+               if !m.match(i, pos) {
+                       re.put(m)
+                       return nil
+               }
        }
        if ncap == 0 {
                re.put(m)
                return empty // empty but not nil
        }
-       cap := make([]int, ncap)
+       cap := make([]int, len(m.matchcap))
        copy(cap, m.matchcap)
        re.put(m)
        return cap
diff --git a/libgo/go/regexp/onepass.go b/libgo/go/regexp/onepass.go
new file mode 100644 (file)
index 0000000..501fb28
--- /dev/null
@@ -0,0 +1,582 @@
+// Copyright 2014 The Go Authors.  All rights reserved.
+
+package regexp
+
+import (
+       "bytes"
+       "regexp/syntax"
+       "sort"
+       "unicode"
+)
+
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// "One-pass" regexp execution.
+// Some regexps can be analyzed to determine that they never need
+// backtracking: they are guaranteed to run in one pass over the string
+// without bothering to save all the usual NFA state.
+// Detect those and execute them more quickly.
+
+// A onePassProg is a compiled one-pass regular expression program.
+// It is the same as syntax.Prog except for the use of onePassInst.
+type onePassProg struct {
+       Inst   []onePassInst
+       Start  int // index of start instruction
+       NumCap int // number of InstCapture insts in re
+}
+
+// A onePassInst is a single instruction in a one-pass regular expression program.
+// It is the same as syntax.Inst except for the new 'Next' field.
+type onePassInst struct {
+       syntax.Inst
+       Next []uint32
+}
+
+// OnePassPrefix returns a literal string that all matches for the
+// regexp must start with.  Complete is true if the prefix
+// is the entire match. Pc is the index of the last rune instruction
+// in the string. The OnePassPrefix skips over the mandatory
+// EmptyBeginText
+func onePassPrefix(p *syntax.Prog) (prefix string, complete bool, pc uint32) {
+       i := &p.Inst[p.Start]
+       if i.Op != syntax.InstEmptyWidth || (syntax.EmptyOp(i.Arg))&syntax.EmptyBeginText == 0 {
+               return "", i.Op == syntax.InstMatch, uint32(p.Start)
+       }
+       pc = i.Out
+       i = &p.Inst[pc]
+       for i.Op == syntax.InstNop {
+               pc = i.Out
+               i = &p.Inst[pc]
+       }
+       // Avoid allocation of buffer if prefix is empty.
+       if iop(i) != syntax.InstRune || len(i.Rune) != 1 {
+               return "", i.Op == syntax.InstMatch, uint32(p.Start)
+       }
+
+       // Have prefix; gather characters.
+       var buf bytes.Buffer
+       for iop(i) == syntax.InstRune && len(i.Rune) == 1 && syntax.Flags(i.Arg)&syntax.FoldCase == 0 {
+               buf.WriteRune(i.Rune[0])
+               pc, i = i.Out, &p.Inst[i.Out]
+       }
+       return buf.String(), i.Op == syntax.InstEmptyWidth && (syntax.EmptyOp(i.Arg))&syntax.EmptyBeginText != 0, pc
+}
+
+// OnePassNext selects the next actionable state of the prog, based on the input character.
+// It should only be called when i.Op == InstAlt or InstAltMatch, and from the one-pass machine.
+// One of the alternates may ultimately lead without input to end of line. If the instruction
+// is InstAltMatch the path to the InstMatch is in i.Out, the normal node in i.Next.
+func onePassNext(i *onePassInst, r rune) uint32 {
+       next := i.MatchRunePos(r)
+       if next >= 0 {
+               return i.Next[next]
+       }
+       if i.Op == syntax.InstAltMatch {
+               return i.Out
+       }
+       return 0
+}
+
+func iop(i *syntax.Inst) syntax.InstOp {
+       op := i.Op
+       switch op {
+       case syntax.InstRune1, syntax.InstRuneAny, syntax.InstRuneAnyNotNL:
+               op = syntax.InstRune
+       }
+       return op
+}
+
+// Sparse Array implementation is used as a queueOnePass.
+type queueOnePass struct {
+       sparse          []uint32
+       dense           []uint32
+       size, nextIndex uint32
+}
+
+func (q *queueOnePass) empty() bool {
+       return q.nextIndex >= q.size
+}
+
+func (q *queueOnePass) next() (n uint32) {
+       n = q.dense[q.nextIndex]
+       q.nextIndex++
+       return
+}
+
+func (q *queueOnePass) clear() {
+       q.size = 0
+       q.nextIndex = 0
+}
+
+func (q *queueOnePass) reset() {
+       q.nextIndex = 0
+}
+
+func (q *queueOnePass) contains(u uint32) bool {
+       if u >= uint32(len(q.sparse)) {
+               return false
+       }
+       return q.sparse[u] < q.size && q.dense[q.sparse[u]] == u
+}
+
+func (q *queueOnePass) insert(u uint32) {
+       if !q.contains(u) {
+               q.insertNew(u)
+       }
+}
+
+func (q *queueOnePass) insertNew(u uint32) {
+       if u >= uint32(len(q.sparse)) {
+               return
+       }
+       q.sparse[u] = q.size
+       q.dense[q.size] = u
+       q.size++
+}
+
+func newQueue(size int) (q *queueOnePass) {
+       return &queueOnePass{
+               sparse: make([]uint32, size),
+               dense:  make([]uint32, size),
+       }
+}
+
+// mergeRuneSets merges two non-intersecting runesets, and returns the merged result,
+// and a NextIp array. The idea is that if a rune matches the OnePassRunes at index
+// i, NextIp[i/2] is the target. If the input sets intersect, an empty runeset and a
+// NextIp array with the single element mergeFailed is returned.
+// The code assumes that both inputs contain ordered and non-intersecting rune pairs.
+const mergeFailed = uint32(0xffffffff)
+
+var (
+       noRune = []rune{}
+       noNext = []uint32{mergeFailed}
+)
+
+func mergeRuneSets(leftRunes, rightRunes *[]rune, leftPC, rightPC uint32) ([]rune, []uint32) {
+       leftLen := len(*leftRunes)
+       rightLen := len(*rightRunes)
+       if leftLen&0x1 != 0 || rightLen&0x1 != 0 {
+               panic("mergeRuneSets odd length []rune")
+       }
+       var (
+               lx, rx int
+       )
+       merged := make([]rune, 0)
+       next := make([]uint32, 0)
+       ok := true
+       defer func() {
+               if !ok {
+                       merged = nil
+                       next = nil
+               }
+       }()
+
+       ix := -1
+       extend := func(newLow *int, newArray *[]rune, pc uint32) bool {
+               if ix > 0 && (*newArray)[*newLow] <= merged[ix] {
+                       return false
+               }
+               merged = append(merged, (*newArray)[*newLow], (*newArray)[*newLow+1])
+               *newLow += 2
+               ix += 2
+               next = append(next, pc)
+               return true
+       }
+
+       for lx < leftLen || rx < rightLen {
+               switch {
+               case rx >= rightLen:
+                       ok = extend(&lx, leftRunes, leftPC)
+               case lx >= leftLen:
+                       ok = extend(&rx, rightRunes, rightPC)
+               case (*rightRunes)[rx] < (*leftRunes)[lx]:
+                       ok = extend(&rx, rightRunes, rightPC)
+               default:
+                       ok = extend(&lx, leftRunes, leftPC)
+               }
+               if !ok {
+                       return noRune, noNext
+               }
+       }
+       return merged, next
+}
+
+// cleanupOnePass drops working memory, and restores certain shortcut instructions.
+func cleanupOnePass(prog *onePassProg, original *syntax.Prog) {
+       for ix, instOriginal := range original.Inst {
+               switch instOriginal.Op {
+               case syntax.InstAlt, syntax.InstAltMatch, syntax.InstRune:
+               case syntax.InstCapture, syntax.InstEmptyWidth, syntax.InstNop, syntax.InstMatch, syntax.InstFail:
+                       prog.Inst[ix].Next = nil
+               case syntax.InstRune1, syntax.InstRuneAny, syntax.InstRuneAnyNotNL:
+                       prog.Inst[ix].Next = nil
+                       prog.Inst[ix] = onePassInst{Inst: instOriginal}
+               }
+       }
+}
+
+// onePassCopy creates a copy of the original Prog, as we'll be modifying it
+func onePassCopy(prog *syntax.Prog) *onePassProg {
+       p := &onePassProg{
+               Start:  prog.Start,
+               NumCap: prog.NumCap,
+       }
+       for _, inst := range prog.Inst {
+               p.Inst = append(p.Inst, onePassInst{Inst: inst})
+       }
+
+       // rewrites one or more common Prog constructs that enable some otherwise
+       // non-onepass Progs to be onepass. A:BD (for example) means an InstAlt at
+       // ip A, that points to ips B & C.
+       // A:BC + B:DA => A:BC + B:CD
+       // A:BC + B:DC => A:DC + B:DC
+       for pc := range p.Inst {
+               switch p.Inst[pc].Op {
+               default:
+                       continue
+               case syntax.InstAlt, syntax.InstAltMatch:
+                       // A:Bx + B:Ay
+                       p_A_Other := &p.Inst[pc].Out
+                       p_A_Alt := &p.Inst[pc].Arg
+                       // make sure a target is another Alt
+                       instAlt := p.Inst[*p_A_Alt]
+                       if !(instAlt.Op == syntax.InstAlt || instAlt.Op == syntax.InstAltMatch) {
+                               p_A_Alt, p_A_Other = p_A_Other, p_A_Alt
+                               instAlt = p.Inst[*p_A_Alt]
+                               if !(instAlt.Op == syntax.InstAlt || instAlt.Op == syntax.InstAltMatch) {
+                                       continue
+                               }
+                       }
+                       instOther := p.Inst[*p_A_Other]
+                       // Analyzing both legs pointing to Alts is for another day
+                       if instOther.Op == syntax.InstAlt || instOther.Op == syntax.InstAltMatch {
+                               // too complicated
+                               continue
+                       }
+                       // simple empty transition loop
+                       // A:BC + B:DA => A:BC + B:DC
+                       p_B_Alt := &p.Inst[*p_A_Alt].Out
+                       p_B_Other := &p.Inst[*p_A_Alt].Arg
+                       patch := false
+                       if instAlt.Out == uint32(pc) {
+                               patch = true
+                       } else if instAlt.Arg == uint32(pc) {
+                               patch = true
+                               p_B_Alt, p_B_Other = p_B_Other, p_B_Alt
+                       }
+                       if patch {
+                               *p_B_Alt = *p_A_Other
+                       }
+
+                       // empty transition to common target
+                       // A:BC + B:DC => A:DC + B:DC
+                       if *p_A_Other == *p_B_Alt {
+                               *p_A_Alt = *p_B_Other
+                       }
+               }
+       }
+       return p
+}
+
+// runeSlice exists to permit sorting the case-folded rune sets.
+type runeSlice []rune
+
+func (p runeSlice) Len() int           { return len(p) }
+func (p runeSlice) Less(i, j int) bool { return p[i] < p[j] }
+func (p runeSlice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }
+
+// Sort is a convenience method.
+func (p runeSlice) Sort() {
+       sort.Sort(p)
+}
+
+var anyRuneNotNL = []rune{0, '\n' - 1, '\n' + 1, unicode.MaxRune}
+var anyRune = []rune{0, unicode.MaxRune}
+
+// makeOnePass creates a onepass Prog, if possible. It is possible if at any alt,
+// the match engine can always tell which branch to take. The routine may modify
+// p if it is turned into a onepass Prog. If it isn't possible for this to be a
+// onepass Prog, the Prog notOnePass is returned. makeOnePass is recursive
+// to the size of the Prog.
+func makeOnePass(p *onePassProg) *onePassProg {
+       // If the machine is very long, it's not worth the time to check if we can use one pass.
+       if len(p.Inst) >= 1000 {
+               return notOnePass
+       }
+
+       var (
+               instQueue    = newQueue(len(p.Inst))
+               visitQueue   = newQueue(len(p.Inst))
+               build        func(uint32, *queueOnePass)
+               check        func(uint32, map[uint32]bool) bool
+               onePassRunes = make([][]rune, len(p.Inst))
+       )
+       build = func(pc uint32, q *queueOnePass) {
+               if q.contains(pc) {
+                       return
+               }
+               inst := p.Inst[pc]
+               switch inst.Op {
+               case syntax.InstAlt, syntax.InstAltMatch:
+                       q.insert(inst.Out)
+                       build(inst.Out, q)
+                       q.insert(inst.Arg)
+               case syntax.InstMatch, syntax.InstFail:
+               default:
+                       q.insert(inst.Out)
+               }
+       }
+
+       // check that paths from Alt instructions are unambiguous, and rebuild the new
+       // program as a onepass program
+       check = func(pc uint32, m map[uint32]bool) (ok bool) {
+               ok = true
+               inst := &p.Inst[pc]
+               if visitQueue.contains(pc) {
+                       return
+               }
+               visitQueue.insert(pc)
+               switch inst.Op {
+               case syntax.InstAlt, syntax.InstAltMatch:
+                       ok = check(inst.Out, m) && check(inst.Arg, m)
+                       // check no-input paths to InstMatch
+                       matchOut := m[inst.Out]
+                       matchArg := m[inst.Arg]
+                       if matchOut && matchArg {
+                               ok = false
+                               break
+                       }
+                       // Match on empty goes in inst.Out
+                       if matchArg {
+                               inst.Out, inst.Arg = inst.Arg, inst.Out
+                               matchOut, matchArg = matchArg, matchOut
+                       }
+                       if matchOut {
+                               m[pc] = true
+                               inst.Op = syntax.InstAltMatch
+                       }
+
+                       // build a dispatch operator from the two legs of the alt.
+                       onePassRunes[pc], inst.Next = mergeRuneSets(
+                               &onePassRunes[inst.Out], &onePassRunes[inst.Arg], inst.Out, inst.Arg)
+                       if len(inst.Next) > 0 && inst.Next[0] == mergeFailed {
+                               ok = false
+                               break
+                       }
+               case syntax.InstCapture, syntax.InstNop:
+                       ok = check(inst.Out, m)
+                       m[pc] = m[inst.Out]
+                       // pass matching runes back through these no-ops.
+                       onePassRunes[pc] = append([]rune{}, onePassRunes[inst.Out]...)
+                       inst.Next = []uint32{}
+                       for i := len(onePassRunes[pc]) / 2; i >= 0; i-- {
+                               inst.Next = append(inst.Next, inst.Out)
+                       }
+               case syntax.InstEmptyWidth:
+                       ok = check(inst.Out, m)
+                       m[pc] = m[inst.Out]
+                       onePassRunes[pc] = append([]rune{}, onePassRunes[inst.Out]...)
+                       inst.Next = []uint32{}
+                       for i := len(onePassRunes[pc]) / 2; i >= 0; i-- {
+                               inst.Next = append(inst.Next, inst.Out)
+                       }
+               case syntax.InstMatch, syntax.InstFail:
+                       m[pc] = inst.Op == syntax.InstMatch
+                       break
+               case syntax.InstRune:
+                       ok = check(inst.Out, m)
+                       m[pc] = false
+                       if len(inst.Next) > 0 {
+                               break
+                       }
+                       if len(inst.Rune) == 0 {
+                               onePassRunes[pc] = []rune{}
+                               inst.Next = []uint32{inst.Out}
+                               break
+                       }
+                       runes := make([]rune, 0)
+                       if len(inst.Rune) == 1 && syntax.Flags(inst.Arg)&syntax.FoldCase != 0 {
+                               r0 := inst.Rune[0]
+                               runes = append(runes, r0, r0)
+                               for r1 := unicode.SimpleFold(r0); r1 != r0; r1 = unicode.SimpleFold(r1) {
+                                       runes = append(runes, r1, r1)
+                               }
+                               sort.Sort(runeSlice(runes))
+                       } else {
+                               runes = append(runes, inst.Rune...)
+                       }
+                       onePassRunes[pc] = runes
+                       inst.Next = []uint32{}
+                       for i := len(onePassRunes[pc]) / 2; i >= 0; i-- {
+                               inst.Next = append(inst.Next, inst.Out)
+                       }
+                       inst.Op = syntax.InstRune
+               case syntax.InstRune1:
+                       ok = check(inst.Out, m)
+                       m[pc] = false
+                       if len(inst.Next) > 0 {
+                               break
+                       }
+                       runes := []rune{}
+                       // expand case-folded runes
+                       if syntax.Flags(inst.Arg)&syntax.FoldCase != 0 {
+                               r0 := inst.Rune[0]
+                               runes = append(runes, r0, r0)
+                               for r1 := unicode.SimpleFold(r0); r1 != r0; r1 = unicode.SimpleFold(r1) {
+                                       runes = append(runes, r1, r1)
+                               }
+                               sort.Sort(runeSlice(runes))
+                       } else {
+                               runes = append(runes, inst.Rune[0], inst.Rune[0])
+                       }
+                       onePassRunes[pc] = runes
+                       inst.Next = []uint32{}
+                       for i := len(onePassRunes[pc]) / 2; i >= 0; i-- {
+                               inst.Next = append(inst.Next, inst.Out)
+                       }
+                       inst.Op = syntax.InstRune
+               case syntax.InstRuneAny:
+                       ok = check(inst.Out, m)
+                       m[pc] = false
+                       if len(inst.Next) > 0 {
+                               break
+                       }
+                       onePassRunes[pc] = append([]rune{}, anyRune...)
+                       inst.Next = []uint32{inst.Out}
+               case syntax.InstRuneAnyNotNL:
+                       ok = check(inst.Out, m)
+                       m[pc] = false
+                       if len(inst.Next) > 0 {
+                               break
+                       }
+                       onePassRunes[pc] = append([]rune{}, anyRuneNotNL...)
+                       inst.Next = []uint32{}
+                       for i := len(onePassRunes[pc]) / 2; i >= 0; i-- {
+                               inst.Next = append(inst.Next, inst.Out)
+                       }
+               }
+               return
+       }
+
+       instQueue.clear()
+       instQueue.insert(uint32(p.Start))
+       m := make(map[uint32]bool, len(p.Inst))
+       for !instQueue.empty() {
+               pc := instQueue.next()
+               inst := p.Inst[pc]
+               visitQueue.clear()
+               if !check(uint32(pc), m) {
+                       p = notOnePass
+                       break
+               }
+               switch inst.Op {
+               case syntax.InstAlt, syntax.InstAltMatch:
+                       instQueue.insert(inst.Out)
+                       instQueue.insert(inst.Arg)
+               case syntax.InstCapture, syntax.InstEmptyWidth, syntax.InstNop:
+                       instQueue.insert(inst.Out)
+               case syntax.InstMatch:
+               case syntax.InstFail:
+               case syntax.InstRune, syntax.InstRune1, syntax.InstRuneAny, syntax.InstRuneAnyNotNL:
+               default:
+               }
+       }
+       if p != notOnePass {
+               for i, _ := range p.Inst {
+                       p.Inst[i].Rune = onePassRunes[i]
+               }
+       }
+       return p
+}
+
+// walk visits each Inst in the prog once, and applies the argument
+// function(ip, next), in pre-order.
+func walk(prog *syntax.Prog, funcs ...func(ip, next uint32)) {
+       var walk1 func(uint32)
+       progQueue := newQueue(len(prog.Inst))
+       walk1 = func(ip uint32) {
+               if progQueue.contains(ip) {
+                       return
+               }
+               progQueue.insert(ip)
+               inst := prog.Inst[ip]
+               switch inst.Op {
+               case syntax.InstAlt, syntax.InstAltMatch:
+                       for _, f := range funcs {
+                               f(ip, inst.Out)
+                               f(ip, inst.Arg)
+                       }
+                       walk1(inst.Out)
+                       walk1(inst.Arg)
+               default:
+                       for _, f := range funcs {
+                               f(ip, inst.Out)
+                       }
+                       walk1(inst.Out)
+               }
+       }
+       walk1(uint32(prog.Start))
+}
+
+// find returns the Insts that match the argument predicate function
+func find(prog *syntax.Prog, f func(*syntax.Prog, int) bool) (matches []uint32) {
+       matches = []uint32{}
+
+       for ip := range prog.Inst {
+               if f(prog, ip) {
+                       matches = append(matches, uint32(ip))
+               }
+       }
+       return
+}
+
+var notOnePass *onePassProg = nil
+
+// compileOnePass returns a new *syntax.Prog suitable for onePass execution if the original Prog
+// can be recharacterized as a one-pass regexp program, or syntax.notOnePass if the
+// Prog cannot be converted. For a one pass prog, the fundamental condition that must
+// be true is: at any InstAlt, there must be no ambiguity about what branch to  take.
+func compileOnePass(prog *syntax.Prog) (p *onePassProg) {
+       if prog.Start == 0 {
+               return notOnePass
+       }
+       // onepass regexp is anchored
+       if prog.Inst[prog.Start].Op != syntax.InstEmptyWidth ||
+               syntax.EmptyOp(prog.Inst[prog.Start].Arg)&syntax.EmptyBeginText != syntax.EmptyBeginText {
+               return notOnePass
+       }
+       // every instruction leading to InstMatch must be EmptyEndText
+       for _, inst := range prog.Inst {
+               opOut := prog.Inst[inst.Out].Op
+               switch inst.Op {
+               default:
+                       if opOut == syntax.InstMatch {
+                               return notOnePass
+                       }
+               case syntax.InstAlt, syntax.InstAltMatch:
+                       if opOut == syntax.InstMatch || prog.Inst[inst.Arg].Op == syntax.InstMatch {
+                               return notOnePass
+                       }
+               case syntax.InstEmptyWidth:
+                       if opOut == syntax.InstMatch {
+                               if syntax.EmptyOp(inst.Arg)&syntax.EmptyEndText == syntax.EmptyEndText {
+                                       continue
+                               }
+                               return notOnePass
+                       }
+               }
+       }
+       // Creates a slightly optimized copy of the original Prog
+       // that cleans up some Prog idioms that block valid onepass programs
+       p = onePassCopy(prog)
+
+       // checkAmbiguity on InstAlts, build onepass Prog if possible
+       p = makeOnePass(p)
+
+       if p != notOnePass {
+               cleanupOnePass(p, prog)
+       }
+       return p
+}
diff --git a/libgo/go/regexp/onepass_test.go b/libgo/go/regexp/onepass_test.go
new file mode 100644 (file)
index 0000000..7b2beea
--- /dev/null
@@ -0,0 +1,208 @@
+// Copyright 2014 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 regexp
+
+import (
+       "reflect"
+       "regexp/syntax"
+       "testing"
+)
+
+var runeMergeTests = []struct {
+       left, right, merged []rune
+       next                []uint32
+       leftPC, rightPC     uint32
+}{
+       {
+               // empty rhs
+               []rune{69, 69},
+               []rune{},
+               []rune{69, 69},
+               []uint32{1},
+               1, 2,
+       },
+       {
+               // identical runes, identical targets
+               []rune{69, 69},
+               []rune{69, 69},
+               []rune{},
+               []uint32{mergeFailed},
+               1, 1,
+       },
+       {
+               // identical runes, different targets
+               []rune{69, 69},
+               []rune{69, 69},
+               []rune{},
+               []uint32{mergeFailed},
+               1, 2,
+       },
+       {
+               // append right-first
+               []rune{69, 69},
+               []rune{71, 71},
+               []rune{69, 69, 71, 71},
+               []uint32{1, 2},
+               1, 2,
+       },
+       {
+               // append, left-first
+               []rune{71, 71},
+               []rune{69, 69},
+               []rune{69, 69, 71, 71},
+               []uint32{2, 1},
+               1, 2,
+       },
+       {
+               // successful interleave
+               []rune{60, 60, 71, 71, 101, 101},
+               []rune{69, 69, 88, 88},
+               []rune{60, 60, 69, 69, 71, 71, 88, 88, 101, 101},
+               []uint32{1, 2, 1, 2, 1},
+               1, 2,
+       },
+       {
+               // left surrounds right
+               []rune{69, 74},
+               []rune{71, 71},
+               []rune{},
+               []uint32{mergeFailed},
+               1, 2,
+       },
+       {
+               // right surrounds left
+               []rune{69, 74},
+               []rune{68, 75},
+               []rune{},
+               []uint32{mergeFailed},
+               1, 2,
+       },
+       {
+               // overlap at interval begin
+               []rune{69, 74},
+               []rune{74, 75},
+               []rune{},
+               []uint32{mergeFailed},
+               1, 2,
+       },
+       {
+               // overlap ar interval end
+               []rune{69, 74},
+               []rune{65, 69},
+               []rune{},
+               []uint32{mergeFailed},
+               1, 2,
+       },
+       {
+               // overlap from above
+               []rune{69, 74},
+               []rune{71, 74},
+               []rune{},
+               []uint32{mergeFailed},
+               1, 2,
+       },
+       {
+               // overlap from below
+               []rune{69, 74},
+               []rune{65, 71},
+               []rune{},
+               []uint32{mergeFailed},
+               1, 2,
+       },
+       {
+               // out of order []rune
+               []rune{69, 74, 60, 65},
+               []rune{66, 67},
+               []rune{},
+               []uint32{mergeFailed},
+               1, 2,
+       },
+}
+
+func TestMergeRuneSet(t *testing.T) {
+       for ix, test := range runeMergeTests {
+               merged, next := mergeRuneSets(&test.left, &test.right, test.leftPC, test.rightPC)
+               if !reflect.DeepEqual(merged, test.merged) {
+                       t.Errorf("mergeRuneSet :%d (%v, %v) merged\n have\n%v\nwant\n%v", ix, test.left, test.right, merged, test.merged)
+               }
+               if !reflect.DeepEqual(next, test.next) {
+                       t.Errorf("mergeRuneSet :%d(%v, %v) next\n have\n%v\nwant\n%v", ix, test.left, test.right, next, test.next)
+               }
+       }
+}
+
+const noStr = `!`
+
+var onePass = &onePassProg{}
+
+var onePassTests = []struct {
+       re      string
+       onePass *onePassProg
+       prog    string
+}{
+       {`^(?:a|(?:a*))$`, notOnePass, noStr},
+       {`^(?:(a)|(?:a*))$`, notOnePass, noStr},
+       {`^(?:(?:(?:.(?:$))?))$`, onePass, `a`},
+       {`^abcd$`, onePass, `abcd`},
+       {`^abcd$`, onePass, `abcde`},
+       {`^(?:(?:a{0,})*?)$`, onePass, `a`},
+       {`^(?:(?:a+)*)$`, onePass, ``},
+       {`^(?:(?:a|(?:aa)))$`, onePass, ``},
+       {`^(?:[^\s\S])$`, onePass, ``},
+       {`^(?:(?:a{3,4}){0,})$`, notOnePass, `aaaaaa`},
+       {`^(?:(?:a+)*)$`, onePass, `a`},
+       {`^(?:(?:(?:a*)+))$`, onePass, noStr},
+       {`^(?:(?:a+)*)$`, onePass, ``},
+       {`^[a-c]+$`, onePass, `abc`},
+       {`^[a-c]*$`, onePass, `abcdabc`},
+       {`^(?:a*)$`, onePass, `aaaaaaa`},
+       {`^(?:(?:aa)|a)$`, onePass, `a`},
+       {`^[a-c]*`, notOnePass, `abcdabc`},
+       {`^[a-c]*$`, onePass, `abc`},
+       {`^...$`, onePass, ``},
+       {`^(?:a|(?:aa))$`, onePass, `a`},
+       {`^[a-c]*`, notOnePass, `abcabc`},
+       {`^a((b))c$`, onePass, noStr},
+       {`^a.[l-nA-Cg-j]?e$`, onePass, noStr},
+       {`^a((b))$`, onePass, noStr},
+       {`^a(?:(b)|(c))c$`, onePass, noStr},
+       {`^a(?:(b*)|(c))c$`, notOnePass, noStr},
+       {`^a(?:b|c)$`, onePass, noStr},
+       {`^a(?:b?|c)$`, onePass, noStr},
+       {`^a(?:b?|c?)$`, notOnePass, noStr},
+       {`^a(?:b?|c+)$`, onePass, noStr},
+       {`^a(?:b+|(bc))d$`, notOnePass, noStr},
+       {`^a(?:bc)+$`, onePass, noStr},
+       {`^a(?:[bcd])+$`, onePass, noStr},
+       {`^a((?:[bcd])+)$`, onePass, noStr},
+       {`^a(:?b|c)*d$`, onePass, `abbbccbbcbbd"`},
+       {`^.bc(d|e)*$`, onePass, `abcddddddeeeededd`},
+       {`^(?:(?:aa)|.)$`, notOnePass, `a`},
+       {`^(?:(?:a{1,2}){1,2})$`, notOnePass, `aaaa`},
+}
+
+func TestCompileOnePass(t *testing.T) {
+       var (
+               p   *syntax.Prog
+               re  *syntax.Regexp
+               err error
+       )
+       for _, test := range onePassTests {
+               if re, err = syntax.Parse(test.re, syntax.Perl); err != nil {
+                       t.Errorf("Parse(%q) got err:%s, want success", test.re, err)
+                       continue
+               }
+               // needs to be done before compile...
+               re = re.Simplify()
+               if p, err = syntax.Compile(re); err != nil {
+                       t.Errorf("Compile(%q) got err:%s, want success", test.re, err)
+                       continue
+               }
+               onePass = compileOnePass(p)
+               if (onePass == notOnePass) != (test.onePass == notOnePass) {
+                       t.Errorf("CompileOnePass(%q) got %v, expected %v", test.re, onePass, test.onePass)
+               }
+       }
+}
index 6ce5902a5aa1311d5ac31b6ab03af99e4992e5ba..0b8336a04fbba9be3168ae2762892f4113267b0a 100644 (file)
 // For an overview of the syntax, run
 //   godoc regexp/syntax
 //
+// The regexp implementation provided by this package is
+// guaranteed to run in time linear in the size of the input.
+// (This is a property not guaranteed by most open source
+// implementations of regular expressions.) For more information
+// about this property, see
+//     http://swtch.com/~rsc/regexp/regexp1.html
+// or any book about automata theory.
+//
 // All characters are UTF-8-encoded code points.
 //
 // There are 16 methods of Regexp that match a regular expression and identify
@@ -75,10 +83,12 @@ type Regexp struct {
        // read-only after Compile
        expr           string         // as passed to Compile
        prog           *syntax.Prog   // compiled program
+       onepass        *onePassProg   // onpass program or nil
        prefix         string         // required prefix in unanchored matches
        prefixBytes    []byte         // prefix, as a []byte
        prefixComplete bool           // prefix is the entire regexp
        prefixRune     rune           // first rune in prefix
+       prefixEnd      uint32         // pc for last rune in prefix
        cond           syntax.EmptyOp // empty-width conditions required at start of match
        numSubexp      int
        subexpNames    []string
@@ -155,12 +165,17 @@ func compile(expr string, mode syntax.Flags, longest bool) (*Regexp, error) {
        regexp := &Regexp{
                expr:        expr,
                prog:        prog,
+               onepass:     compileOnePass(prog),
                numSubexp:   maxCap,
                subexpNames: capNames,
                cond:        prog.StartCond(),
                longest:     longest,
        }
-       regexp.prefix, regexp.prefixComplete = prog.Prefix()
+       if regexp.onepass == notOnePass {
+               regexp.prefix, regexp.prefixComplete = prog.Prefix()
+       } else {
+               regexp.prefix, regexp.prefixComplete, regexp.prefixEnd = onePassPrefix(prog)
+       }
        if regexp.prefix != "" {
                // TODO(rsc): Remove this allocation by adding
                // IndexString to package bytes.
@@ -182,7 +197,7 @@ func (re *Regexp) get() *machine {
                return z
        }
        re.mu.Unlock()
-       z := progMachine(re.prog)
+       z := progMachine(re.prog, re.onepass)
        z.re = re
        return z
 }
index e52632ef72686e0aa9349a76ee5da2bde7c65603..8e72c90d3ebb718f39551f99f3196f735c4a24b5 100644 (file)
@@ -46,6 +46,10 @@ Repetitions:
   x{n,}?         n or more x, prefer fewer
   x{n}?          exactly n x
 
+Implementation restriction: The counting forms x{n} etc. (but not the other
+forms x* etc.) have an upper limit of n=1000. Negative or higher explicit
+counts yield the parse error ErrInvalidRepeatSize.
+
 Grouping:
   (re)           numbered capturing group (submatch)
   (?P<name>re)   named & numbered capturing group (submatch)
index 42d0bf4a16f66c890c3cbefd08ec97c24900fa18..cb25dca39563a9abf5b19c20fa84ba45aa180ee9 100644 (file)
@@ -668,7 +668,6 @@ func Parse(s string, flags Flags) (*Regexp, error) {
                c          rune
                op         Op
                lastRepeat string
-               min, max   int
        )
        p.flags = flags
        p.wholeRegexp = s
@@ -740,7 +739,7 @@ func Parse(s string, flags Flags) (*Regexp, error) {
                                op = OpQuest
                        }
                        after := t[1:]
-                       if after, err = p.repeat(op, min, max, before, after, lastRepeat); err != nil {
+                       if after, err = p.repeat(op, 0, 0, before, after, lastRepeat); err != nil {
                                return nil, err
                        }
                        repeat = before
index a482a82f21545b03ffb43f690be1411475825199..29bd282d0d96c7c0c235037b9f0bcac33c592bfe 100644 (file)
@@ -37,6 +37,27 @@ const (
        InstRuneAnyNotNL
 )
 
+var instOpNames = []string{
+       "InstAlt",
+       "InstAltMatch",
+       "InstCapture",
+       "InstEmptyWidth",
+       "InstMatch",
+       "InstFail",
+       "InstNop",
+       "InstRune",
+       "InstRune1",
+       "InstRuneAny",
+       "InstRuneAnyNotNL",
+}
+
+func (i InstOp) String() string {
+       if uint(i) >= uint(len(instOpNames)) {
+               return ""
+       }
+       return instOpNames[i]
+}
+
 // An EmptyOp specifies a kind or mixture of zero-width assertions.
 type EmptyOp uint8
 
@@ -103,13 +124,13 @@ func (p *Prog) String() string {
 
 // skipNop follows any no-op or capturing instructions
 // and returns the resulting pc.
-func (p *Prog) skipNop(pc uint32) *Inst {
+func (p *Prog) skipNop(pc uint32) (*Inst, uint32) {
        i := &p.Inst[pc]
        for i.Op == InstNop || i.Op == InstCapture {
                pc = i.Out
                i = &p.Inst[pc]
        }
-       return i
+       return i, pc
 }
 
 // op returns i.Op but merges all the Rune special cases into InstRune
@@ -126,7 +147,7 @@ func (i *Inst) op() InstOp {
 // regexp must start with.  Complete is true if the prefix
 // is the entire match.
 func (p *Prog) Prefix() (prefix string, complete bool) {
-       i := p.skipNop(uint32(p.Start))
+       i, _ := p.skipNop(uint32(p.Start))
 
        // Avoid allocation of buffer if prefix is empty.
        if i.op() != InstRune || len(i.Rune) != 1 {
@@ -137,7 +158,7 @@ func (p *Prog) Prefix() (prefix string, complete bool) {
        var buf bytes.Buffer
        for i.op() == InstRune && len(i.Rune) == 1 && Flags(i.Arg)&FoldCase == 0 {
                buf.WriteRune(i.Rune[0])
-               i = p.skipNop(i.Out)
+               i, _ = p.skipNop(i.Out)
        }
        return buf.String(), i.Op == InstMatch
 }
@@ -166,35 +187,46 @@ Loop:
        return flag
 }
 
+const noMatch = -1
+
 // MatchRune returns true if the instruction matches (and consumes) r.
 // It should only be called when i.Op == InstRune.
 func (i *Inst) MatchRune(r rune) bool {
+       return i.MatchRunePos(r) != noMatch
+}
+
+// MatchRunePos checks whether the instruction matches (and consumes) r.
+// If so, MatchRunePos returns the index of the matching rune pair
+// (or, when len(i.Rune) == 1, rune singleton).
+// If not, MatchRunePos returns -1.
+// MatchRunePos should only be called when i.Op == InstRune.
+func (i *Inst) MatchRunePos(r rune) int {
        rune := i.Rune
 
        // Special case: single-rune slice is from literal string, not char class.
        if len(rune) == 1 {
                r0 := rune[0]
                if r == r0 {
-                       return true
+                       return 0
                }
                if Flags(i.Arg)&FoldCase != 0 {
                        for r1 := unicode.SimpleFold(r0); r1 != r0; r1 = unicode.SimpleFold(r1) {
                                if r == r1 {
-                                       return true
+                                       return 0
                                }
                        }
                }
-               return false
+               return noMatch
        }
 
        // Peek at the first few pairs.
        // Should handle ASCII well.
        for j := 0; j < len(rune) && j <= 8; j += 2 {
                if r < rune[j] {
-                       return false
+                       return noMatch
                }
                if r <= rune[j+1] {
-                       return true
+                       return j / 2
                }
        }
 
@@ -205,14 +237,14 @@ func (i *Inst) MatchRune(r rune) bool {
                m := lo + (hi-lo)/2
                if c := rune[2*m]; c <= r {
                        if r <= rune[2*m+1] {
-                               return true
+                               return m
                        }
                        lo = m + 1
                } else {
                        hi = m
                }
        }
-       return false
+       return noMatch
 }
 
 // As per re2's Prog::IsWordChar. Determines whether rune is an ASCII word char.
index cd71abc2a471cc3571041d328ac4fdd9428aaa0f..50bfa3d4bef6d4643c1c6b4ff49673c6ec8fa244 100644 (file)
@@ -4,9 +4,7 @@
 
 package syntax
 
-import (
-       "testing"
-)
+import "testing"
 
 var compileTests = []struct {
        Regexp string
index 782176c883639152035cbfe0a8da8c180043c935..ce4b3962717f612585d5c8820b77be1ec29c7e13 100644 (file)
@@ -431,27 +431,15 @@ func TestMultiConsumer(t *testing.T) {
 }
 
 func BenchmarkChanNonblocking(b *testing.B) {
-       const CallsPerSched = 1000
-       procs := runtime.GOMAXPROCS(-1)
-       N := int32(b.N / CallsPerSched)
-       c := make(chan bool, procs)
        myc := make(chan int)
-       for p := 0; p < procs; p++ {
-               go func() {
-                       for atomic.AddInt32(&N, -1) >= 0 {
-                               for g := 0; g < CallsPerSched; g++ {
-                                       select {
-                                       case <-myc:
-                                       default:
-                                       }
-                               }
+       b.RunParallel(func(pb *testing.PB) {
+               for pb.Next() {
+                       select {
+                       case <-myc:
+                       default:
                        }
-                       c <- true
-               }()
-       }
-       for p := 0; p < procs; p++ {
-               <-c
-       }
+               }
+       })
 }
 
 func BenchmarkSelectUncontended(b *testing.B) {
@@ -713,23 +701,11 @@ func BenchmarkChanCreation(b *testing.B) {
 
 func BenchmarkChanSem(b *testing.B) {
        type Empty struct{}
-       const CallsPerSched = 1000
-       procs := runtime.GOMAXPROCS(0)
-       N := int32(b.N / CallsPerSched)
-       c := make(chan bool, procs)
-       myc := make(chan Empty, procs)
-       for p := 0; p < procs; p++ {
-               go func() {
-                       for atomic.AddInt32(&N, -1) >= 0 {
-                               for g := 0; g < CallsPerSched; g++ {
-                                       myc <- Empty{}
-                                       <-myc
-                               }
-                       }
-                       c <- true
-               }()
-       }
-       for p := 0; p < procs; p++ {
-               <-c
-       }
+       myc := make(chan Empty, runtime.GOMAXPROCS(0))
+       b.RunParallel(func(pb *testing.PB) {
+               for pb.Next() {
+                       myc <- Empty{}
+                       <-myc
+               }
+       })
 }
index d8bfdbdad6dcfb5fe040a521da92070a0baaf798..39e04345a10ac968d9f38f92415249d435483b50 100644 (file)
@@ -9,6 +9,7 @@ import (
        "os"
        "os/exec"
        "path/filepath"
+       "runtime"
        "strings"
        "testing"
        "text/template"
@@ -32,6 +33,10 @@ func testEnv(cmd *exec.Cmd) *exec.Cmd {
 
 func executeTest(t *testing.T, templ string, data interface{}) string {
        t.Skip("gccgo does not have a go command")
+       if runtime.GOOS == "nacl" {
+               t.Skip("skipping on nacl")
+       }
+
        checkStaleRuntime(t)
 
        st := template.Must(template.New("crashSource").Parse(templ))
@@ -112,8 +117,9 @@ func TestLockedDeadlock2(t *testing.T) {
 
 func TestGoexitDeadlock(t *testing.T) {
        output := executeTest(t, goexitDeadlockSource, nil)
-       if output != "" {
-               t.Fatalf("expected no output, got:\n%s", output)
+       want := "no goroutines (main called runtime.Goexit) - deadlock!"
+       if !strings.Contains(output, want) {
+               t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
        }
 }
 
@@ -133,6 +139,34 @@ func TestThreadExhaustion(t *testing.T) {
        }
 }
 
+func TestRecursivePanic(t *testing.T) {
+       output := executeTest(t, recursivePanicSource, nil)
+       want := `wrap: bad
+panic: again
+
+`
+       if !strings.HasPrefix(output, want) {
+               t.Fatalf("output does not start with %q:\n%s", want, output)
+       }
+
+}
+
+func TestGoexitCrash(t *testing.T) {
+       output := executeTest(t, goexitExitSource, nil)
+       want := "no goroutines (main called runtime.Goexit) - deadlock!"
+       if !strings.Contains(output, want) {
+               t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
+       }
+}
+
+func TestGoNil(t *testing.T) {
+       output := executeTest(t, goNilSource, nil)
+       want := "go of nil func value"
+       if !strings.Contains(output, want) {
+               t.Fatalf("output:\n%s\n\nwant output containing: %s", output, want)
+       }
+}
+
 const crashSource = `
 package main
 
@@ -273,3 +307,61 @@ func main() {
        }
 }
 `
+
+const recursivePanicSource = `
+package main
+
+import (
+       "fmt"
+)
+
+func main() {
+       func() {
+               defer func() {
+                       fmt.Println(recover())
+               }()
+               var x [8192]byte
+               func(x [8192]byte) {
+                       defer func() {
+                               if err := recover(); err != nil {
+                                       panic("wrap: " + err.(string))
+                               }
+                       }()
+                       panic("bad")
+               }(x)
+       }()
+       panic("again")
+}
+`
+
+const goexitExitSource = `
+package main
+
+import (
+       "runtime"
+       "time"
+)
+
+func main() {
+       go func() {
+               time.Sleep(time.Millisecond)
+       }()
+       i := 0
+       runtime.SetFinalizer(&i, func(p *int) {})
+       runtime.GC()
+       runtime.Goexit()
+}
+`
+
+const goNilSource = `
+package main
+
+func main() {
+       defer func() {
+               recover()
+       }()
+       var f func()
+       go f()
+       select{}
+}
+`
index a724fdf8f605c0d9db6de26472aa4fc95a631cb5..edb3643871e67368d0b7cd11c135d170c1e0f18e 100644 (file)
@@ -135,3 +135,19 @@ func SetMaxStack(bytes int) int {
 func SetMaxThreads(threads int) int {
        return setMaxThreads(threads)
 }
+
+// SetPanicOnFault controls the runtime's behavior when a program faults
+// at an unexpected (non-nil) address. Such faults are typically caused by
+// bugs such as runtime memory corruption, so the default response is to crash
+// the program. Programs working with memory-mapped files or unsafe
+// manipulation of memory may cause faults at non-nil addresses in less
+// dramatic situations; SetPanicOnFault allows such programs to request
+// that the runtime trigger only a panic, not a crash.
+// SetPanicOnFault applies only to the current goroutine.
+// It returns the previous setting.
+func SetPanicOnFault(enabled bool) bool
+
+// WriteHeapDump writes a description of the heap and the objects in
+// it to the given file descriptor.
+// The heap dump format is defined at http://golang.org/s/go13heapdump.
+func WriteHeapDump(fd uintptr)
diff --git a/libgo/go/runtime/debug/heapdump_test.go b/libgo/go/runtime/debug/heapdump_test.go
new file mode 100644 (file)
index 0000000..9201901
--- /dev/null
@@ -0,0 +1,33 @@
+// Copyright 2014 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 debug
+
+import (
+       "io/ioutil"
+       "os"
+       "runtime"
+       "testing"
+)
+
+func TestWriteHeapDumpNonempty(t *testing.T) {
+       if runtime.GOOS == "nacl" {
+               t.Skip("WriteHeapDump is not available on NaCl.")
+       }
+       f, err := ioutil.TempFile("", "heapdumptest")
+       if err != nil {
+               t.Fatalf("TempFile failed: %v", err)
+       }
+       defer os.Remove(f.Name())
+       defer f.Close()
+       WriteHeapDump(f.Fd())
+       fi, err := f.Stat()
+       if err != nil {
+               t.Fatalf("Stat failed: %v", err)
+       }
+       const minSize = 1
+       if size := fi.Size(); size < minSize {
+               t.Fatalf("Heap dump size %d bytes, expected at least %d bytes", size, minSize)
+       }
+}
index 85b69cfdcfcb93e169986b4f9276b61c01f16d40..333d4fd000f4de9944a99e12e96a7cee9669ca86 100644 (file)
@@ -36,6 +36,9 @@ a comma-separated list of name=val pairs. Supported names are:
        length of the pause. Setting gctrace=2 emits the same summary but also
        repeats each collection.
 
+       gcdead: setting gcdead=1 causes the garbage collector to clobber all stack slots
+       that it thinks are dead.
+
        scheddetail: setting schedtrace=X and scheddetail=1 causes the scheduler to emit
        detailed multiline info every X milliseconds, describing state of the scheduler,
        processors, threads and goroutines.
@@ -76,6 +79,11 @@ func Gosched()
 
 // Goexit terminates the goroutine that calls it.  No other goroutine is affected.
 // Goexit runs all deferred calls before terminating the goroutine.
+//
+// Calling Goexit from the main goroutine terminates that goroutine
+// without func main returning. Since func main has not returned,
+// the program continues execution of other goroutines.
+// If all other goroutines exit, the program crashes.
 func Goexit()
 
 // Caller reports file and line number information about function invocations on
@@ -182,10 +190,8 @@ func GOROOT() string {
 }
 
 // Version returns the Go tree's version string.
-// It is either a sequence number or, when possible,
-// a release tag like "release.2010-03-04".
-// A trailing + indicates that the tree had local modifications
-// at the time of the build.
+// It is either the commit hash and date at the time of the build or,
+// when possible, a release tag like "go1.3".
 func Version() string {
        return theVersion
 }
index 1b3ccbf7d936d19e4d491122caa01e3c9091c2b2..5a1e9b89c4294bb353f402f009c9028416087bfd 100644 (file)
@@ -9,6 +9,7 @@ import (
        "runtime"
        "runtime/debug"
        "testing"
+       "time"
 )
 
 func TestGcSys(t *testing.T) {
@@ -153,3 +154,85 @@ func TestGcRescan(t *testing.T) {
                }
        }
 }
+
+func TestGcLastTime(t *testing.T) {
+       ms := new(runtime.MemStats)
+       t0 := time.Now().UnixNano()
+       runtime.GC()
+       t1 := time.Now().UnixNano()
+       runtime.ReadMemStats(ms)
+       last := int64(ms.LastGC)
+       if t0 > last || last > t1 {
+               t.Fatalf("bad last GC time: got %v, want [%v, %v]", last, t0, t1)
+       }
+}
+
+func BenchmarkSetTypeNoPtr1(b *testing.B) {
+       type NoPtr1 struct {
+               p uintptr
+       }
+       var p *NoPtr1
+       for i := 0; i < b.N; i++ {
+               p = &NoPtr1{}
+       }
+       _ = p
+}
+func BenchmarkSetTypeNoPtr2(b *testing.B) {
+       type NoPtr2 struct {
+               p, q uintptr
+       }
+       var p *NoPtr2
+       for i := 0; i < b.N; i++ {
+               p = &NoPtr2{}
+       }
+       _ = p
+}
+func BenchmarkSetTypePtr1(b *testing.B) {
+       type Ptr1 struct {
+               p *byte
+       }
+       var p *Ptr1
+       for i := 0; i < b.N; i++ {
+               p = &Ptr1{}
+       }
+       _ = p
+}
+func BenchmarkSetTypePtr2(b *testing.B) {
+       type Ptr2 struct {
+               p, q *byte
+       }
+       var p *Ptr2
+       for i := 0; i < b.N; i++ {
+               p = &Ptr2{}
+       }
+       _ = p
+}
+
+func BenchmarkAllocation(b *testing.B) {
+       type T struct {
+               x, y *byte
+       }
+       ngo := runtime.GOMAXPROCS(0)
+       work := make(chan bool, b.N+ngo)
+       result := make(chan *T)
+       for i := 0; i < b.N; i++ {
+               work <- true
+       }
+       for i := 0; i < ngo; i++ {
+               work <- false
+       }
+       for i := 0; i < ngo; i++ {
+               go func() {
+                       var x *T
+                       for <-work {
+                               for i := 0; i < 1000; i++ {
+                                       x = &T{}
+                               }
+                       }
+                       result <- x
+               }()
+       }
+       for i := 0; i < ngo; i++ {
+               <-result
+       }
+}
index fe5d3ad86c911191f35e3932dc191991de6a9921..d9690253582ea193e55c5396489ed0849548f5c3 100644 (file)
@@ -449,3 +449,43 @@ func TestMapIterOrder(t *testing.T) {
                }
        }
 }
+
+func TestMapStringBytesLookup(t *testing.T) {
+       if runtime.Compiler == "gccgo" {
+               t.Skip("skipping for gccgo")
+       }
+       // Use large string keys to avoid small-allocation coalescing,
+       // which can cause AllocsPerRun to report lower counts than it should.
+       m := map[string]int{
+               "1000000000000000000000000000000000000000000000000": 1,
+               "2000000000000000000000000000000000000000000000000": 2,
+       }
+       buf := []byte("1000000000000000000000000000000000000000000000000")
+       if x := m[string(buf)]; x != 1 {
+               t.Errorf(`m[string([]byte("1"))] = %d, want 1`, x)
+       }
+       buf[0] = '2'
+       if x := m[string(buf)]; x != 2 {
+               t.Errorf(`m[string([]byte("2"))] = %d, want 2`, x)
+       }
+
+       var x int
+       n := testing.AllocsPerRun(100, func() {
+               x += m[string(buf)]
+       })
+       if n != 0 {
+               t.Errorf("AllocsPerRun for m[string(buf)] = %v, want 0", n)
+       }
+
+       x = 0
+       n = testing.AllocsPerRun(100, func() {
+               y, ok := m[string(buf)]
+               if !ok {
+                       panic("!ok")
+               }
+               x += y
+       })
+       if n != 0 {
+               t.Errorf("AllocsPerRun for x,ok = m[string(buf)] = %v, want 0", n)
+       }
+}
index 5c01aac97a91c9cfd88062d5a40562e580b1b2ee..540f0feb5495cf0266cbb7824fe3a6c0f326c5fa 100644 (file)
@@ -161,3 +161,83 @@ func BenchmarkMemclr64(b *testing.B)    { bmMemclr(b, 64) }
 func BenchmarkMemclr256(b *testing.B)   { bmMemclr(b, 256) }
 func BenchmarkMemclr4096(b *testing.B)  { bmMemclr(b, 4096) }
 func BenchmarkMemclr65536(b *testing.B) { bmMemclr(b, 65536) }
+
+func BenchmarkClearFat32(b *testing.B) {
+       for i := 0; i < b.N; i++ {
+               var x [32]byte
+               _ = x
+       }
+}
+func BenchmarkClearFat64(b *testing.B) {
+       for i := 0; i < b.N; i++ {
+               var x [64]byte
+               _ = x
+       }
+}
+func BenchmarkClearFat128(b *testing.B) {
+       for i := 0; i < b.N; i++ {
+               var x [128]byte
+               _ = x
+       }
+}
+func BenchmarkClearFat256(b *testing.B) {
+       for i := 0; i < b.N; i++ {
+               var x [256]byte
+               _ = x
+       }
+}
+func BenchmarkClearFat512(b *testing.B) {
+       for i := 0; i < b.N; i++ {
+               var x [512]byte
+               _ = x
+       }
+}
+func BenchmarkClearFat1024(b *testing.B) {
+       for i := 0; i < b.N; i++ {
+               var x [1024]byte
+               _ = x
+       }
+}
+
+func BenchmarkCopyFat32(b *testing.B) {
+       var x [32 / 4]uint32
+       for i := 0; i < b.N; i++ {
+               y := x
+               _ = y
+       }
+}
+func BenchmarkCopyFat64(b *testing.B) {
+       var x [64 / 4]uint32
+       for i := 0; i < b.N; i++ {
+               y := x
+               _ = y
+       }
+}
+func BenchmarkCopyFat128(b *testing.B) {
+       var x [128 / 4]uint32
+       for i := 0; i < b.N; i++ {
+               y := x
+               _ = y
+       }
+}
+func BenchmarkCopyFat256(b *testing.B) {
+       var x [256 / 4]uint32
+       for i := 0; i < b.N; i++ {
+               y := x
+               _ = y
+       }
+}
+func BenchmarkCopyFat512(b *testing.B) {
+       var x [512 / 4]uint32
+       for i := 0; i < b.N; i++ {
+               y := x
+               _ = y
+       }
+}
+func BenchmarkCopyFat1024(b *testing.B) {
+       var x [1024 / 4]uint32
+       for i := 0; i < b.N; i++ {
+               y := x
+               _ = y
+       }
+}
index ffcffbd4befa0bc5239985e3e2da13ca7d7fb0b4..b47f83c3923ff5bd70d620c3ac336597048da6b1 100644 (file)
@@ -6,10 +6,9 @@ package runtime_test
 
 import (
        "runtime"
-       "sync"
-       "sync/atomic"
        "testing"
        "time"
+       "unsafe"
 )
 
 type Tintptr *int // assignable to *int
@@ -112,50 +111,133 @@ func TestFinalizerZeroSizedStruct(t *testing.T) {
 }
 
 func BenchmarkFinalizer(b *testing.B) {
-       const CallsPerSched = 1000
-       procs := runtime.GOMAXPROCS(-1)
-       N := int32(b.N / CallsPerSched)
-       var wg sync.WaitGroup
-       wg.Add(procs)
-       for p := 0; p < procs; p++ {
-               go func() {
-                       var data [CallsPerSched]*int
-                       for i := 0; i < CallsPerSched; i++ {
-                               data[i] = new(int)
+       const Batch = 1000
+       b.RunParallel(func(pb *testing.PB) {
+               var data [Batch]*int
+               for i := 0; i < Batch; i++ {
+                       data[i] = new(int)
+               }
+               for pb.Next() {
+                       for i := 0; i < Batch; i++ {
+                               runtime.SetFinalizer(data[i], fin)
                        }
-                       for atomic.AddInt32(&N, -1) >= 0 {
-                               runtime.Gosched()
-                               for i := 0; i < CallsPerSched; i++ {
-                                       runtime.SetFinalizer(data[i], fin)
-                               }
-                               for i := 0; i < CallsPerSched; i++ {
-                                       runtime.SetFinalizer(data[i], nil)
-                               }
+                       for i := 0; i < Batch; i++ {
+                               runtime.SetFinalizer(data[i], nil)
                        }
-                       wg.Done()
-               }()
-       }
-       wg.Wait()
+               }
+       })
 }
 
 func BenchmarkFinalizerRun(b *testing.B) {
-       const CallsPerSched = 1000
-       procs := runtime.GOMAXPROCS(-1)
-       N := int32(b.N / CallsPerSched)
-       var wg sync.WaitGroup
-       wg.Add(procs)
-       for p := 0; p < procs; p++ {
-               go func() {
-                       for atomic.AddInt32(&N, -1) >= 0 {
-                               runtime.Gosched()
-                               for i := 0; i < CallsPerSched; i++ {
-                                       v := new(int)
-                                       runtime.SetFinalizer(v, fin)
-                               }
-                               runtime.GC()
+       b.RunParallel(func(pb *testing.PB) {
+               for pb.Next() {
+                       v := new(int)
+                       runtime.SetFinalizer(v, fin)
+               }
+       })
+}
+
+// One chunk must be exactly one sizeclass in size.
+// It should be a sizeclass not used much by others, so we
+// have a greater chance of finding adjacent ones.
+// size class 19: 320 byte objects, 25 per page, 1 page alloc at a time
+const objsize = 320
+
+type objtype [objsize]byte
+
+func adjChunks() (*objtype, *objtype) {
+       var s []*objtype
+
+       for {
+               c := new(objtype)
+               for _, d := range s {
+                       if uintptr(unsafe.Pointer(c))+unsafe.Sizeof(*c) == uintptr(unsafe.Pointer(d)) {
+                               return c, d
                        }
-                       wg.Done()
-               }()
+                       if uintptr(unsafe.Pointer(d))+unsafe.Sizeof(*c) == uintptr(unsafe.Pointer(c)) {
+                               return d, c
+                       }
+               }
+               s = append(s, c)
+       }
+}
+
+// Make sure an empty slice on the stack doesn't pin the next object in memory.
+func TestEmptySlice(t *testing.T) {
+       if true { // disable until bug 7564 is fixed.
+               return
+       }
+       x, y := adjChunks()
+
+       // the pointer inside xs points to y.
+       xs := x[objsize:] // change objsize to objsize-1 and the test passes
+
+       fin := make(chan bool, 1)
+       runtime.SetFinalizer(y, func(z *objtype) { fin <- true })
+       runtime.GC()
+       select {
+       case <-fin:
+       case <-time.After(4 * time.Second):
+               t.Errorf("finalizer of next object in memory didn't run")
+       }
+       xsglobal = xs // keep empty slice alive until here
+}
+
+var xsglobal []byte
+
+func adjStringChunk() (string, *objtype) {
+       b := make([]byte, objsize)
+       for {
+               s := string(b)
+               t := new(objtype)
+               p := *(*uintptr)(unsafe.Pointer(&s))
+               q := uintptr(unsafe.Pointer(t))
+               if p+objsize == q {
+                       return s, t
+               }
+       }
+}
+
+// Make sure an empty string on the stack doesn't pin the next object in memory.
+func TestEmptyString(t *testing.T) {
+       if runtime.Compiler == "gccgo" {
+               t.Skip("skipping for gccgo")
+       }
+
+       x, y := adjStringChunk()
+
+       ss := x[objsize:] // change objsize to objsize-1 and the test passes
+       fin := make(chan bool, 1)
+       // set finalizer on string contents of y
+       runtime.SetFinalizer(y, func(z *objtype) { fin <- true })
+       runtime.GC()
+       select {
+       case <-fin:
+       case <-time.After(4 * time.Second):
+               t.Errorf("finalizer of next string in memory didn't run")
        }
-       wg.Wait()
+       ssglobal = ss // keep 0-length string live until here
+}
+
+var ssglobal string
+
+// Test for issue 7656.
+func TestFinalizerOnGlobal(t *testing.T) {
+       runtime.SetFinalizer(Foo1, func(p *Object1) {})
+       runtime.SetFinalizer(Foo2, func(p *Object2) {})
+       runtime.SetFinalizer(Foo1, nil)
+       runtime.SetFinalizer(Foo2, nil)
 }
+
+type Object1 struct {
+       Something []byte
+}
+
+type Object2 struct {
+       Something byte
+}
+
+var (
+       Foo2 = &Object2{}
+       Foo1 = &Object1{}
+)
index b15054662225636e1aeb3b1bc11a155d0e15b4f2..624485d18bfc112b823ec23f0beabfb548659280 100644 (file)
@@ -9,7 +9,19 @@ func gc_m_ptr(ret *interface{}) {
        *ret = (*m)(nil)
 }
 
+// Called from C. Returns the Go type *g.
+func gc_g_ptr(ret *interface{}) {
+       *ret = (*g)(nil)
+}
+
 // Called from C. Returns the Go type *itab.
 func gc_itab_ptr(ret *interface{}) {
        *ret = (*itab)(nil)
 }
+
+func timenow() (sec int64, nsec int32)
+
+func gc_unixnanotime(now *int64) {
+       sec, nsec := timenow()
+       *now = sec*1e9 + int64(nsec)
+}
index a3d5b00860c04b7c03d036899a14af8b9b4d62eb..3b171877a6f5b77396159a5936e92dae2fd32e5d 100644 (file)
@@ -9,7 +9,6 @@ package runtime_test
 
 import (
        "runtime"
-       "sync/atomic"
        "testing"
 )
 
@@ -31,28 +30,17 @@ func BenchmarkSyscallExcessWork(b *testing.B) {
 }
 
 func benchmarkSyscall(b *testing.B, work, excess int) {
-       const CallsPerSched = 1000
-       procs := runtime.GOMAXPROCS(-1) * excess
-       N := int32(b.N / CallsPerSched)
-       c := make(chan bool, procs)
-       for p := 0; p < procs; p++ {
-               go func() {
-                       foo := 42
-                       for atomic.AddInt32(&N, -1) >= 0 {
-                               runtime.Gosched()
-                               for g := 0; g < CallsPerSched; g++ {
-                                       runtime.Entersyscall()
-                                       for i := 0; i < work; i++ {
-                                               foo *= 2
-                                               foo /= 2
-                                       }
-                                       runtime.Exitsyscall()
-                               }
+       b.SetParallelism(excess)
+       b.RunParallel(func(pb *testing.PB) {
+               foo := 42
+               for pb.Next() {
+                       runtime.Entersyscall()
+                       for i := 0; i < work; i++ {
+                               foo *= 2
+                               foo /= 2
                        }
-                       c <- foo == 42
-               }()
-       }
-       for p := 0; p < procs; p++ {
-               <-c
-       }
+                       runtime.Exitsyscall()
+               }
+               _ = foo
+       })
 }
index 98080457cb581e14a3e4b8bbfcb70f0263eb727a..26aa0b8be5cf0fc5f12fcbc082b0d42d5daa2133 100644 (file)
@@ -20,7 +20,7 @@ import (
        "text/tabwriter"
 )
 
-// BUG(rsc): Profiles are incomplete and inaccuate on NetBSD and OS X.
+// BUG(rsc): Profiles are incomplete and inaccurate on NetBSD and OS X.
 // See http://golang.org/issue/6047 for details.
 
 // A Profile is a collection of stack traces showing the call sequences
index cce60e1be3b94c6f1ce62226be4c4ea67fb0ffd0..f714472fd55b106bb929c840ee46d96a1f29463a 100644 (file)
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// +build !nacl
+
 package pprof_test
 
 import (
index bdcb199d72782da49ac0401102d85a51865dc341..4f364dc4636d52b59eb5be775fa91a48d64d0ab6 100644 (file)
@@ -373,24 +373,11 @@ func TestSchedLocalQueueSteal(t *testing.T) {
 }
 
 func benchmarkStackGrowth(b *testing.B, rec int) {
-       const CallsPerSched = 1000
-       procs := runtime.GOMAXPROCS(-1)
-       N := int32(b.N / CallsPerSched)
-       c := make(chan bool, procs)
-       for p := 0; p < procs; p++ {
-               go func() {
-                       for atomic.AddInt32(&N, -1) >= 0 {
-                               runtime.Gosched()
-                               for g := 0; g < CallsPerSched; g++ {
-                                       stackGrowthRecursive(rec)
-                               }
-                       }
-                       c <- true
-               }()
-       }
-       for p := 0; p < procs; p++ {
-               <-c
-       }
+       b.RunParallel(func(pb *testing.PB) {
+               for pb.Next() {
+                       stackGrowthRecursive(rec)
+               }
+       })
 }
 
 func BenchmarkStackGrowth(b *testing.B) {
index 1702298aed1a40c757bb71b5ad334965830b4e74..7dae95a2d4f5894fc8dbcfa9633ed89e953aea37 100644 (file)
@@ -10,9 +10,11 @@ import (
        // "os"
        // "os/exec"
        // . "runtime"
+       "runtime/debug"
        // "strconv"
        // "strings"
        "testing"
+       "unsafe"
 )
 
 var errf error
@@ -95,10 +97,10 @@ func BenchmarkDeferMany(b *testing.B) {
 // The value reported will include the padding between runtime.gogo and the
 // next function in memory. That's fine.
 func TestRuntimeGogoBytes(t *testing.T) {
-       // TODO(brainman): delete when issue 6973 is fixed.
-       if GOOS == "windows" {
-               t.Skip("skipping broken test on windows")
+       if GOOS == "nacl" {
+               t.Skip("skipping on nacl")
        }
+
        dir, err := ioutil.TempDir("", "go-build")
        if err != nil {
                t.Fatalf("failed to create temp directory: %v", err)
@@ -134,3 +136,77 @@ func TestRuntimeGogoBytes(t *testing.T) {
 func TestStopCPUProfilingWithProfilerOff(t *testing.T) {
        SetCPUProfileRate(0)
 }
+
+// Addresses to test for faulting behavior.
+// This is less a test of SetPanicOnFault and more a check that
+// the operating system and the runtime can process these faults
+// correctly. That is, we're indirectly testing that without SetPanicOnFault
+// these would manage to turn into ordinary crashes.
+// Note that these are truncated on 32-bit systems, so the bottom 32 bits
+// of the larger addresses must themselves be invalid addresses.
+// We might get unlucky and the OS might have mapped one of these
+// addresses, but probably not: they're all in the first page, very high
+// adderesses that normally an OS would reserve for itself, or malformed
+// addresses. Even so, we might have to remove one or two on different
+// systems. We will see.
+
+var faultAddrs = []uint64{
+       // low addresses
+       0,
+       1,
+       0xfff,
+       // high (kernel) addresses
+       // or else malformed.
+       0xffffffffffffffff,
+       0xfffffffffffff001,
+       // no 0xffffffffffff0001; 0xffff0001 is mapped for 32-bit user space on OS X
+       // no 0xfffffffffff00001; 0xfff00001 is mapped for 32-bit user space sometimes on Linux
+       0xffffffffff000001,
+       0xfffffffff0000001,
+       0xffffffff00000001,
+       0xfffffff000000001,
+       0xffffff0000000001,
+       0xfffff00000000001,
+       0xffff000000000001,
+       0xfff0000000000001,
+       0xff00000000000001,
+       0xf000000000000001,
+       0x8000000000000001,
+}
+
+func TestSetPanicOnFault(t *testing.T) {
+       // This currently results in a fault in the signal trampoline on
+       // dragonfly/386 - see issue 7421.
+       if GOOS == "dragonfly" && GOARCH == "386" {
+               t.Skip("skipping test on dragonfly/386")
+       }
+
+       old := debug.SetPanicOnFault(true)
+       defer debug.SetPanicOnFault(old)
+
+       for _, addr := range faultAddrs {
+               if Compiler == "gccgo" && GOARCH == "386" && (addr&0xff000000) != 0 {
+                       // On gccgo these addresses can be used for
+                       // the thread stack.
+                       continue
+               }
+               testSetPanicOnFault(t, uintptr(addr))
+       }
+}
+
+func testSetPanicOnFault(t *testing.T, addr uintptr) {
+       if GOOS == "nacl" {
+               t.Skip("nacl doesn't seem to fault on high addresses")
+       }
+
+       defer func() {
+               if err := recover(); err == nil {
+                       t.Fatalf("did not find error in recover")
+               }
+       }()
+
+       var p *int
+       p = (*int)(unsafe.Pointer(addr))
+       println(*p)
+       t.Fatalf("still here - should have faulted on address %#x", addr)
+}
index eba34e4a6badc9843b693468587952275fe6cd2f..1211f22257540f5fbff154821233598cd6c346a4 100644 (file)
@@ -27,6 +27,7 @@ type rtype struct {
        string *string
        *uncommonType
        ptrToThis *rtype
+       zero      unsafe.Pointer
 }
 
 type _method struct {
index 242c5fffa217e2af515283f5eec189e192cde647..e980c295c32f61b8bafdf9761943ea71d5554989 100644 (file)
@@ -287,7 +287,7 @@ func StringsAreSorted(a []string) bool { return IsSorted(StringSlice(a)) }
 // Notes on stable sorting:
 // The used algorithms are simple and provable correct on all input and use
 // only logarithmic additional stack space.  They perform well if compared
-// experimentaly to other stable in-place sorting algorithms.
+// experimentally to other stable in-place sorting algorithms.
 //
 // Remarks on other algorithms evaluated:
 //  - GCC's 4.6.3 stable_sort with merge_without_buffer from libstdc++:
index 2d0db7155f75bc986231c706b91928566dae10f5..cbf0380ec829e5a9f13cef27d442a722dccee639 100644 (file)
@@ -142,9 +142,11 @@ Error:
 //
 // The errors that ParseInt returns have concrete type *NumError
 // and include err.Num = s.  If s is empty or contains invalid
-// digits, err.Err = ErrSyntax; if the value corresponding
-// to s cannot be represented by a signed integer of the
-// given size, err.Err = ErrRange.
+// digits, err.Err = ErrSyntax and the returned value is 0;
+// if the value corresponding to s cannot be represented by a
+// signed integer of the given size, err.Err = ErrRange and the
+// returned value is the maximum magnitude integer of the
+// appropriate bitSize and sign.
 func ParseInt(s string, base int, bitSize int) (i int64, err error) {
        const fnParseInt = "ParseInt"
 
index 7d6cdcf0b54985ce3133556be18500bab4db8da3..aded7e5930c329992c1ed3b865e48d5bb2f5accc 100644 (file)
@@ -144,7 +144,8 @@ func AppendQuoteRuneToASCII(dst []byte, r rune) []byte {
 // characters other than space and tab.
 func CanBackquote(s string) bool {
        for i := 0; i < len(s); i++ {
-               if (s[i] < ' ' && s[i] != '\t') || s[i] == '`' {
+               c := s[i]
+               if (c < ' ' && c != '\t') || c == '`' || c == '\u007F' {
                        return false
                }
        }
index 61d9bf9a571428e351bb4cdde9689f45dd17f7fc..e4b5b6b9fd2d0d2fb9e05ba5f9e1ba589d5b8458 100644 (file)
@@ -140,6 +140,7 @@ var canbackquotetests = []canBackquoteTest{
        {string(29), false},
        {string(30), false},
        {string(31), false},
+       {string(0x7F), false},
        {`' !"#$%&'()*+,-./:;<=>?@[\]^_{|}~`, true},
        {`0123456789`, true},
        {`ABCDEFGHIJKLMNOPQRSTUVWXYZ`, true},
index 11240efc0780f7223d1e88a8b9161fb481b9414e..82df974398c57c04352bcd0d7bc10500624b5484 100644 (file)
@@ -15,40 +15,41 @@ import (
 // from a string.
 type Reader struct {
        s        string
-       i        int // current reading index
-       prevRune int // index of previous rune; or < 0
+       i        int64 // current reading index
+       prevRune int   // index of previous rune; or < 0
 }
 
 // Len returns the number of bytes of the unread portion of the
 // string.
 func (r *Reader) Len() int {
-       if r.i >= len(r.s) {
+       if r.i >= int64(len(r.s)) {
                return 0
        }
-       return len(r.s) - r.i
+       return int(int64(len(r.s)) - r.i)
 }
 
 func (r *Reader) Read(b []byte) (n int, err error) {
        if len(b) == 0 {
                return 0, nil
        }
-       if r.i >= len(r.s) {
+       if r.i >= int64(len(r.s)) {
                return 0, io.EOF
        }
-       n = copy(b, r.s[r.i:])
-       r.i += n
        r.prevRune = -1
+       n = copy(b, r.s[r.i:])
+       r.i += int64(n)
        return
 }
 
 func (r *Reader) ReadAt(b []byte, off int64) (n int, err error) {
+       // cannot modify state - see io.ReaderAt
        if off < 0 {
-               return 0, errors.New("strings: invalid offset")
+               return 0, errors.New("strings.Reader.ReadAt: negative offset")
        }
        if off >= int64(len(r.s)) {
                return 0, io.EOF
        }
-       n = copy(b, r.s[int(off):])
+       n = copy(b, r.s[off:])
        if n < len(b) {
                err = io.EOF
        }
@@ -56,49 +57,51 @@ func (r *Reader) ReadAt(b []byte, off int64) (n int, err error) {
 }
 
 func (r *Reader) ReadByte() (b byte, err error) {
-       if r.i >= len(r.s) {
+       r.prevRune = -1
+       if r.i >= int64(len(r.s)) {
                return 0, io.EOF
        }
        b = r.s[r.i]
        r.i++
-       r.prevRune = -1
        return
 }
 
 func (r *Reader) UnreadByte() error {
+       r.prevRune = -1
        if r.i <= 0 {
-               return errors.New("strings.Reader: at beginning of string")
+               return errors.New("strings.Reader.UnreadByte: at beginning of string")
        }
        r.i--
-       r.prevRune = -1
        return nil
 }
 
 func (r *Reader) ReadRune() (ch rune, size int, err error) {
-       if r.i >= len(r.s) {
+       if r.i >= int64(len(r.s)) {
+               r.prevRune = -1
                return 0, 0, io.EOF
        }
-       r.prevRune = r.i
+       r.prevRune = int(r.i)
        if c := r.s[r.i]; c < utf8.RuneSelf {
                r.i++
                return rune(c), 1, nil
        }
        ch, size = utf8.DecodeRuneInString(r.s[r.i:])
-       r.i += size
+       r.i += int64(size)
        return
 }
 
 func (r *Reader) UnreadRune() error {
        if r.prevRune < 0 {
-               return errors.New("strings.Reader: previous operation was not ReadRune")
+               return errors.New("strings.Reader.UnreadRune: previous operation was not ReadRune")
        }
-       r.i = r.prevRune
+       r.i = int64(r.prevRune)
        r.prevRune = -1
        return nil
 }
 
 // Seek implements the io.Seeker interface.
 func (r *Reader) Seek(offset int64, whence int) (int64, error) {
+       r.prevRune = -1
        var abs int64
        switch whence {
        case 0:
@@ -108,22 +111,19 @@ func (r *Reader) Seek(offset int64, whence int) (int64, error) {
        case 2:
                abs = int64(len(r.s)) + offset
        default:
-               return 0, errors.New("strings: invalid whence")
+               return 0, errors.New("strings.Reader.Seek: invalid whence")
        }
        if abs < 0 {
-               return 0, errors.New("strings: negative position")
-       }
-       if abs >= 1<<31 {
-               return 0, errors.New("strings: position out of range")
+               return 0, errors.New("strings.Reader.Seek: negative position")
        }
-       r.i = int(abs)
+       r.i = abs
        return abs, nil
 }
 
 // WriteTo implements the io.WriterTo interface.
 func (r *Reader) WriteTo(w io.Writer) (n int64, err error) {
        r.prevRune = -1
-       if r.i >= len(r.s) {
+       if r.i >= int64(len(r.s)) {
                return 0, nil
        }
        s := r.s[r.i:]
@@ -131,7 +131,7 @@ func (r *Reader) WriteTo(w io.Writer) (n int64, err error) {
        if m > len(s) {
                panic("strings.Reader.WriteTo: invalid WriteString count")
        }
-       r.i += m
+       r.i += int64(m)
        n = int64(m)
        if m != len(s) && err == nil {
                err = io.ErrShortWrite
index 4fdddcdb58e47719271fc1e23ec4c4468d1eb8d9..bee90eb2585faf11f77fb80c85dfaccc74e606d0 100644 (file)
@@ -10,6 +10,7 @@ import (
        "io"
        "os"
        "strings"
+       "sync"
        "testing"
 )
 
@@ -26,9 +27,9 @@ func TestReader(t *testing.T) {
                {seek: os.SEEK_SET, off: 0, n: 20, want: "0123456789"},
                {seek: os.SEEK_SET, off: 1, n: 1, want: "1"},
                {seek: os.SEEK_CUR, off: 1, wantpos: 3, n: 2, want: "34"},
-               {seek: os.SEEK_SET, off: -1, seekerr: "strings: negative position"},
-               {seek: os.SEEK_SET, off: 1<<31 - 1},
-               {seek: os.SEEK_CUR, off: 1, seekerr: "strings: position out of range"},
+               {seek: os.SEEK_SET, off: -1, seekerr: "strings.Reader.Seek: negative position"},
+               {seek: os.SEEK_SET, off: 1 << 33, wantpos: 1 << 33},
+               {seek: os.SEEK_CUR, off: 1, wantpos: 1<<33 + 1},
                {seek: os.SEEK_SET, n: 5, want: "01234"},
                {seek: os.SEEK_CUR, n: 5, want: "56789"},
                {seek: os.SEEK_END, off: -1, n: 1, wantpos: 9, want: "9"},
@@ -60,6 +61,16 @@ func TestReader(t *testing.T) {
        }
 }
 
+func TestReadAfterBigSeek(t *testing.T) {
+       r := strings.NewReader("0123456789")
+       if _, err := r.Seek(1<<31+5, os.SEEK_SET); err != nil {
+               t.Fatal(err)
+       }
+       if n, err := r.Read(make([]byte, 10)); n != 0 || err != io.EOF {
+               t.Errorf("Read = %d, %v; want 0, EOF", n, err)
+       }
+}
+
 func TestReaderAt(t *testing.T) {
        r := strings.NewReader("0123456789")
        tests := []struct {
@@ -73,7 +84,7 @@ func TestReaderAt(t *testing.T) {
                {1, 9, "123456789", nil},
                {11, 10, "", io.EOF},
                {0, 0, "", nil},
-               {-1, 0, "", "strings: invalid offset"},
+               {-1, 0, "", "strings.Reader.ReadAt: negative offset"},
        }
        for i, tt := range tests {
                b := make([]byte, tt.n)
@@ -88,6 +99,43 @@ func TestReaderAt(t *testing.T) {
        }
 }
 
+func TestReaderAtConcurrent(t *testing.T) {
+       // Test for the race detector, to verify ReadAt doesn't mutate
+       // any state.
+       r := strings.NewReader("0123456789")
+       var wg sync.WaitGroup
+       for i := 0; i < 5; i++ {
+               wg.Add(1)
+               go func(i int) {
+                       defer wg.Done()
+                       var buf [1]byte
+                       r.ReadAt(buf[:], int64(i))
+               }(i)
+       }
+       wg.Wait()
+}
+
+func TestEmptyReaderConcurrent(t *testing.T) {
+       // Test for the race detector, to verify a Read that doesn't yield any bytes
+       // is okay to use from multiple goroutines. This was our historic behavior.
+       // See golang.org/issue/7856
+       r := strings.NewReader("")
+       var wg sync.WaitGroup
+       for i := 0; i < 5; i++ {
+               wg.Add(2)
+               go func() {
+                       defer wg.Done()
+                       var buf [1]byte
+                       r.Read(buf[:])
+               }()
+               go func() {
+                       defer wg.Done()
+                       r.Read(nil)
+               }()
+       }
+       wg.Wait()
+}
+
 func TestWriteTo(t *testing.T) {
        const str = "0123456789"
        for i := 0; i <= len(str); i++ {
index 54c9323e0489137a5b19f8f5ab2974c8d2d89014..3e05d2057bef48181cc80dcdbc76e0f6b7862ad0 100644 (file)
@@ -492,7 +492,7 @@ func (r *byteStringReplacer) Replace(s string) string {
        for i := 0; i < len(s); i++ {
                b := s[i]
                if r.old[b>>5]&uint32(1<<(b&31)) != 0 {
-                       n := copy(bi[:], r.new[b])
+                       n := copy(bi, r.new[b])
                        bi = bi[n:]
                } else {
                        bi[0] = b
index a5be2f9bed5e0e56f43d5a7b5e5db0dcfcf6788e..e40a18015e21e355322153e441bbdc911fcc5d31 100644 (file)
@@ -652,7 +652,7 @@ func equal(m string, s1, s2 string, t *testing.T) bool {
        e1 := Split(s1, "")
        e2 := Split(s2, "")
        for i, c1 := range e1 {
-               if i > len(e2) {
+               if i >= len(e2) {
                        break
                }
                r1, _ := utf8.DecodeRuneInString(c1)
@@ -858,6 +858,32 @@ func TestReadRune(t *testing.T) {
        }
 }
 
+var UnreadRuneErrorTests = []struct {
+       name string
+       f    func(*Reader)
+}{
+       {"Read", func(r *Reader) { r.Read([]byte{0}) }},
+       {"ReadByte", func(r *Reader) { r.ReadByte() }},
+       {"UnreadRune", func(r *Reader) { r.UnreadRune() }},
+       {"Seek", func(r *Reader) { r.Seek(0, 1) }},
+       {"WriteTo", func(r *Reader) { r.WriteTo(&bytes.Buffer{}) }},
+}
+
+func TestUnreadRuneError(t *testing.T) {
+       for _, tt := range UnreadRuneErrorTests {
+               reader := NewReader("0123456789")
+               if _, _, err := reader.ReadRune(); err != nil {
+                       // should not happen
+                       t.Fatal(err)
+               }
+               tt.f(reader)
+               err := reader.UnreadRune()
+               if err == nil {
+                       t.Errorf("Unreading after %s: expected error", tt.name)
+               }
+       }
+}
+
 var ReplaceTests = []struct {
        in       string
        old, new string
index 06dd5f7ce899925613e92b5c680daa95bfe760e2..d2af4f4187143b36745b420da5b06fb5fff46b57 100644 (file)
@@ -813,7 +813,7 @@ func hammerSwapUintptr32(uaddr *uint32, count int) {
                new := uintptr(seed+i)<<16 | uintptr(seed+i)<<16>>16
                old := SwapUintptr(addr, new)
                if old>>16 != old<<16>>16 {
-                       panic(fmt.Sprintf("SwapUintptr is not atomic: %v", old))
+                       panic(fmt.Sprintf("SwapUintptr is not atomic: %#08x", old))
                }
        }
 }
@@ -827,7 +827,7 @@ func hammerSwapPointer32(uaddr *uint32, count int) {
                new := uintptr(seed+i)<<16 | uintptr(seed+i)<<16>>16
                old := uintptr(SwapPointer(addr, unsafe.Pointer(new)))
                if old>>16 != old<<16>>16 {
-                       panic(fmt.Sprintf("SwapPointer is not atomic: %v", old))
+                       panic(fmt.Sprintf("SwapPointer is not atomic: %#08x", old))
                }
        }
 }
@@ -1465,6 +1465,9 @@ func TestUnaligned64(t *testing.T) {
 }
 
 func TestNilDeref(t *testing.T) {
+       if p := runtime.GOOS + "/" + runtime.GOARCH; p == "freebsd/arm" || p == "netbsd/arm" {
+               t.Skipf("issue 7338: skipping test on %q", p)
+       }
        funcs := [...]func(){
                func() { CompareAndSwapInt32(nil, 0, 0) },
                func() { CompareAndSwapInt64(nil, 0, 0) },
index bf78c6f609ca67c7e0ce42822521900dc089ac61..151b25c10fce9c9eacf076710145676a81c1f802 100644 (file)
@@ -9,7 +9,6 @@ package sync_test
 import (
        "runtime"
        . "sync"
-       "sync/atomic"
        "testing"
 )
 
@@ -90,63 +89,34 @@ func BenchmarkMutexUncontended(b *testing.B) {
                Mutex
                pad [128]uint8
        }
-       const CallsPerSched = 1000
-       procs := runtime.GOMAXPROCS(-1)
-       N := int32(b.N / CallsPerSched)
-       c := make(chan bool, procs)
-       for p := 0; p < procs; p++ {
-               go func() {
-                       var mu PaddedMutex
-                       for atomic.AddInt32(&N, -1) >= 0 {
-                               runtime.Gosched()
-                               for g := 0; g < CallsPerSched; g++ {
-                                       mu.Lock()
-                                       mu.Unlock()
-                               }
-                       }
-                       c <- true
-               }()
-       }
-       for p := 0; p < procs; p++ {
-               <-c
-       }
+       b.RunParallel(func(pb *testing.PB) {
+               var mu PaddedMutex
+               for pb.Next() {
+                       mu.Lock()
+                       mu.Unlock()
+               }
+       })
 }
 
 func benchmarkMutex(b *testing.B, slack, work bool) {
-       const (
-               CallsPerSched  = 1000
-               LocalWork      = 100
-               GoroutineSlack = 10
-       )
-       procs := runtime.GOMAXPROCS(-1)
+       var mu Mutex
        if slack {
-               procs *= GoroutineSlack
+               b.SetParallelism(10)
        }
-       N := int32(b.N / CallsPerSched)
-       c := make(chan bool, procs)
-       var mu Mutex
-       for p := 0; p < procs; p++ {
-               go func() {
-                       foo := 0
-                       for atomic.AddInt32(&N, -1) >= 0 {
-                               runtime.Gosched()
-                               for g := 0; g < CallsPerSched; g++ {
-                                       mu.Lock()
-                                       mu.Unlock()
-                                       if work {
-                                               for i := 0; i < LocalWork; i++ {
-                                                       foo *= 2
-                                                       foo /= 2
-                                               }
-                                       }
+       b.RunParallel(func(pb *testing.PB) {
+               foo := 0
+               for pb.Next() {
+                       mu.Lock()
+                       mu.Unlock()
+                       if work {
+                               for i := 0; i < 100; i++ {
+                                       foo *= 2
+                                       foo /= 2
                                }
                        }
-                       c <- foo == 42
-               }()
-       }
-       for p := 0; p < procs; p++ {
-               <-c
-       }
+               }
+               _ = foo
+       })
 }
 
 func BenchmarkMutex(b *testing.B) {
index 183069a1a234dd9a8643b4d37fab25fe2c0ff10b..8afda82f3e18dc74f77209c4d652eaa9a49c4d48 100644 (file)
@@ -5,9 +5,7 @@
 package sync_test
 
 import (
-       "runtime"
        . "sync"
-       "sync/atomic"
        "testing"
 )
 
@@ -62,24 +60,11 @@ func TestOncePanic(t *testing.T) {
 }
 
 func BenchmarkOnce(b *testing.B) {
-       const CallsPerSched = 1000
-       procs := runtime.GOMAXPROCS(-1)
-       N := int32(b.N / CallsPerSched)
        var once Once
        f := func() {}
-       c := make(chan bool, procs)
-       for p := 0; p < procs; p++ {
-               go func() {
-                       for atomic.AddInt32(&N, -1) >= 0 {
-                               runtime.Gosched()
-                               for g := 0; g < CallsPerSched; g++ {
-                                       once.Do(f)
-                               }
-                       }
-                       c <- true
-               }()
-       }
-       for p := 0; p < procs; p++ {
-               <-c
-       }
+       b.RunParallel(func(pb *testing.PB) {
+               for pb.Next() {
+                       once.Do(f)
+               }
+       })
 }
index ca49d21a0d1df969b97c29c9bf96290fcd023326..1f08707cd42269dbc654d4a2cf9e8f88753984a4 100644 (file)
@@ -10,64 +10,51 @@ import (
        "unsafe"
 )
 
-const (
-       cacheLineSize = 128
-       poolLocalSize = 2 * cacheLineSize
-       poolLocalCap  = poolLocalSize/unsafe.Sizeof(*(*interface{})(nil)) - 1
-)
-
-// A Pool is a set of temporary objects that may be individually saved
-// and retrieved.
+// A Pool is a set of temporary objects that may be individually saved and
+// retrieved.
 //
-// Any item stored in the Pool may be removed automatically by the
-// implementation at any time without notification.
-// If the Pool holds the only reference when this happens, the item
-// might be deallocated.
+// Any item stored in the Pool may be removed automatically at any time without
+// notification. If the Pool holds the only reference when this happens, the
+// item might be deallocated.
 //
 // A Pool is safe for use by multiple goroutines simultaneously.
 //
-// Pool's intended use is for free lists maintained in global variables,
-// typically accessed by multiple goroutines simultaneously. Using a
-// Pool instead of a custom free list allows the runtime to reclaim
-// entries from the pool when it makes sense to do so. An
-// appropriate use of sync.Pool is to create a pool of temporary buffers
-// shared between independent clients of a global resource. On the
-// other hand, if a free list is maintained as part of an object used
-// only by a single client and freed when the client completes,
-// implementing that free list as a Pool is not appropriate.
+// Pool's purpose is to cache allocated but unused items for later reuse,
+// relieving pressure on the garbage collector. That is, it makes it easy to
+// build efficient, thread-safe free lists. However, it is not suitable for all
+// free lists.
+//
+// An appropriate use of a Pool is to manage a group of temporary items
+// silently shared among and potentially reused by concurrent independent
+// clients of a package. Pool provides a way to amortize allocation overhead
+// across many clients.
+//
+// An example of good use of a Pool is in the fmt package, which maintains a
+// dynamically-sized store of temporary output buffers. The store scales under
+// load (when many goroutines are actively printing) and shrinks when
+// quiescent.
+//
+// On the other hand, a free list maintained as part of a short-lived object is
+// not a suitable use for a Pool, since the overhead does not amortize well in
+// that scenario. It is more efficient to have such objects implement their own
+// free list.
 //
-// This is an experimental type and might not be released.
 type Pool struct {
-       // The following fields are known to runtime.
-       next         *Pool      // for use by runtime
-       local        *poolLocal // local fixed-size per-P pool, actually an array
-       localSize    uintptr    // size of the local array
-       globalOffset uintptr    // offset of global
-       // The rest is not known to runtime.
+       local     unsafe.Pointer // local fixed-size per-P pool, actual type is [P]poolLocal
+       localSize uintptr        // size of the local array
 
        // New optionally specifies a function to generate
        // a value when Get would otherwise return nil.
        // It may not be changed concurrently with calls to Get.
        New func() interface{}
-
-       pad [cacheLineSize]byte
-       // Read-mostly date above this point, mutable data follows.
-       mu     Mutex
-       global []interface{} // global fallback pool
 }
 
 // Local per-P Pool appendix.
 type poolLocal struct {
-       tail   int
-       unused int
-       buf    [poolLocalCap]interface{}
-}
-
-func init() {
-       var v poolLocal
-       if unsafe.Sizeof(v) != poolLocalSize {
-               panic("sync: incorrect pool size")
-       }
+       private interface{}   // Can be used only by the respective P.
+       shared  []interface{} // Can be used by any P.
+       Mutex                 // Protects shared.
+       pad     [128]byte     // Prevents false sharing.
 }
 
 // Put adds x to the pool.
@@ -82,14 +69,17 @@ func (p *Pool) Put(x interface{}) {
                return
        }
        l := p.pin()
-       t := l.tail
-       if t < int(poolLocalCap) {
-               l.buf[t] = x
-               l.tail = t + 1
-               runtime_procUnpin()
+       if l.private == nil {
+               l.private = x
+               x = nil
+       }
+       runtime_procUnpin()
+       if x == nil {
                return
        }
-       p.putSlow(l, x)
+       l.Lock()
+       l.shared = append(l.shared, x)
+       l.Unlock()
 }
 
 // Get selects an arbitrary item from the Pool, removes it from the
@@ -108,72 +98,52 @@ func (p *Pool) Get() interface{} {
                return nil
        }
        l := p.pin()
-       t := l.tail
-       if t > 0 {
-               t -= 1
-               x := l.buf[t]
-               l.tail = t
-               runtime_procUnpin()
+       x := l.private
+       l.private = nil
+       runtime_procUnpin()
+       if x != nil {
                return x
        }
-       return p.getSlow()
-}
-
-func (p *Pool) putSlow(l *poolLocal, x interface{}) {
-       // Grab half of items from local pool and put to global pool.
-       // Can not lock the mutex while pinned.
-       const N = int(poolLocalCap/2 + 1)
-       var buf [N]interface{}
-       buf[0] = x
-       for i := 1; i < N; i++ {
-               l.tail--
-               buf[i] = l.buf[l.tail]
+       l.Lock()
+       last := len(l.shared) - 1
+       if last >= 0 {
+               x = l.shared[last]
+               l.shared = l.shared[:last]
        }
-       runtime_procUnpin()
-
-       p.mu.Lock()
-       p.global = append(p.global, buf[:]...)
-       p.mu.Unlock()
+       l.Unlock()
+       if x != nil {
+               return x
+       }
+       return p.getSlow()
 }
 
 func (p *Pool) getSlow() (x interface{}) {
-       // Grab a batch of items from global pool and put to local pool.
-       // Can not lock the mutex while pinned.
-       runtime_procUnpin()
-       p.mu.Lock()
+       // See the comment in pin regarding ordering of the loads.
+       size := atomic.LoadUintptr(&p.localSize) // load-acquire
+       local := p.local                         // load-consume
+       // Try to steal one element from other procs.
        pid := runtime_procPin()
-       s := p.localSize
-       l := p.local
-       if uintptr(pid) < s {
-               l = indexLocal(l, pid)
-               // Get the item to return.
-               last := len(p.global) - 1
+       runtime_procUnpin()
+       for i := 0; i < int(size); i++ {
+               l := indexLocal(local, (pid+i+1)%int(size))
+               l.Lock()
+               last := len(l.shared) - 1
                if last >= 0 {
-                       x = p.global[last]
-                       p.global = p.global[:last]
-               }
-               // Try to refill local pool, we may have been rescheduled to another P.
-               if last > 0 && l.tail == 0 {
-                       n := int(poolLocalCap / 2)
-                       gl := len(p.global)
-                       if n > gl {
-                               n = gl
-                       }
-                       copy(l.buf[:], p.global[gl-n:])
-                       p.global = p.global[:gl-n]
-                       l.tail = n
+                       x = l.shared[last]
+                       l.shared = l.shared[:last]
+                       l.Unlock()
+                       break
                }
+               l.Unlock()
        }
-       runtime_procUnpin()
-       p.mu.Unlock()
 
        if x == nil && p.New != nil {
                x = p.New()
        }
-       return
+       return x
 }
 
-// pin pins current goroutine to P, disables preemption and returns poolLocal pool for the P.
+// pin pins the current goroutine to P, disables preemption and returns poolLocal pool for the P.
 // Caller must call runtime_procUnpin() when done with the pool.
 func (p *Pool) pin() *poolLocal {
        pid := runtime_procPin()
@@ -191,32 +161,63 @@ func (p *Pool) pin() *poolLocal {
 
 func (p *Pool) pinSlow() *poolLocal {
        // Retry under the mutex.
+       // Can not lock the mutex while pinned.
        runtime_procUnpin()
-       p.mu.Lock()
-       defer p.mu.Unlock()
+       allPoolsMu.Lock()
+       defer allPoolsMu.Unlock()
        pid := runtime_procPin()
+       // poolCleanup won't be called while we are pinned.
        s := p.localSize
        l := p.local
        if uintptr(pid) < s {
                return indexLocal(l, pid)
        }
        if p.local == nil {
-               p.globalOffset = unsafe.Offsetof(p.global)
-               runtime_registerPool(p)
+               allPools = append(allPools, p)
        }
        // If GOMAXPROCS changes between GCs, we re-allocate the array and lose the old one.
        size := runtime.GOMAXPROCS(0)
        local := make([]poolLocal, size)
-       atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&p.local)), unsafe.Pointer(&local[0])) // store-release
-       atomic.StoreUintptr(&p.localSize, uintptr(size))                                            // store-release
+       atomic.StorePointer((*unsafe.Pointer)(&p.local), unsafe.Pointer(&local[0])) // store-release
+       atomic.StoreUintptr(&p.localSize, uintptr(size))                            // store-release
        return &local[pid]
 }
 
-func indexLocal(l *poolLocal, i int) *poolLocal {
-       return (*poolLocal)(unsafe.Pointer(uintptr(unsafe.Pointer(l)) + unsafe.Sizeof(*l)*uintptr(i))) // uh...
+func poolCleanup() {
+       // This function is called with the world stopped, at the beginning of a garbage collection.
+       // It must not allocate and probably should not call any runtime functions.
+       // Defensively zero out everything, 2 reasons:
+       // 1. To prevent false retention of whole Pools.
+       // 2. If GC happens while a goroutine works with l.shared in Put/Get,
+       //    it will retain whole Pool. So next cycle memory consumption would be doubled.
+       for i, p := range allPools {
+               allPools[i] = nil
+               for i := 0; i < int(p.localSize); i++ {
+                       l := indexLocal(p.local, i)
+                       l.private = nil
+                       for j := range l.shared {
+                               l.shared[j] = nil
+                       }
+                       l.shared = nil
+               }
+       }
+       allPools = []*Pool{}
+}
+
+var (
+       allPoolsMu Mutex
+       allPools   []*Pool
+)
+
+func init() {
+       runtime_registerPoolCleanup(poolCleanup)
+}
+
+func indexLocal(l unsafe.Pointer, i int) *poolLocal {
+       return &(*[1000000]poolLocal)(l)[i]
 }
 
 // Implemented in runtime.
-func runtime_registerPool(*Pool)
+func runtime_registerPoolCleanup(cleanup func())
 func runtime_procPin() int
 func runtime_procUnpin()
index 39ba7a913d3c7aeac94ae1e5871a89f37a8703c4..c13477de9045d7e033e279f3166a14188c02d95d 100644 (file)
@@ -25,12 +25,12 @@ func TestPool(t *testing.T) {
        }
        p.Put("a")
        p.Put("b")
-       if g := p.Get(); g != "b" {
-               t.Fatalf("got %#v; want b", g)
-       }
        if g := p.Get(); g != "a" {
                t.Fatalf("got %#v; want a", g)
        }
+       if g := p.Get(); g != "b" {
+               t.Fatalf("got %#v; want b", g)
+       }
        if g := p.Get(); g != nil {
                t.Fatalf("got %#v; want nil", g)
        }
@@ -87,7 +87,7 @@ func TestPoolGC(t *testing.T) {
        }
        for i := 0; i < 5; i++ {
                runtime.GC()
-               time.Sleep(time.Millisecond)
+               time.Sleep(time.Duration(i*100+10) * time.Millisecond)
                // 1 pointer can remain on stack or elsewhere
                if atomic.LoadUint32(&fin) >= N-1 {
                        return
@@ -133,42 +133,24 @@ func TestPoolStress(t *testing.T) {
 
 func BenchmarkPool(b *testing.B) {
        var p Pool
-       var wg WaitGroup
-       n0 := uintptr(b.N)
-       n := n0
-       for i := 0; i < runtime.GOMAXPROCS(0); i++ {
-               wg.Add(1)
-               go func() {
-                       defer wg.Done()
-                       for atomic.AddUintptr(&n, ^uintptr(0)) < n0 {
-                               for b := 0; b < 100; b++ {
-                                       p.Put(1)
-                                       p.Get()
-                               }
-                       }
-               }()
-       }
-       wg.Wait()
+       b.RunParallel(func(pb *testing.PB) {
+               for pb.Next() {
+                       p.Put(1)
+                       p.Get()
+               }
+       })
 }
 
 func BenchmarkPoolOverlflow(b *testing.B) {
        var p Pool
-       var wg WaitGroup
-       n0 := uintptr(b.N)
-       n := n0
-       for i := 0; i < runtime.GOMAXPROCS(0); i++ {
-               wg.Add(1)
-               go func() {
-                       defer wg.Done()
-                       for atomic.AddUintptr(&n, ^uintptr(0)) < n0 {
-                               for b := 0; b < 100; b++ {
-                                       p.Put(1)
-                               }
-                               for b := 0; b < 100; b++ {
-                                       p.Get()
-                               }
+       b.RunParallel(func(pb *testing.PB) {
+               for pb.Next() {
+                       for b := 0; b < 100; b++ {
+                               p.Put(1)
                        }
-               }()
-       }
-       wg.Wait()
+                       for b := 0; b < 100; b++ {
+                               p.Get()
+                       }
+               }
+       })
 }
index 57a8dbee78398f7173384c49f849f17af3c6cb0b..5b7dd3df3f0ff58001c3fd17338f157c214043d9 100644 (file)
@@ -7,7 +7,6 @@ package sync_test
 import (
        "runtime"
        . "sync"
-       "sync/atomic"
        "testing"
 )
 
@@ -16,72 +15,44 @@ func BenchmarkSemaUncontended(b *testing.B) {
                sem uint32
                pad [32]uint32
        }
-       const CallsPerSched = 1000
-       procs := runtime.GOMAXPROCS(-1)
-       N := int32(b.N / CallsPerSched)
-       c := make(chan bool, procs)
-       for p := 0; p < procs; p++ {
-               go func() {
-                       sem := new(PaddedSem)
-                       for atomic.AddInt32(&N, -1) >= 0 {
-                               runtime.Gosched()
-                               for g := 0; g < CallsPerSched; g++ {
-                                       Runtime_Semrelease(&sem.sem)
-                                       Runtime_Semacquire(&sem.sem)
-                               }
-                       }
-                       c <- true
-               }()
-       }
-       for p := 0; p < procs; p++ {
-               <-c
-       }
+       b.RunParallel(func(pb *testing.PB) {
+               sem := new(PaddedSem)
+               for pb.Next() {
+                       Runtime_Semrelease(&sem.sem)
+                       Runtime_Semacquire(&sem.sem)
+               }
+       })
 }
 
 func benchmarkSema(b *testing.B, block, work bool) {
-       const CallsPerSched = 1000
-       const LocalWork = 100
-       procs := runtime.GOMAXPROCS(-1)
-       N := int32(b.N / CallsPerSched)
-       c := make(chan bool, procs)
-       c2 := make(chan bool, procs/2)
        sem := uint32(0)
        if block {
-               for p := 0; p < procs/2; p++ {
-                       go func() {
-                               Runtime_Semacquire(&sem)
-                               c2 <- true
-                       }()
-               }
-       }
-       for p := 0; p < procs; p++ {
+               done := make(chan bool)
                go func() {
-                       foo := 0
-                       for atomic.AddInt32(&N, -1) >= 0 {
-                               runtime.Gosched()
-                               for g := 0; g < CallsPerSched; g++ {
-                                       Runtime_Semrelease(&sem)
-                                       if work {
-                                               for i := 0; i < LocalWork; i++ {
-                                                       foo *= 2
-                                                       foo /= 2
-                                               }
-                                       }
-                                       Runtime_Semacquire(&sem)
-                               }
+                       for p := 0; p < runtime.GOMAXPROCS(0)/2; p++ {
+                               Runtime_Semacquire(&sem)
                        }
-                       c <- foo == 42
-                       Runtime_Semrelease(&sem)
+                       done <- true
+               }()
+               defer func() {
+                       <-done
                }()
        }
-       if block {
-               for p := 0; p < procs/2; p++ {
-                       <-c2
+       b.RunParallel(func(pb *testing.PB) {
+               foo := 0
+               for pb.Next() {
+                       Runtime_Semrelease(&sem)
+                       if work {
+                               for i := 0; i < 100; i++ {
+                                       foo *= 2
+                                       foo /= 2
+                               }
+                       }
+                       Runtime_Semacquire(&sem)
                }
-       }
-       for p := 0; p < procs; p++ {
-               <-c
-       }
+               _ = foo
+               Runtime_Semrelease(&sem)
+       })
 }
 
 func BenchmarkSemaSyntNonblock(b *testing.B) {
index 39d5d6540de7853680e6f813983508436ea1fab6..0436f97239c7a16b823e6b05b4772bc134d064f0 100644 (file)
@@ -160,64 +160,39 @@ func BenchmarkRWMutexUncontended(b *testing.B) {
                RWMutex
                pad [32]uint32
        }
-       const CallsPerSched = 1000
-       procs := runtime.GOMAXPROCS(-1)
-       N := int32(b.N / CallsPerSched)
-       c := make(chan bool, procs)
-       for p := 0; p < procs; p++ {
-               go func() {
-                       var rwm PaddedRWMutex
-                       for atomic.AddInt32(&N, -1) >= 0 {
-                               runtime.Gosched()
-                               for g := 0; g < CallsPerSched; g++ {
-                                       rwm.RLock()
-                                       rwm.RLock()
-                                       rwm.RUnlock()
-                                       rwm.RUnlock()
-                                       rwm.Lock()
-                                       rwm.Unlock()
-                               }
-                       }
-                       c <- true
-               }()
-       }
-       for p := 0; p < procs; p++ {
-               <-c
-       }
+       b.RunParallel(func(pb *testing.PB) {
+               var rwm PaddedRWMutex
+               for pb.Next() {
+                       rwm.RLock()
+                       rwm.RLock()
+                       rwm.RUnlock()
+                       rwm.RUnlock()
+                       rwm.Lock()
+                       rwm.Unlock()
+               }
+       })
 }
 
 func benchmarkRWMutex(b *testing.B, localWork, writeRatio int) {
-       const CallsPerSched = 1000
-       procs := runtime.GOMAXPROCS(-1)
-       N := int32(b.N / CallsPerSched)
-       c := make(chan bool, procs)
        var rwm RWMutex
-       for p := 0; p < procs; p++ {
-               go func() {
-                       foo := 0
-                       for atomic.AddInt32(&N, -1) >= 0 {
-                               runtime.Gosched()
-                               for g := 0; g < CallsPerSched; g++ {
-                                       foo++
-                                       if foo%writeRatio == 0 {
-                                               rwm.Lock()
-                                               rwm.Unlock()
-                                       } else {
-                                               rwm.RLock()
-                                               for i := 0; i != localWork; i += 1 {
-                                                       foo *= 2
-                                                       foo /= 2
-                                               }
-                                               rwm.RUnlock()
-                                       }
+       b.RunParallel(func(pb *testing.PB) {
+               foo := 0
+               for pb.Next() {
+                       foo++
+                       if foo%writeRatio == 0 {
+                               rwm.Lock()
+                               rwm.Unlock()
+                       } else {
+                               rwm.RLock()
+                               for i := 0; i != localWork; i += 1 {
+                                       foo *= 2
+                                       foo /= 2
                                }
+                               rwm.RUnlock()
                        }
-                       c <- foo == 42
-               }()
-       }
-       for p := 0; p < procs; p++ {
-               <-c
-       }
+               }
+               _ = foo
+       })
 }
 
 func BenchmarkRWMutexWrite100(b *testing.B) {
index 22681115cb670064b0d76dc5ce6adf76c565076b..4c64dca393fe0eec79f4631646c576d64fe98620 100644 (file)
@@ -67,11 +67,13 @@ func (wg *WaitGroup) Add(delta int) {
                return
        }
        wg.m.Lock()
-       for i := int32(0); i < wg.waiters; i++ {
-               runtime_Semrelease(wg.sema)
+       if atomic.LoadInt32(&wg.counter) == 0 {
+               for i := int32(0); i < wg.waiters; i++ {
+                       runtime_Semrelease(wg.sema)
+               }
+               wg.waiters = 0
+               wg.sema = nil
        }
-       wg.waiters = 0
-       wg.sema = nil
        wg.m.Unlock()
 }
 
index 84c4cfc37a34a43bf2dc13d40263efbae5ff7789..4c0a043c01ee7d6ce058c59032e4ade62075e6b0 100644 (file)
@@ -5,7 +5,6 @@
 package sync_test
 
 import (
-       "runtime"
        . "sync"
        "sync/atomic"
        "testing"
@@ -61,60 +60,60 @@ func TestWaitGroupMisuse(t *testing.T) {
        t.Fatal("Should panic")
 }
 
+func TestWaitGroupRace(t *testing.T) {
+       // Run this test for about 1ms.
+       for i := 0; i < 1000; i++ {
+               wg := &WaitGroup{}
+               n := new(int32)
+               // spawn goroutine 1
+               wg.Add(1)
+               go func() {
+                       atomic.AddInt32(n, 1)
+                       wg.Done()
+               }()
+               // spawn goroutine 2
+               wg.Add(1)
+               go func() {
+                       atomic.AddInt32(n, 1)
+                       wg.Done()
+               }()
+               // Wait for goroutine 1 and 2
+               wg.Wait()
+               if atomic.LoadInt32(n) != 2 {
+                       t.Fatal("Spurious wakeup from Wait")
+               }
+       }
+}
+
 func BenchmarkWaitGroupUncontended(b *testing.B) {
        type PaddedWaitGroup struct {
                WaitGroup
                pad [128]uint8
        }
-       const CallsPerSched = 1000
-       procs := runtime.GOMAXPROCS(-1)
-       N := int32(b.N / CallsPerSched)
-       c := make(chan bool, procs)
-       for p := 0; p < procs; p++ {
-               go func() {
-                       var wg PaddedWaitGroup
-                       for atomic.AddInt32(&N, -1) >= 0 {
-                               runtime.Gosched()
-                               for g := 0; g < CallsPerSched; g++ {
-                                       wg.Add(1)
-                                       wg.Done()
-                                       wg.Wait()
-                               }
-                       }
-                       c <- true
-               }()
-       }
-       for p := 0; p < procs; p++ {
-               <-c
-       }
+       b.RunParallel(func(pb *testing.PB) {
+               var wg PaddedWaitGroup
+               for pb.Next() {
+                       wg.Add(1)
+                       wg.Done()
+                       wg.Wait()
+               }
+       })
 }
 
 func benchmarkWaitGroupAddDone(b *testing.B, localWork int) {
-       const CallsPerSched = 1000
-       procs := runtime.GOMAXPROCS(-1)
-       N := int32(b.N / CallsPerSched)
-       c := make(chan bool, procs)
        var wg WaitGroup
-       for p := 0; p < procs; p++ {
-               go func() {
-                       foo := 0
-                       for atomic.AddInt32(&N, -1) >= 0 {
-                               runtime.Gosched()
-                               for g := 0; g < CallsPerSched; g++ {
-                                       wg.Add(1)
-                                       for i := 0; i < localWork; i++ {
-                                               foo *= 2
-                                               foo /= 2
-                                       }
-                                       wg.Done()
-                               }
+       b.RunParallel(func(pb *testing.PB) {
+               foo := 0
+               for pb.Next() {
+                       wg.Add(1)
+                       for i := 0; i < localWork; i++ {
+                               foo *= 2
+                               foo /= 2
                        }
-                       c <- foo == 42
-               }()
-       }
-       for p := 0; p < procs; p++ {
-               <-c
-       }
+                       wg.Done()
+               }
+               _ = foo
+       })
 }
 
 func BenchmarkWaitGroupAddDone(b *testing.B) {
@@ -126,34 +125,18 @@ func BenchmarkWaitGroupAddDoneWork(b *testing.B) {
 }
 
 func benchmarkWaitGroupWait(b *testing.B, localWork int) {
-       const CallsPerSched = 1000
-       procs := runtime.GOMAXPROCS(-1)
-       N := int32(b.N / CallsPerSched)
-       c := make(chan bool, procs)
        var wg WaitGroup
-       wg.Add(procs)
-       for p := 0; p < procs; p++ {
-               go wg.Done()
-       }
-       for p := 0; p < procs; p++ {
-               go func() {
-                       foo := 0
-                       for atomic.AddInt32(&N, -1) >= 0 {
-                               runtime.Gosched()
-                               for g := 0; g < CallsPerSched; g++ {
-                                       wg.Wait()
-                                       for i := 0; i < localWork; i++ {
-                                               foo *= 2
-                                               foo /= 2
-                                       }
-                               }
+       b.RunParallel(func(pb *testing.PB) {
+               foo := 0
+               for pb.Next() {
+                       wg.Wait()
+                       for i := 0; i < localWork; i++ {
+                               foo *= 2
+                               foo /= 2
                        }
-                       c <- foo == 42
-               }()
-       }
-       for p := 0; p < procs; p++ {
-               <-c
-       }
+               }
+               _ = foo
+       })
 }
 
 func BenchmarkWaitGroupWait(b *testing.B) {
diff --git a/libgo/go/syscall/consistency_unix_test.go b/libgo/go/syscall/consistency_unix_test.go
deleted file mode 100644 (file)
index 6c9fb82..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2013 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 freebsd dragonfly darwin linux netbsd openbsd
-
-// This file tests that some basic syscalls are consistent across
-// all Unixes.
-
-package syscall_test
-
-import "syscall"
-
-// {Set,Get}priority and needed constants for them
-func _() {
-       var (
-               _ func(int, int, int) error   = syscall.Setpriority
-               _ func(int, int) (int, error) = syscall.Getpriority
-       )
-       const (
-               _ int = syscall.PRIO_USER
-               _ int = syscall.PRIO_PROCESS
-               _ int = syscall.PRIO_PGRP
-       )
-}
-
-// termios functions and constants
-func _() {
-       const (
-               _ int = syscall.TCIFLUSH
-               _ int = syscall.TCIOFLUSH
-               _ int = syscall.TCOFLUSH
-       )
-}
-
-func _() {
-       _ = syscall.Flock_t{
-               Type:   int16(0),
-               Whence: int16(0),
-               Start:  int64(0),
-               Len:    int64(0),
-               Pid:    int32(0),
-       }
-}
index d9fb26b133f1e7c3acc39afd87a37584518ba72e..697bf5499c35c04e667e89a00d2e5a2d54a70e94 100644 (file)
@@ -54,7 +54,7 @@ var nullDir = Dir{
 }
 
 // Null assigns special "don't touch" values to members of d to
-// avoid modifiying them during syscall.Wstat.
+// avoid modifying them during syscall.Wstat.
 func (d *Dir) Null() { *d = nullDir }
 
 // Marshal encodes a 9P stat message corresponding to d into b
index f64202ed1102c9ebbcc7a9daa249e500f1105e33..7f39958437cdb64945869bba6461ebbbe3e04602 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.
 
-// +build darwin dragonfly freebsd linux netbsd openbsd
+// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
 
 // Unix environment variables.
 
index 0cfedb71cf613ffc06fc239b3689cb0cf47268f3..6bf28cc8ac22809926b77fc2fdcb421c32686e41 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.
 
-// +build darwin dragonfly freebsd linux netbsd openbsd
+// +build darwin dragonfly freebsd linux netbsd openbsd solaris
 
 // Fork, exec, wait, etc.
 
@@ -194,7 +194,7 @@ func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error)
                return 0, err
        }
 
-       if runtime.GOOS == "freebsd" && len(argv[0]) > len(argv0) {
+       if (runtime.GOOS == "freebsd" || runtime.GOOS == "dragonfly") && len(argv[0]) > len(argv0) {
                argvp[0] = argv0p
        }
 
index 85f38e08665c4b41edcedbbc58791d64d6ae01ce..d3580a1bc0cb5a0b7a6beb210e8a82215bd335e1 100644 (file)
@@ -244,9 +244,6 @@ func FDZero(set *FdSet) {
 //sys Getpriority(which int, who int) (prio int, err error)
 //getpriority(which _C_int, who _C_int) _C_int
 
-//sysnb        Getrlimit(resource int, rlim *Rlimit) (err error)
-//getrlimit(resource _C_int, rlim *Rlimit) _C_int
-
 //sysnb        Getrusage(who int, rusage *Rusage) (err error)
 //getrusage(who _C_int, rusage *Rusage) _C_int
 
@@ -319,9 +316,6 @@ func Gettimeofday(tv *Timeval) (err error) {
 //sysnb        Setreuid(ruid int, euid int) (err error)
 //setreuid(ruid Uid_t, euid Uid_t) _C_int
 
-//sysnb        Setrlimit(resource int, rlim *Rlimit) (err error)
-//setrlimit(resource int, rlim *Rlimit) _C_int
-
 //sysnb        Setsid() (pid int, err error)
 //setsid() Pid_t
 
index fced6e57ddead70ad9a315c051e57b0f98a08799..c05d3d2a5ec1756cc91cc963a81932d2bf5c9fff 100644 (file)
@@ -12,6 +12,9 @@ package syscall
 //sys  Ftruncate(fd int, length int64) (err error)
 //ftruncate64(fd _C_int, length Offset_t) _C_int
 
+//sysnb        Getrlimit(resource int, rlim *Rlimit) (err error)
+//getrlimit64(resource _C_int, rlim *Rlimit) _C_int
+
 //sys  Lstat(path string, stat *Stat_t) (err error)
 //lstat64(path *byte, stat *Stat_t) _C_int
 
@@ -30,6 +33,9 @@ package syscall
 //sys  Seek(fd int, offset int64, whence int) (off int64, err error)
 //lseek64(fd _C_int, offset Offset_t, whence _C_int) Offset_t
 
+//sysnb        Setrlimit(resource int, rlim *Rlimit) (err error)
+//setrlimit64(resource int, rlim *Rlimit) _C_int
+
 //sys  Stat(path string, stat *Stat_t) (err error)
 //stat64(path *byte, stat *Stat_t) _C_int
 
index 6c98e29873c81a3899875c3dfe0c8de400b38fc2..7de58009393fb31f95d867cd094913f0949cbede 100644 (file)
@@ -13,6 +13,9 @@ package syscall
 //sys  Ftruncate(fd int, length int64) (err error)
 //ftruncate(fd _C_int, length Offset_t) _C_int
 
+//sysnb        Getrlimit(resource int, rlim *Rlimit) (err error)
+//getrlimit(resource _C_int, rlim *Rlimit) _C_int
+
 //sys  Lstat(path string, stat *Stat_t) (err error)
 //lstat(path *byte, stat *Stat_t) _C_int
 
@@ -31,6 +34,9 @@ package syscall
 //sys  Seek(fd int, offset int64, whence int) (off int64, err error)
 //lseek(fd _C_int, offset Offset_t, whence _C_int) Offset_t
 
+//sysnb        Setrlimit(resource int, rlim *Rlimit) (err error)
+//setrlimit(resource int, rlim *Rlimit) _C_int
+
 //sys  Stat(path string, stat *Stat_t) (err error)
 //stat(path *byte, stat *Stat_t) _C_int
 
diff --git a/libgo/go/syscall/mmap_unix_test.go b/libgo/go/syscall/mmap_unix_test.go
new file mode 100644 (file)
index 0000000..01f7783
--- /dev/null
@@ -0,0 +1,22 @@
+// Copyright 2014 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 dragonfly freebsd linux netbsd openbsd
+
+package syscall_test
+
+import (
+       "syscall"
+       "testing"
+)
+
+func TestMmap(t *testing.T) {
+       b, err := syscall.Mmap(-1, 0, syscall.Getpagesize(), syscall.PROT_NONE, syscall.MAP_ANON|syscall.MAP_PRIVATE)
+       if err != nil {
+               t.Fatalf("Mmap: %v", err)
+       }
+       if err := syscall.Munmap(b); err != nil {
+               t.Fatalf("Munmap: %v", err)
+       }
+}
diff --git a/libgo/go/syscall/rlimit_unix_test.go b/libgo/go/syscall/rlimit_unix_test.go
deleted file mode 100644 (file)
index fc9b026..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2013 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 dragonfly freebsd linux netbsd openbsd
-
-package syscall_test
-
-import (
-       "runtime"
-       "syscall"
-       "testing"
-)
-
-func TestRlimit(t *testing.T) {
-       var rlimit, zero syscall.Rlimit
-       err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rlimit)
-       if err != nil {
-               t.Fatalf("Getrlimit: save failed: %v", err)
-       }
-       if zero == rlimit {
-               t.Fatalf("Getrlimit: save failed: got zero value %#v", rlimit)
-       }
-       set := rlimit
-       set.Cur = set.Max - 1
-       err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &set)
-       if err != nil {
-               t.Fatalf("Setrlimit: set failed: %#v %v", set, err)
-       }
-       var get syscall.Rlimit
-       err = syscall.Getrlimit(syscall.RLIMIT_NOFILE, &get)
-       if err != nil {
-               t.Fatalf("Getrlimit: get failed: %v", err)
-       }
-       set = rlimit
-       set.Cur = set.Max - 1
-       if set != get {
-               // Seems like Darwin requires some privilege to
-               // increase the soft limit of rlimit sandbox, though
-               // Setrlimit never reports an error.
-               switch runtime.GOOS {
-               case "darwin":
-               default:
-                       t.Fatalf("Rlimit: change failed: wanted %#v got %#v", set, get)
-               }
-       }
-       err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rlimit)
-       if err != nil {
-               t.Fatalf("Setrlimit: restore failed: %#v %v", rlimit, err)
-       }
-}
index acad7a2be8fe1bc1b7264bb2250c3da89ff5f0f7..79190d2b01b27b5af285c8293f0268474e185f13 100644 (file)
@@ -30,7 +30,7 @@ func (any *anyMessage) toRoutingMessage(b []byte) RoutingMessage {
 }
 
 // InterfaceAnnounceMessage represents a routing message containing
-// network interface arrival and depature information.
+// network interface arrival and departure information.
 type InterfaceAnnounceMessage struct {
        Header IfAnnounceMsghdr
 }
index d8f80316b8c2cfd2e49fd6f6e5fdbc90662deae3..15897b1aca9476f19cbb2350ec47a8fd5be2b3bf 100644 (file)
@@ -8,14 +8,20 @@ package syscall
 
 import "unsafe"
 
+// See http://www.freebsd.org/doc/en/books/porters-handbook/freebsd-versions.html.
+var freebsdVersion uint32
+
+func init() {
+       freebsdVersion, _ = SysctlUint32("kern.osreldate")
+}
+
 func (any *anyMessage) toRoutingMessage(b []byte) RoutingMessage {
        switch any.Type {
        case RTM_ADD, RTM_DELETE, RTM_CHANGE, RTM_GET, RTM_LOSING, RTM_REDIRECT, RTM_MISS, RTM_LOCK, RTM_RESOLVE:
                p := (*RouteMessage)(unsafe.Pointer(any))
                return &RouteMessage{Header: p.Header, Data: b[SizeofRtMsghdr:any.Msglen]}
        case RTM_IFINFO:
-               p := (*InterfaceMessage)(unsafe.Pointer(any))
-               return &InterfaceMessage{Header: p.Header, Data: b[SizeofIfMsghdr:any.Msglen]}
+               return any.parseInterfaceMessage(b)
        case RTM_IFANNOUNCE:
                p := (*InterfaceAnnounceMessage)(unsafe.Pointer(any))
                return &InterfaceAnnounceMessage{Header: p.Header}
@@ -30,7 +36,7 @@ func (any *anyMessage) toRoutingMessage(b []byte) RoutingMessage {
 }
 
 // InterfaceAnnounceMessage represents a routing message containing
-// network interface arrival and depature information.
+// network interface arrival and departure information.
 type InterfaceAnnounceMessage struct {
        Header IfAnnounceMsghdr
 }
diff --git a/libgo/go/syscall/route_freebsd_32bit.go b/libgo/go/syscall/route_freebsd_32bit.go
new file mode 100644 (file)
index 0000000..93efddd
--- /dev/null
@@ -0,0 +1,24 @@
+// Copyright 2014 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 freebsd,386 freebsd,arm
+
+package syscall
+
+import "unsafe"
+
+func (any *anyMessage) parseInterfaceMessage(b []byte) *InterfaceMessage {
+       p := (*InterfaceMessage)(unsafe.Pointer(any))
+       // FreeBSD 10 and beyond have a restructured mbuf
+       // packet header view.
+       // See http://svnweb.freebsd.org/base?view=revision&revision=254804.
+       if freebsdVersion >= 1000000 {
+               m := (*ifMsghdr)(unsafe.Pointer(any))
+               p.Header.Data.Hwassist = uint32(m.Data.Hwassist)
+               p.Header.Data.Epoch = m.Data.Epoch
+               p.Header.Data.Lastchange = m.Data.Lastchange
+               return &InterfaceMessage{Header: p.Header, Data: b[sizeofIfMsghdr:any.Msglen]}
+       }
+       return &InterfaceMessage{Header: p.Header, Data: b[SizeofIfMsghdr:any.Msglen]}
+}
diff --git a/libgo/go/syscall/route_freebsd_64bit.go b/libgo/go/syscall/route_freebsd_64bit.go
new file mode 100644 (file)
index 0000000..9377f2f
--- /dev/null
@@ -0,0 +1,14 @@
+// Copyright 2014 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 freebsd,amd64
+
+package syscall
+
+import "unsafe"
+
+func (any *anyMessage) parseInterfaceMessage(b []byte) *InterfaceMessage {
+       p := (*InterfaceMessage)(unsafe.Pointer(any))
+       return &InterfaceMessage{Header: p.Header, Data: b[SizeofIfMsghdr:any.Msglen]}
+}
index a6baa02f80256de6c13439b8b00fe7d8793686aa..9883aebaf5f556509bc316e24554ec020404df68 100644 (file)
@@ -27,7 +27,7 @@ func (any *anyMessage) toRoutingMessage(b []byte) RoutingMessage {
 }
 
 // InterfaceAnnounceMessage represents a routing message containing
-// network interface arrival and depature information.
+// network interface arrival and departure information.
 type InterfaceAnnounceMessage struct {
        Header IfAnnounceMsghdr
 }
index 223c15779168b652931f56bd99d4c8be71ff9c26..19f902db74b33cd2a879616b9c331a8e620e5bad 100644 (file)
@@ -27,7 +27,7 @@ func (any *anyMessage) toRoutingMessage(b []byte) RoutingMessage {
 }
 
 // InterfaceAnnounceMessage represents a routing message containing
-// network interface arrival and depature information.
+// network interface arrival and departure information.
 type InterfaceAnnounceMessage struct {
        Header IfAnnounceMsghdr
 }
index 5bc4c2acfbd5197d0e2a091dcc495ab2c9844b66..34c707d9327e441206a17c2ff59d1c61c0a82ca0 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.
 
-// +build darwin dragonfly freebsd linux netbsd openbsd
+// +build darwin dragonfly freebsd linux netbsd openbsd solaris
 
 // Socket control messages
 
@@ -16,9 +16,9 @@ import (
 // Round the length of a raw sockaddr up to align it properly.
 func cmsgAlignOf(salen int) int {
        salign := int(sizeofPtr)
-       // NOTE: It seems like 64-bit Darwin kernel still requires 32-bit
-       // aligned access to BSD subsystem.
-       if darwin64Bit {
+       // NOTE: It seems like 64-bit Darwin and DragonFly BSD kernels
+       // still require 32-bit aligned access to network subsystem.
+       if darwin64Bit || dragonfly64Bit {
                salign = 4
        }
        // NOTE: Solaris always uses 32-bit alignment,
index 491f52c100e30b484b1f539bfe9aa34472ccd1ab..d96a717303b3b7ceb4a2afa4c0bc71f20e1d3b40 100644 (file)
@@ -359,13 +359,18 @@ func Recvmsg(fd int, p, oob []byte, flags int) (n, oobn int, recvflags int, from
 }
 
 func Sendmsg(fd int, p, oob []byte, to Sockaddr, flags int) (err error) {
+       _, err = SendmsgN(fd, p, oob, to, flags)
+       return
+}
+
+func SendmsgN(fd int, p, oob []byte, to Sockaddr, flags int) (n int, err error) {
        var ptr *RawSockaddrAny
        var salen Socklen_t
        if to != nil {
                var err error
                ptr, salen, err = to.sockaddr()
                if err != nil {
-                       return err
+                       return 0, err
                }
        }
        var msg Msghdr
@@ -388,10 +393,13 @@ func Sendmsg(fd int, p, oob []byte, to Sockaddr, flags int) (err error) {
        }
        msg.Iov = &iov
        msg.Iovlen = 1
-       if err = sendmsg(fd, &msg, flags); err != nil {
-               return
+       if n, err = sendmsg(fd, &msg, flags); err != nil {
+               return 0, err
        }
-       return
+       if len(oob) > 0 && len(p) == 0 {
+               n = 0
+       }
+       return n, nil
 }
 
 //sys  Listen(fd int, n int) (err error)
index fd96524f13bd87aed1720a6e870f1e08783289d0..fda7dc65e413dbc58ca633b02b91e9f091e0acb5 100644 (file)
@@ -27,5 +27,5 @@ package syscall
 //sys  recvmsg(s int, msg *Msghdr, flags int) (n int, err error)
 //recvmsg(s _C_int, msg *Msghdr, flags _C_int) Ssize_t
 
-//sys  sendmsg(s int, msg *Msghdr, flags int) (err error)
+//sys  sendmsg(s int, msg *Msghdr, flags int) (n int, err error)
 //sendmsg(s _C_int, msg *Msghdr, flags _C_int) Ssize_t
index 04400107d06f69bea46aeb9a6c286967f7358403..3c5b6b4180742b2798d73c275c40a991dc68df33 100644 (file)
@@ -28,5 +28,5 @@ package syscall
 //sys  recvmsg(s int, msg *Msghdr, flags int) (n int, err error)
 //__xnet_recvmsg(s _C_int, msg *Msghdr, flags _C_int) Ssize_t
 
-//sys  sendmsg(s int, msg *Msghdr, flags int) (err error)
+//sys  sendmsg(s int, msg *Msghdr, flags int) (n int, err error)
 //__xnet_sendmsg(s _C_int, msg *Msghdr, flags _C_int) Ssize_t
index 08422def37591c499b05b5fbbf9d0e31b6ea3a25..591d3e1a33f896dd54409741b630ef36bb399b1b 100644 (file)
@@ -4,6 +4,9 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// TODO(rsc): Rewrite all nn(SP) references into name+(nn-8)(FP)
+// so that go vet can check that they are correct.
+
 package syscall
 
 import "unsafe"
index fcb90dbfa753b54879d0b5964c3ded38992c4dbc..a64b05fb5cc97d604841fcbfc9d304081ac24ec2 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.
 
-// +build darwin dragonfly freebsd linux netbsd openbsd
+// +build darwin dragonfly freebsd linux netbsd openbsd solaris
 
 package syscall
 
@@ -25,8 +25,9 @@ func c_syscall32(trap int32, a1, a2, a3, a4, a5, a6 int32) int32
 func c_syscall64(trap int64, a1, a2, a3, a4, a5, a6 int64) int64
 
 const (
-       darwin64Bit = runtime.GOOS == "darwin" && sizeofPtr == 8
-       netbsd32Bit = runtime.GOOS == "netbsd" && sizeofPtr == 4
+       darwin64Bit    = runtime.GOOS == "darwin" && sizeofPtr == 8
+       dragonfly64Bit = runtime.GOOS == "dragonfly" && sizeofPtr == 8
+       netbsd32Bit    = runtime.GOOS == "netbsd" && sizeofPtr == 4
 )
 
 // Do a system call.  We look at the size of uintptr to see how to pass
similarity index 62%
rename from libgo/go/syscall/passfd_test.go
rename to libgo/go/syscall/syscall_unix_test.go
index e8ac32d3f5706b46e440905d1bc59ac9d1c784fc..897ad18a974e80d8f5c2abacd3e60064360429ce 100644 (file)
@@ -1,8 +1,8 @@
-// Copyright 2012 The Go Authors. All rights reserved.
+// Copyright 2013 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 linux dragonfly darwin freebsd netbsd openbsd
+// +build darwin dragonfly freebsd linux netbsd openbsd solaris
 
 package syscall_test
 
@@ -13,12 +13,70 @@ import (
        "net"
        "os"
        "os/exec"
+       "path/filepath"
        "runtime"
        "syscall"
        "testing"
        "time"
 )
 
+// Tests that below functions, structures and constants are consistent
+// on all Unix-like systems.
+func _() {
+       // program scheduling priority functions and constants
+       var (
+               _ func(int, int, int) error   = syscall.Setpriority
+               _ func(int, int) (int, error) = syscall.Getpriority
+       )
+       const (
+               _ int = syscall.PRIO_USER
+               _ int = syscall.PRIO_PROCESS
+               _ int = syscall.PRIO_PGRP
+       )
+
+       // termios constants
+       const (
+               _ int = syscall.TCIFLUSH
+               _ int = syscall.TCIOFLUSH
+               _ int = syscall.TCOFLUSH
+       )
+
+       // fcntl file locking structure and constants
+       var (
+               _ = syscall.Flock_t{
+                       Type:   int16(0),
+                       Whence: int16(0),
+                       Start:  int64(0),
+                       Len:    int64(0),
+                       Pid:    int32(0),
+               }
+       )
+       const (
+               _ = syscall.F_GETLK
+               _ = syscall.F_SETLK
+               _ = syscall.F_SETLKW
+       )
+}
+
+// TestFcntlFlock tests whether the file locking structure matches
+// the calling convention of each kernel.
+func TestFcntlFlock(t *testing.T) {
+       name := filepath.Join(os.TempDir(), "TestFcntlFlock")
+       fd, err := syscall.Open(name, syscall.O_CREAT|syscall.O_RDWR|syscall.O_CLOEXEC, 0)
+       if err != nil {
+               t.Fatalf("Open failed: %v", err)
+       }
+       defer syscall.Unlink(name)
+       defer syscall.Close(fd)
+       flock := syscall.Flock_t{
+               Type:  syscall.F_RDLCK,
+               Start: 0, Len: 0, Whence: 1,
+       }
+       if err := syscall.FcntlFlock(uintptr(fd), syscall.F_GETLK, &flock); err != nil {
+               t.Fatalf("FcntlFlock failed: %v", err)
+       }
+}
+
 // TestPassFD tests passing a file descriptor over a Unix socket.
 //
 // This test involved both a parent and child process. The parent
@@ -27,9 +85,13 @@ import (
 // "-test.run=^TestPassFD$" and an environment variable used to signal
 // that the test should become the child process instead.
 func TestPassFD(t *testing.T) {
-       if runtime.GOOS == "dragonfly" {
+       switch runtime.GOOS {
+       case "dragonfly":
                // TODO(jsing): Figure out why sendmsg is returning EINVAL.
-               t.Skip("Skipping test on dragonfly")
+               t.Skip("skipping test on dragonfly")
+       case "solaris":
+               // TODO(aram): Figure out why ReadMsgUnix is returning empty message.
+               t.Skip("skipping test on solaris, see issue 7402")
        }
        if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
                passFDChild()
@@ -204,3 +266,53 @@ func TestUnixRightsRoundtrip(t *testing.T) {
                }
        }
 }
+
+func TestRlimit(t *testing.T) {
+       var rlimit, zero syscall.Rlimit
+       err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rlimit)
+       if err != nil {
+               t.Fatalf("Getrlimit: save failed: %v", err)
+       }
+       if zero == rlimit {
+               t.Fatalf("Getrlimit: save failed: got zero value %#v", rlimit)
+       }
+       set := rlimit
+       set.Cur = set.Max - 1
+       err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &set)
+       if err != nil {
+               t.Fatalf("Setrlimit: set failed: %#v %v", set, err)
+       }
+       var get syscall.Rlimit
+       err = syscall.Getrlimit(syscall.RLIMIT_NOFILE, &get)
+       if err != nil {
+               t.Fatalf("Getrlimit: get failed: %v", err)
+       }
+       set = rlimit
+       set.Cur = set.Max - 1
+       if set != get {
+               // Seems like Darwin requires some privilege to
+               // increase the soft limit of rlimit sandbox, though
+               // Setrlimit never reports an error.
+               switch runtime.GOOS {
+               case "darwin":
+               default:
+                       t.Fatalf("Rlimit: change failed: wanted %#v got %#v", set, get)
+               }
+       }
+       err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rlimit)
+       if err != nil {
+               t.Fatalf("Setrlimit: restore failed: %#v %v", rlimit, err)
+       }
+}
+
+func TestSeekFailure(t *testing.T) {
+       _, err := syscall.Seek(-1, 0, 0)
+       if err == nil {
+               t.Fatalf("Seek(-1, 0, 0) did not fail")
+       }
+       str := err.Error() // used to crash on Linux
+       t.Logf("Seek: %v", str)
+       if str == "" {
+               t.Fatalf("Seek(-1, 0, 0) return error with empty message")
+       }
+}
index e6f3c6d7903214c776d08afa983a56b7252e520d..1fbf5c8615f8c73a91a5d911da78877b3c8e985d 100644 (file)
@@ -417,6 +417,9 @@ func (b *B) RunParallel(body func(*PB)) {
                }()
        }
        wg.Wait()
+       if n <= uint64(b.N) && !b.Failed() {
+               b.Fatal("RunParallel: body exited without pb.Next() == false")
+       }
 }
 
 // SetParallelism sets the number of goroutines used by RunParallel to p*GOMAXPROCS.
index 9997b9920424509670b61166e94fa20406f37e72..f7ea64e7f1c89076acada85c27e01739e8c288f9 100644 (file)
@@ -88,7 +88,6 @@ func TestRunParallelFail(t *testing.T) {
                        // w/o crashing/deadlocking the whole benchmark.
                        b.Log("log")
                        b.Error("error")
-                       b.Fatal("fatal")
                })
        })
 }
index 855f3a9bbe9145e1fd69d2111405adf36dbd47f5..8078ba7cc03b2a93b08d9203e96afb1043176486 100644 (file)
@@ -8,9 +8,17 @@
 //     func TestXxx(*testing.T)
 // where Xxx can be any alphanumeric string (but the first letter must not be in
 // [a-z]) and serves to identify the test routine.
-// These TestXxx routines should be declared within the package they are testing.
 //
-// Tests and benchmarks may be skipped if not applicable like this:
+// Within these functions, use the Error, Fail or related methods to signal failure.
+//
+// To write a new test suite, create a file whose name ends _test.go that
+// contains the TestXxx functions as described here. Put the file in the same
+// package as the one being tested. The file will be excluded from regular
+// package builds but will be included when the ``go test'' command is run.
+// For more detail, run ``go help test'' and ``go help testflag''.
+//
+// Tests and benchmarks may be skipped if not applicable with a call to
+// the Skip method of *T and *B:
 //     func TestTimeConsuming(t *testing.T) {
 //         if testing.Short() {
 //             t.Skip("skipping test in short mode.")
@@ -429,6 +437,7 @@ func Main(matchString func(pat, str string) (bool, error), tests []InternalTest,
        stopAlarm()
        if !testOk || !exampleOk {
                fmt.Println("FAIL")
+               after()
                os.Exit(1)
        }
        fmt.Println("PASS")
index e0d86e343da9f6ac9b593d819a201be007e7408d..db7ca73c68da2459af8f772d16fb6523c177bbba 100644 (file)
@@ -240,6 +240,9 @@ func (s *Scanner) next() rune {
                        s.srcEnd = i + n
                        s.srcBuf[s.srcEnd] = utf8.RuneSelf // sentinel
                        if err != nil {
+                               if err != io.EOF {
+                                       s.error(err.Error())
+                               }
                                if s.srcEnd == 0 {
                                        if s.lastCharLen > 0 {
                                                // previous character was not EOF
@@ -248,9 +251,6 @@ func (s *Scanner) next() rune {
                                        s.lastCharLen = 0
                                        return EOF
                                }
-                               if err != io.EOF {
-                                       s.error(err.Error())
-                               }
                                // If err == EOF, we won't be getting more
                                // bytes; break to avoid infinite loop. If
                                // err is something else, we don't know if
index 086ab5660eb935fd0d95eeebda6d4fbe678b34f4..7d3f597eb9ab074168bd2544d17c8f2f4a673869 100644 (file)
@@ -462,6 +462,33 @@ func TestError(t *testing.T) {
        testError(t, `/*/`, "1:4", "comment not terminated", EOF)
 }
 
+// An errReader returns (0, err) where err is not io.EOF.
+type errReader struct{}
+
+func (errReader) Read(b []byte) (int, error) {
+       return 0, io.ErrNoProgress // some error that is not io.EOF
+}
+
+func TestIOError(t *testing.T) {
+       s := new(Scanner).Init(errReader{})
+       errorCalled := false
+       s.Error = func(s *Scanner, msg string) {
+               if !errorCalled {
+                       if want := io.ErrNoProgress.Error(); msg != want {
+                               t.Errorf("msg = %q, want %q", msg, want)
+                       }
+                       errorCalled = true
+               }
+       }
+       tok := s.Scan()
+       if tok != EOF {
+               t.Errorf("tok = %s, want EOF", TokenString(tok))
+       }
+       if !errorCalled {
+               t.Errorf("error handler not called")
+       }
+}
+
 func checkPos(t *testing.T, got, want Position) {
        if got.Offset != want.Offset || got.Line != want.Line || got.Column != want.Column {
                t.Errorf("got offset, line, column = %d, %d, %d; want %d, %d, %d",
index f622ac7dcee2019e82ad0cf3dbd7829f78a75236..7c6efd59cded112d1f201bc5e805a43c7279e3d1 100644 (file)
@@ -20,7 +20,7 @@ The input text for a template is UTF-8-encoded text in any format.
 "{{" and "}}"; all text outside actions is copied to the output unchanged.
 Actions may not span newlines, although comments can.
 
-Once constructed, a template may be executed safely in parallel.
+Once parsed, a template may be executed safely in parallel.
 
 Here is a trivial example that prints "17 items are made of wool".
 
index 6de37a19963f35a3c345b549f979e34babc42d36..2f323126453fdc1ad6e1b8f03135128b83584e99 100644 (file)
@@ -108,6 +108,10 @@ func errRecover(errp *error) {
 
 // ExecuteTemplate applies the template associated with t that has the given name
 // to the specified data object and writes the output to wr.
+// If an error occurs executing the template or writing its output,
+// execution stops, but partial results may already have been written to
+// the output writer.
+// A template may be executed safely in parallel.
 func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{}) error {
        tmpl := t.tmpl[name]
        if tmpl == nil {
@@ -118,6 +122,10 @@ func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{})
 
 // Execute applies a parsed template to the specified data object,
 // and writes the output to wr.
+// If an error occurs executing the template or writing its output,
+// execution stops, but partial results may already have been written to
+// the output writer.
+// A template may be executed safely in parallel.
 func (t *Template) Execute(wr io.Writer, data interface{}) (err error) {
        defer errRecover(&err)
        value := reflect.ValueOf(data)
index 6f92c126268b3cf886bdab3d513d1ebf54ffb615..9f210ea27df199f97615c20becc84e903287f4b8 100644 (file)
@@ -102,7 +102,7 @@ const (
 // std0x records the std values for "01", "02", ..., "06".
 var std0x = [...]int{stdZeroMonth, stdZeroDay, stdZeroHour12, stdZeroMinute, stdZeroSecond, stdYear}
 
-// startsWithLowerCase reports whether the the string has a lower-case letter at the beginning.
+// startsWithLowerCase reports whether the string has a lower-case letter at the beginning.
 // Its purpose is to prevent matching strings like "Month" when looking for "Mon".
 func startsWithLowerCase(str string) bool {
        if len(str) == 0 {
@@ -1037,8 +1037,8 @@ func parseTimeZone(value string) (length int, ok bool) {
        if len(value) < 3 {
                return 0, false
        }
-       // Special case 1: This is the only zone with a lower-case letter.
-       if len(value) >= 4 && value[:4] == "ChST" {
+       // Special case 1: ChST and MeST are the only zones with a lower-case letter.
+       if len(value) >= 4 && (value[:4] == "ChST" || value[:4] == "MeST") {
                return 4, true
        }
        // Special case 2: GMT may have an hour offset; treat it specially.
@@ -1240,5 +1240,8 @@ func ParseDuration(s string) (Duration, error) {
        if neg {
                f = -f
        }
+       if f < float64(-1<<63) || f > float64(1<<63-1) {
+               return 0, errors.New("time: overflow parsing duration")
+       }
        return Duration(f), nil
 }
index 93273392e9631ed858cc42ff117003bd08a07125..70e653e6706346d07e3262abe597f6bbc038a563 100644 (file)
@@ -333,6 +333,7 @@ var parseTimeZoneTests = []ParseTimeZoneTest{
        {"GMT-5 hi there", 5, true},
        {"GMT-51 hi there", 3, true},
        {"ChST hi there", 4, true},
+       {"MeST hi there", 4, true},
        {"MSDx", 3, true},
        {"MSDY", 0, false}, // four letters must end in T.
        {"ESAST hi", 5, true},
index 4ba6d478debac433b4307e657be6251b97bad1a7..2243d3668deaee97925971b977896d44b727a9a5 100644 (file)
@@ -29,7 +29,7 @@ func CheckRuntimeTimerOverflow() error {
        // detection logic in NewTimer: we're testing the underlying
        // runtime.addtimer function.
        r := &runtimeTimer{
-               when: nano() + (1<<63 - 1),
+               when: runtimeNano() + (1<<63 - 1),
                f:    empty,
                arg:  nil,
        }
index 4f55bebe62a6de33e5223db17c7adbbf635cf4fc..6a03f417bd00ffb7efe2dd224c6d1fe9bff95d1d 100644 (file)
@@ -8,10 +8,8 @@ package time
 // A negative or zero duration causes Sleep to return immediately.
 func Sleep(d Duration)
 
-func nano() int64 {
-       sec, nsec := now()
-       return sec*1e9 + int64(nsec)
-}
+// runtimeNano returns the current value of the runtime clock in nanoseconds.
+func runtimeNano() int64
 
 // Interface to timers implemented in package runtime.
 // Must be in sync with ../runtime/runtime.h:/^struct.Timer$
@@ -29,9 +27,9 @@ type runtimeTimer struct {
 // zero because of an overflow, MaxInt64 is returned.
 func when(d Duration) int64 {
        if d <= 0 {
-               return nano()
+               return runtimeNano()
        }
-       t := nano() + int64(d)
+       t := runtimeNano() + int64(d)
        if t < 0 {
                t = 1<<63 - 1 // math.MaxInt64
        }
@@ -92,7 +90,7 @@ func sendTime(now int64, c interface{}) {
        // the desired behavior when the reader gets behind,
        // because the sends are periodic.
        select {
-       case c.(chan Time) <- Unix(0, now):
+       case c.(chan Time) <- Now():
        default:
        }
 }
index e86e294ee7a4aba8f8d74f377fe3dc14bc39080f..7c2dcaf5471cfcbe4d28be89fe39f610ecc39d43 100644 (file)
@@ -74,26 +74,13 @@ func benchmark(b *testing.B, bench func(n int)) {
        for i := 0; i < len(garbage); i++ {
                garbage[i] = AfterFunc(Hour, nil)
        }
-
-       const batch = 1000
-       P := runtime.GOMAXPROCS(-1)
-       N := int32(b.N / batch)
-
        b.ResetTimer()
 
-       var wg sync.WaitGroup
-       wg.Add(P)
-
-       for p := 0; p < P; p++ {
-               go func() {
-                       for atomic.AddInt32(&N, -1) >= 0 {
-                               bench(batch)
-                       }
-                       wg.Done()
-               }()
-       }
-
-       wg.Wait()
+       b.RunParallel(func(pb *testing.PB) {
+               for pb.Next() {
+                       bench(1000)
+               }
+       })
 
        b.StopTimer()
        for i := 0; i < len(garbage); i++ {
@@ -361,19 +348,18 @@ func TestReset(t *testing.T) {
 // Test that sleeping for an interval so large it overflows does not
 // result in a short sleep duration.
 func TestOverflowSleep(t *testing.T) {
-       const timeout = 25 * Millisecond
        const big = Duration(int64(1<<63 - 1))
        select {
        case <-After(big):
                t.Fatalf("big timeout fired")
-       case <-After(timeout):
+       case <-After(25 * Millisecond):
                // OK
        }
        const neg = Duration(-1 << 63)
        select {
        case <-After(neg):
                // OK
-       case <-After(timeout):
+       case <-After(1 * Second):
                t.Fatalf("negative timeout didn't fire")
        }
 }
index 36c214b6b093b99d3df23918807bd654d76eae24..379e13d6a537f7ec87ef818c9881a7212ecdb52d 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.
 
-// +build darwin dragonfly freebsd linux netbsd openbsd solaris
+// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
 
 package time
 
index b92c339c02a21ac7cab801b4361181ae6c73ba1d..19007841e1af045821168cbde5faf6322dfda6ad 100644 (file)
@@ -17,6 +17,7 @@ type Ticker struct {
 // time with a period specified by the duration argument.
 // It adjusts the intervals or drops ticks to make up for slow receivers.
 // The duration d must be greater than zero; if not, NewTicker will panic.
+// Stop the ticker to release associated resources.
 func NewTicker(d Duration) *Ticker {
        if d <= 0 {
                panic(errors.New("non-positive interval for NewTicker"))
@@ -28,7 +29,7 @@ func NewTicker(d Duration) *Ticker {
        t := &Ticker{
                C: c,
                r: runtimeTimer{
-                       when:   nano() + int64(d),
+                       when:   when(d),
                        period: int64(d),
                        f:      sendTime,
                        arg:    c,
index c504df74013a88678d47279b5823fd49e8a8f2a2..0a2b0914283e8f85622934976944fb843c1c509f 100644 (file)
@@ -934,6 +934,8 @@ func (t *Time) GobDecode(data []byte) error {
 // The time is a quoted string in RFC 3339 format, with sub-second precision added if present.
 func (t Time) MarshalJSON() ([]byte, error) {
        if y := t.Year(); y < 0 || y >= 10000 {
+               // RFC 3339 is clear that years are 4 digits exactly.
+               // See golang.org/issue/4556#c15 for more discussion.
                return nil, errors.New("Time.MarshalJSON: year outside of range [0,9999]")
        }
        return []byte(t.Format(`"` + RFC3339Nano + `"`)), nil
index 2615517d9ad13c08188214d2f2418915cb7df644..4ae7da5a443a6706089f9e8bd7b5f3b35e3a5fff 100644 (file)
@@ -842,6 +842,7 @@ var parseDurationTests = []struct {
        {"-.", false, 0},
        {".s", false, 0},
        {"+.s", false, 0},
+       {"3000000h", false, 0}, // overflow
 }
 
 func TestParseDuration(t *testing.T) {
index 4bb4bf665cc23675fd6c39e56e3d1710418cd379..de9ebb41c827f4c25ae69ff92c20815a455f7abb 100644 (file)
@@ -68,7 +68,7 @@ func loadZoneData(bytes []byte) (l *Location, err error) {
 
        // 1-byte version, then 15 bytes of padding
        var p []byte
-       if p = d.read(16); len(p) != 16 || p[0] != 0 && p[0] != '2' {
+       if p = d.read(16); len(p) != 16 || p[0] != 0 && p[0] != '2' && p[0] != '3' {
                return nil, badData
        }
 
@@ -123,7 +123,7 @@ func loadZoneData(bytes []byte) (l *Location, err error) {
                return nil, badData
        }
 
-       // If version == 2, the entire file repeats, this time using
+       // If version == 2 or 3, the entire file repeats, this time using
        // 8-byte ints for txtimes and leap seconds.
        // We won't need those until 2106.
 
index 03f54a69360055ac98ed85f6b673016ae2f605d6..ede5330f5cc49de95e5365ca842fa3eb2b0caf23 100644 (file)
@@ -9,6 +9,16 @@ import (
        "time"
 )
 
+func TestVersion3(t *testing.T) {
+       t.Skip("gccgo does not use the zip file")
+       time.ForceZipFileForTesting(true)
+       defer time.ForceZipFileForTesting(false)
+       _, err := time.LoadLocation("Asia/Jerusalem")
+       if err != nil {
+               t.Fatal(err)
+       }
+}
+
 // Test that we get the correct results for times before the first
 // transition time.  To do this we explicitly check early dates in a
 // couple of specific timezones.
index e70decb36cb5622f8f45169db56f4c7043d0f373..3fe8e55795c0f724ec9c72543836a38290cc1658 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.
 
-// +build darwin dragonfly freebsd linux netbsd openbsd solaris
+// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
 
 // Parse "zoneinfo" time zone file.
 // This is a fairly standard file format used on OS X, Linux, BSD, Sun, and others.
index 377a892153858016b1a81dec8a7c9f3648516a37..6046743e67d0c9fabe7e1470c96d4a3d58ba4f43 100644 (file)
@@ -90,7 +90,7 @@ func toEnglishName(stdname, dstname string) (string, error) {
        return "", errors.New(`English name for time zone "` + stdname + `" not found in registry`)
 }
 
-// extractCAPS exracts capital letters from description desc.
+// extractCAPS extracts capital letters from description desc.
 func extractCAPS(desc string) string {
        var short []rune
        for _, c := range desc {
index 3b389e93f14914e91df7b84eef323e4476ef8300..977bd2b3b0594d17d1318975dd1acc4d5cb42228 100644 (file)
@@ -316,7 +316,7 @@ type foldPair struct {
 // SimpleFold iterates over Unicode code points equivalent under
 // the Unicode-defined simple case folding.  Among the code points
 // equivalent to rune (including rune itself), SimpleFold returns the
-// smallest rune >= r if one exists, or else the smallest rune >= 0.
+// smallest rune > r if one exists, or else the smallest rune >= 0.
 //
 // For example:
 //     SimpleFold('A') = 'a'
index e4d5572a0fecae31eb8e9c65794647a182a1aae8..4ee11fb364f58dab8ea41e237651992473977fe1 100644 (file)
@@ -387,32 +387,20 @@ func TestTurkishCase(t *testing.T) {
 }
 
 var simpleFoldTests = []string{
-       // SimpleFold could order its returned slices in any order it wants,
-       // but we know it orders them in increasing order starting at in
-       // and looping around from MaxRune to 0.
+       // SimpleFold(x) returns the next equivalent rune > x or wraps
+       // around to smaller values.
 
        // Easy cases.
        "Aa",
-       "aA",
        "δΔ",
-       "Δδ",
 
        // ASCII special cases.
        "KkK",
-       "kKK",
-       "KKk",
        "Ssſ",
-       "sſS",
-       "ſSs",
 
        // Non-ASCII special cases.
        "ρϱΡ",
-       "ϱΡρ",
-       "Ρρϱ",
        "ͅΙιι",
-       "Ιιιͅ",
-       "ιιͅΙ",
-       "ιͅΙι",
 
        // Extra special cases: has lower/upper but no case fold.
        "İ",
index 903e4012aa4ab4cb592013f2d7cbc5b02c234f7a..c0e47c535ab6290f0e02abbea05e8ae2dad1a761 100644 (file)
@@ -36,7 +36,7 @@ func IsSurrogate(r rune) bool {
 // the Unicode replacement code point U+FFFD.
 func DecodeRune(r1, r2 rune) rune {
        if surr1 <= r1 && r1 < surr2 && surr2 <= r2 && r2 < surr3 {
-               return (rune(r1)-surr1)<<10 | (rune(r2) - surr2) + 0x10000
+               return (r1-surr1)<<10 | (r2 - surr2) + 0x10000
        }
        return replacementChar
 }
index 93d0be5e0c180ea9aba29ef4f3f3f4b7210c6b80..0dc859a041bf7623c11d8420bc365647fc8083bf 100644 (file)
@@ -329,37 +329,29 @@ func RuneLen(r rune) int {
 // It returns the number of bytes written.
 func EncodeRune(p []byte, r rune) int {
        // Negative values are erroneous.  Making it unsigned addresses the problem.
-       if uint32(r) <= rune1Max {
+       switch i := uint32(r); {
+       case i <= rune1Max:
                p[0] = byte(r)
                return 1
-       }
-
-       if uint32(r) <= rune2Max {
+       case i <= rune2Max:
                p[0] = t2 | byte(r>>6)
                p[1] = tx | byte(r)&maskx
                return 2
-       }
-
-       if uint32(r) > MaxRune {
+       case i > MaxRune, surrogateMin <= i && i <= surrogateMax:
                r = RuneError
-       }
-
-       if surrogateMin <= r && r <= surrogateMax {
-               r = RuneError
-       }
-
-       if uint32(r) <= rune3Max {
+               fallthrough
+       case i <= rune3Max:
                p[0] = t3 | byte(r>>12)
                p[1] = tx | byte(r>>6)&maskx
                p[2] = tx | byte(r)&maskx
                return 3
+       default:
+               p[0] = t4 | byte(r>>18)
+               p[1] = tx | byte(r>>12)&maskx
+               p[2] = tx | byte(r>>6)&maskx
+               p[3] = tx | byte(r)&maskx
+               return 4
        }
-
-       p[0] = t4 | byte(r>>18)
-       p[1] = tx | byte(r>>12)&maskx
-       p[2] = tx | byte(r>>6)&maskx
-       p[3] = tx | byte(r)&maskx
-       return 4
 }
 
 // RuneCount returns the number of runes in p.  Erroneous and short
index b636b01953f005ff012d5365f00f63a55be8049d..e579ac7c41b9539240dca95df42ad7f08f3fd7b0 100755 (executable)
@@ -163,7 +163,7 @@ done
   done
 done
 
-runtime="chan.goc chan.h cpuprof.goc env_posix.c lock_futex.c lfstack.goc lock_sema.c mcache.c mcentral.c mfixalloc.c mgc0.c mgc0.h mheap.c msize.c netpoll.goc netpoll_epoll.c netpoll_kqueue.c netpoll_stub.c panic.c print.c proc.c race.h rdebug.goc runtime.c runtime.h signal_unix.c signal_unix.h malloc.h malloc.goc mprof.goc parfor.c runtime1.goc sema.goc sigqueue.goc string.goc time.goc"
+runtime="chan.goc chan.h cpuprof.goc env_posix.c heapdump.c lock_futex.c lfstack.goc lock_sema.c mcache.c mcentral.c mfixalloc.c mgc0.c mgc0.h mheap.c msize.c netpoll.goc netpoll_epoll.c netpoll_kqueue.c netpoll_stub.c panic.c print.c proc.c race.h rdebug.goc runtime.c runtime.h signal_unix.c signal_unix.h malloc.h malloc.goc mprof.goc parfor.c runtime1.goc sema.goc sigqueue.goc string.goc time.goc"
 for f in $runtime; do
   merge_c $f $f
 done
diff --git a/libgo/runtime/chan.c b/libgo/runtime/chan.c
new file mode 100644 (file)
index 0000000..4559c0f
--- /dev/null
@@ -0,0 +1,1186 @@
+// AUTO-GENERATED by autogen.sh; DO NOT EDIT
+
+#include "runtime.h"
+#include "arch.h"
+#include "go-type.h"
+#include "race.h"
+#include "malloc.h"
+#include "chan.h"
+
+#line 13 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+uint32 runtime_Hchansize = sizeof ( Hchan ) ; 
+#line 15 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+static void dequeueg ( WaitQ* ) ; 
+static SudoG* dequeue ( WaitQ* ) ; 
+static void enqueue ( WaitQ* , SudoG* ) ; 
+static void racesync ( Hchan* , SudoG* ) ; 
+#line 20 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+static Hchan* 
+makechan ( ChanType *t , int64 hint ) 
+{ 
+Hchan *c; 
+uintptr n; 
+const Type *elem; 
+#line 27 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+elem = t->__element_type; 
+#line 30 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+if ( elem->__size >= ( 1<<16 ) ) 
+runtime_throw ( "makechan: invalid channel element type" ) ; 
+#line 33 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+if ( hint < 0 || ( intgo ) hint != hint || ( elem->__size > 0 && ( uintptr ) hint > ( MaxMem - sizeof ( *c ) ) / elem->__size ) ) 
+runtime_panicstring ( "makechan: size out of range" ) ; 
+#line 36 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+n = sizeof ( *c ) ; 
+n = ROUND ( n , elem->__align ) ; 
+#line 40 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+c = ( Hchan* ) runtime_mallocgc ( sizeof ( *c ) + hint*elem->__size , ( uintptr ) t | TypeInfo_Chan , 0 ) ; 
+c->elemsize = elem->__size; 
+c->elemtype = elem; 
+c->dataqsiz = hint; 
+#line 45 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+if ( debug ) 
+runtime_printf ( "makechan: chan=%p; elemsize=%D; dataqsiz=%D\n" , 
+c , ( int64 ) elem->__size , ( int64 ) c->dataqsiz ) ; 
+#line 49 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+return c; 
+} 
+Hchan* reflect_makechan(ChanType* t, uint64 size) __asm__ (GOSYM_PREFIX "reflect.makechan");
+Hchan* reflect_makechan(ChanType* t, uint64 size)
+{
+  Hchan* c;
+#line 52 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+
+       c = makechan(t, size);
+return c;
+}
+
+#line 56 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+Hchan* 
+__go_new_channel ( ChanType *t , uintptr hint ) 
+{ 
+return makechan ( t , hint ) ; 
+} 
+#line 62 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+Hchan* 
+__go_new_channel_big ( ChanType *t , uint64 hint ) 
+{ 
+return makechan ( t , hint ) ; 
+} 
+#line 82 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+static bool 
+chansend ( ChanType *t , Hchan *c , byte *ep , bool block , void *pc ) 
+{ 
+SudoG *sg; 
+SudoG mysg; 
+G* gp; 
+int64 t0; 
+G* g; 
+#line 91 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+g = runtime_g ( ) ; 
+#line 93 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+if ( raceenabled ) 
+runtime_racereadobjectpc ( ep , t->__element_type , runtime_getcallerpc ( &t ) , chansend ) ; 
+#line 96 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+if ( c == nil ) { 
+USED ( t ) ; 
+if ( !block ) 
+return false; 
+runtime_park ( nil , nil , "chan send (nil chan)" ) ; 
+return false; 
+} 
+#line 104 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+if ( runtime_gcwaiting ( ) ) 
+runtime_gosched ( ) ; 
+#line 107 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+if ( debug ) { 
+runtime_printf ( "chansend: chan=%p\n" , c ) ; 
+} 
+#line 111 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+t0 = 0; 
+mysg.releasetime = 0; 
+if ( runtime_blockprofilerate > 0 ) { 
+t0 = runtime_cputicks ( ) ; 
+mysg.releasetime = -1; 
+} 
+#line 118 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+runtime_lock ( c ) ; 
+if ( raceenabled ) 
+runtime_racereadpc ( c , pc , chansend ) ; 
+if ( c->closed ) 
+goto closed; 
+#line 124 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+if ( c->dataqsiz > 0 ) 
+goto asynch; 
+#line 127 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+sg = dequeue ( &c->recvq ) ; 
+if ( sg != nil ) { 
+if ( raceenabled ) 
+racesync ( c , sg ) ; 
+runtime_unlock ( c ) ; 
+#line 133 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+gp = sg->g; 
+gp->param = sg; 
+if ( sg->elem != nil ) 
+runtime_memmove ( sg->elem , ep , c->elemsize ) ; 
+if ( sg->releasetime ) 
+sg->releasetime = runtime_cputicks ( ) ; 
+runtime_ready ( gp ) ; 
+return true; 
+} 
+#line 143 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+if ( !block ) { 
+runtime_unlock ( c ) ; 
+return false; 
+} 
+#line 148 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+mysg.elem = ep; 
+mysg.g = g; 
+mysg.selectdone = nil; 
+g->param = nil; 
+enqueue ( &c->sendq , &mysg ) ; 
+runtime_parkunlock ( c , "chan send" ) ; 
+#line 155 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+if ( g->param == nil ) { 
+runtime_lock ( c ) ; 
+if ( !c->closed ) 
+runtime_throw ( "chansend: spurious wakeup" ) ; 
+goto closed; 
+} 
+#line 162 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+if ( mysg.releasetime > 0 ) 
+runtime_blockevent ( mysg.releasetime - t0 , 2 ) ; 
+#line 165 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+return true; 
+#line 167 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+asynch: 
+if ( c->closed ) 
+goto closed; 
+#line 171 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+if ( c->qcount >= c->dataqsiz ) { 
+if ( !block ) { 
+runtime_unlock ( c ) ; 
+return false; 
+} 
+mysg.g = g; 
+mysg.elem = nil; 
+mysg.selectdone = nil; 
+enqueue ( &c->sendq , &mysg ) ; 
+runtime_parkunlock ( c , "chan send" ) ; 
+#line 182 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+runtime_lock ( c ) ; 
+goto asynch; 
+} 
+#line 186 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+if ( raceenabled ) 
+runtime_racerelease ( chanbuf ( c , c->sendx ) ) ; 
+#line 189 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+runtime_memmove ( chanbuf ( c , c->sendx ) , ep , c->elemsize ) ; 
+if ( ++c->sendx == c->dataqsiz ) 
+c->sendx = 0; 
+c->qcount++; 
+#line 194 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+sg = dequeue ( &c->recvq ) ; 
+if ( sg != nil ) { 
+gp = sg->g; 
+runtime_unlock ( c ) ; 
+if ( sg->releasetime ) 
+sg->releasetime = runtime_cputicks ( ) ; 
+runtime_ready ( gp ) ; 
+} else 
+runtime_unlock ( c ) ; 
+if ( mysg.releasetime > 0 ) 
+runtime_blockevent ( mysg.releasetime - t0 , 2 ) ; 
+return true; 
+#line 207 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+closed: 
+runtime_unlock ( c ) ; 
+runtime_panicstring ( "send on closed channel" ) ; 
+return false; 
+} 
+#line 214 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+static bool 
+chanrecv ( ChanType *t , Hchan* c , byte *ep , bool block , bool *received ) 
+{ 
+SudoG *sg; 
+SudoG mysg; 
+G *gp; 
+int64 t0; 
+G *g; 
+#line 223 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+if ( runtime_gcwaiting ( ) ) 
+runtime_gosched ( ) ; 
+#line 228 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+if ( debug ) 
+runtime_printf ( "chanrecv: chan=%p\n" , c ) ; 
+#line 231 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+g = runtime_g ( ) ; 
+#line 233 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+if ( c == nil ) { 
+USED ( t ) ; 
+if ( !block ) 
+return false; 
+runtime_park ( nil , nil , "chan receive (nil chan)" ) ; 
+return false; 
+} 
+#line 241 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+t0 = 0; 
+mysg.releasetime = 0; 
+if ( runtime_blockprofilerate > 0 ) { 
+t0 = runtime_cputicks ( ) ; 
+mysg.releasetime = -1; 
+} 
+#line 248 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+runtime_lock ( c ) ; 
+if ( c->dataqsiz > 0 ) 
+goto asynch; 
+#line 252 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+if ( c->closed ) 
+goto closed; 
+#line 255 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+sg = dequeue ( &c->sendq ) ; 
+if ( sg != nil ) { 
+if ( raceenabled ) 
+racesync ( c , sg ) ; 
+runtime_unlock ( c ) ; 
+#line 261 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+if ( ep != nil ) 
+runtime_memmove ( ep , sg->elem , c->elemsize ) ; 
+gp = sg->g; 
+gp->param = sg; 
+if ( sg->releasetime ) 
+sg->releasetime = runtime_cputicks ( ) ; 
+runtime_ready ( gp ) ; 
+#line 269 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+if ( received != nil ) 
+*received = true; 
+return true; 
+} 
+#line 274 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+if ( !block ) { 
+runtime_unlock ( c ) ; 
+return false; 
+} 
+#line 279 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+mysg.elem = ep; 
+mysg.g = g; 
+mysg.selectdone = nil; 
+g->param = nil; 
+enqueue ( &c->recvq , &mysg ) ; 
+runtime_parkunlock ( c , "chan receive" ) ; 
+#line 286 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+if ( g->param == nil ) { 
+runtime_lock ( c ) ; 
+if ( !c->closed ) 
+runtime_throw ( "chanrecv: spurious wakeup" ) ; 
+goto closed; 
+} 
+#line 293 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+if ( received != nil ) 
+*received = true; 
+if ( mysg.releasetime > 0 ) 
+runtime_blockevent ( mysg.releasetime - t0 , 2 ) ; 
+return true; 
+#line 299 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+asynch: 
+if ( c->qcount <= 0 ) { 
+if ( c->closed ) 
+goto closed; 
+#line 304 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+if ( !block ) { 
+runtime_unlock ( c ) ; 
+if ( received != nil ) 
+*received = false; 
+return false; 
+} 
+mysg.g = g; 
+mysg.elem = nil; 
+mysg.selectdone = nil; 
+enqueue ( &c->recvq , &mysg ) ; 
+runtime_parkunlock ( c , "chan receive" ) ; 
+#line 316 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+runtime_lock ( c ) ; 
+goto asynch; 
+} 
+#line 320 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+if ( raceenabled ) 
+runtime_raceacquire ( chanbuf ( c , c->recvx ) ) ; 
+#line 323 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+if ( ep != nil ) 
+runtime_memmove ( ep , chanbuf ( c , c->recvx ) , c->elemsize ) ; 
+runtime_memclr ( chanbuf ( c , c->recvx ) , c->elemsize ) ; 
+if ( ++c->recvx == c->dataqsiz ) 
+c->recvx = 0; 
+c->qcount--; 
+#line 330 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+sg = dequeue ( &c->sendq ) ; 
+if ( sg != nil ) { 
+gp = sg->g; 
+runtime_unlock ( c ) ; 
+if ( sg->releasetime ) 
+sg->releasetime = runtime_cputicks ( ) ; 
+runtime_ready ( gp ) ; 
+} else 
+runtime_unlock ( c ) ; 
+#line 340 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+if ( received != nil ) 
+*received = true; 
+if ( mysg.releasetime > 0 ) 
+runtime_blockevent ( mysg.releasetime - t0 , 2 ) ; 
+return true; 
+#line 346 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+closed: 
+if ( ep != nil ) 
+runtime_memclr ( ep , c->elemsize ) ; 
+if ( received != nil ) 
+*received = false; 
+if ( raceenabled ) 
+runtime_raceacquire ( c ) ; 
+runtime_unlock ( c ) ; 
+if ( mysg.releasetime > 0 ) 
+runtime_blockevent ( mysg.releasetime - t0 , 2 ) ; 
+return true; 
+} 
+#line 361 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+void 
+__go_send_small ( ChanType *t , Hchan* c , uint64 val ) 
+{ 
+union 
+{ 
+byte b[sizeof ( uint64 ) ]; 
+uint64 v; 
+} u; 
+byte *v; 
+#line 371 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+u.v = val; 
+#ifndef WORDS_BIGENDIAN 
+v = u.b; 
+#else 
+v = u.b + sizeof ( uint64 ) - t->__element_type->__size; 
+#endif 
+chansend ( t , c , v , true , runtime_getcallerpc ( &t ) ) ; 
+} 
+#line 382 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+void 
+__go_send_big ( ChanType *t , Hchan* c , byte* v ) 
+{ 
+chansend ( t , c , v , true , runtime_getcallerpc ( &t ) ) ; 
+} 
+#line 390 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+void 
+__go_receive ( ChanType *t , Hchan* c , byte* v ) 
+{ 
+chanrecv ( t , c , v , true , nil ) ; 
+} 
+#line 396 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+_Bool runtime_chanrecv2 ( ChanType *t , Hchan* c , byte* v ) 
+__asm__ ( GOSYM_PREFIX "runtime.chanrecv2" ) ; 
+#line 399 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+_Bool 
+runtime_chanrecv2 ( ChanType *t , Hchan* c , byte* v ) 
+{ 
+bool received = false; 
+#line 404 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+chanrecv ( t , c , v , true , &received ) ; 
+return received; 
+} 
+bool runtime_selectnbsend(ChanType* t, Hchan* c, byte* elem) __asm__ (GOSYM_PREFIX "runtime.selectnbsend");
+bool runtime_selectnbsend(ChanType* t, Hchan* c, byte* elem)
+{
+  bool selected;
+#line 425 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+
+       selected = chansend(t, c, elem, false, runtime_getcallerpc(&t));
+return selected;
+}
+bool runtime_selectnbrecv(ChanType* t, byte* elem, Hchan* c) __asm__ (GOSYM_PREFIX "runtime.selectnbrecv");
+bool runtime_selectnbrecv(ChanType* t, byte* elem, Hchan* c)
+{
+  bool selected;
+#line 446 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+
+       selected = chanrecv(t, c, elem, false, nil);
+return selected;
+}
+bool runtime_selectnbrecv2(ChanType* t, byte* elem, bool* received, Hchan* c) __asm__ (GOSYM_PREFIX "runtime.selectnbrecv2");
+bool runtime_selectnbrecv2(ChanType* t, byte* elem, bool* received, Hchan* c)
+{
+  bool selected;
+#line 467 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+
+       bool r;
+
+       selected = chanrecv(t, c, elem, false, received == nil ? nil : &r);
+       if(received != nil)
+               *received = r;
+return selected;
+}
+bool reflect_chansend(ChanType* t, Hchan* c, byte* elem, bool nb) __asm__ (GOSYM_PREFIX "reflect.chansend");
+bool reflect_chansend(ChanType* t, Hchan* c, byte* elem, bool nb)
+{
+  bool selected;
+#line 475 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+
+       selected = chansend(t, c, elem, !nb, runtime_getcallerpc(&t));
+return selected;
+}
+struct reflect_chanrecv_ret {
+  bool selected;
+  bool received;
+};
+struct reflect_chanrecv_ret reflect_chanrecv(ChanType* t, Hchan* c, bool nb, byte* elem) __asm__ (GOSYM_PREFIX "reflect.chanrecv");
+struct reflect_chanrecv_ret reflect_chanrecv(ChanType* t, Hchan* c, bool nb, byte* elem)
+{
+  bool selected;
+  bool received;
+#line 479 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+
+       received = false;
+       selected = chanrecv(t, c, elem, !nb, &received);
+  {
+    struct reflect_chanrecv_ret __ret;
+    __ret.selected = selected;
+    __ret.received = received;
+    return __ret;
+  }
+}
+
+#line 484 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+static Select* newselect ( int32 ) ; 
+byte* runtime_newselect(int32 size) __asm__ (GOSYM_PREFIX "runtime.newselect");
+byte* runtime_newselect(int32 size)
+{
+  byte* sel;
+#line 486 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+
+       sel = (byte*)newselect(size);
+return sel;
+}
+
+#line 490 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+static Select* 
+newselect ( int32 size ) 
+{ 
+int32 n; 
+Select *sel; 
+#line 496 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+n = 0; 
+if ( size > 1 ) 
+n = size-1; 
+#line 504 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+sel = runtime_mal ( sizeof ( *sel ) + 
+n*sizeof ( sel->scase[0] ) + 
+size*sizeof ( sel->lockorder[0] ) + 
+size*sizeof ( sel->pollorder[0] ) ) ; 
+#line 509 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+sel->tcase = size; 
+sel->ncase = 0; 
+sel->lockorder = ( void* ) ( sel->scase + size ) ; 
+sel->pollorder = ( void* ) ( sel->lockorder + size ) ; 
+#line 514 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+if ( debug ) 
+runtime_printf ( "newselect s=%p size=%d\n" , sel , size ) ; 
+return sel; 
+} 
+#line 520 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+static void selectsend ( Select *sel , Hchan *c , int index , void *elem ) ; 
+void runtime_selectsend(Select* sel, Hchan* c, byte* elem, int32 index) __asm__ (GOSYM_PREFIX "runtime.selectsend");
+void runtime_selectsend(Select* sel, Hchan* c, byte* elem, int32 index)
+{
+#line 522 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+
+       // nil cases do not compete
+       if(c != nil)
+               selectsend(sel, c, index, elem);
+}
+
+#line 528 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+static void 
+selectsend ( Select *sel , Hchan *c , int index , void *elem ) 
+{ 
+int32 i; 
+Scase *cas; 
+#line 534 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+i = sel->ncase; 
+if ( i >= sel->tcase ) 
+runtime_throw ( "selectsend: too many cases" ) ; 
+sel->ncase = i+1; 
+cas = &sel->scase[i]; 
+#line 540 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+cas->index = index; 
+cas->chan = c; 
+cas->kind = CaseSend; 
+cas->sg.elem = elem; 
+#line 545 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+if ( debug ) 
+runtime_printf ( "selectsend s=%p index=%d chan=%p\n" , 
+sel , cas->index , cas->chan ) ; 
+} 
+#line 551 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+static void selectrecv ( Select *sel , Hchan *c , int index , void *elem , bool* ) ; 
+void runtime_selectrecv(Select* sel, Hchan* c, byte* elem, int32 index) __asm__ (GOSYM_PREFIX "runtime.selectrecv");
+void runtime_selectrecv(Select* sel, Hchan* c, byte* elem, int32 index)
+{
+#line 553 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+
+       // nil cases do not compete
+       if(c != nil)
+               selectrecv(sel, c, index, elem, nil);
+}
+void runtime_selectrecv2(Select* sel, Hchan* c, byte* elem, bool* received, int32 index) __asm__ (GOSYM_PREFIX "runtime.selectrecv2");
+void runtime_selectrecv2(Select* sel, Hchan* c, byte* elem, bool* received, int32 index)
+{
+#line 559 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+
+       // nil cases do not compete
+       if(c != nil)
+               selectrecv(sel, c, index, elem, received);
+}
+
+#line 565 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+static void 
+selectrecv ( Select *sel , Hchan *c , int index , void *elem , bool *received ) 
+{ 
+int32 i; 
+Scase *cas; 
+#line 571 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+i = sel->ncase; 
+if ( i >= sel->tcase ) 
+runtime_throw ( "selectrecv: too many cases" ) ; 
+sel->ncase = i+1; 
+cas = &sel->scase[i]; 
+cas->index = index; 
+cas->chan = c; 
+#line 579 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+cas->kind = CaseRecv; 
+cas->sg.elem = elem; 
+cas->receivedp = received; 
+#line 583 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+if ( debug ) 
+runtime_printf ( "selectrecv s=%p index=%d chan=%p\n" , 
+sel , cas->index , cas->chan ) ; 
+} 
+#line 589 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+static void selectdefault ( Select* , int ) ; 
+void runtime_selectdefault(Select* sel, int32 index) __asm__ (GOSYM_PREFIX "runtime.selectdefault");
+void runtime_selectdefault(Select* sel, int32 index)
+{
+#line 591 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+
+       selectdefault(sel, index);
+}
+
+#line 595 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+static void 
+selectdefault ( Select *sel , int32 index ) 
+{ 
+int32 i; 
+Scase *cas; 
+#line 601 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+i = sel->ncase; 
+if ( i >= sel->tcase ) 
+runtime_throw ( "selectdefault: too many cases" ) ; 
+sel->ncase = i+1; 
+cas = &sel->scase[i]; 
+cas->index = index; 
+cas->chan = nil; 
+#line 609 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+cas->kind = CaseDefault; 
+#line 611 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+if ( debug ) 
+runtime_printf ( "selectdefault s=%p index=%d\n" , 
+sel , cas->index ) ; 
+} 
+#line 616 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+static void 
+sellock ( Select *sel ) 
+{ 
+uint32 i; 
+Hchan *c , *c0; 
+#line 622 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+c = nil; 
+for ( i=0; i<sel->ncase; i++ ) { 
+c0 = sel->lockorder[i]; 
+if ( c0 && c0 != c ) { 
+c = sel->lockorder[i]; 
+runtime_lock ( c ) ; 
+} 
+} 
+} 
+#line 632 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+static void 
+selunlock ( Select *sel ) 
+{ 
+int32 i , n , r; 
+Hchan *c; 
+#line 646 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+n = ( int32 ) sel->ncase; 
+r = 0; 
+#line 649 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+if ( n>0 && sel->lockorder[0] == nil ) 
+r = 1; 
+for ( i = n-1; i >= r; i-- ) { 
+c = sel->lockorder[i]; 
+if ( i>0 && sel->lockorder[i-1] == c ) 
+continue; 
+runtime_unlock ( c ) ; 
+} 
+} 
+#line 659 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+static bool 
+selparkcommit ( G *gp , void *sel ) 
+{ 
+USED ( gp ) ; 
+selunlock ( sel ) ; 
+return true; 
+} 
+void runtime_block() __asm__ (GOSYM_PREFIX "runtime.block");
+void runtime_block()
+{
+#line 667 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+
+       runtime_park(nil, nil, "select (no cases)");    // forever
+}
+
+#line 671 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+static int selectgo ( Select** ) ; 
+int32 runtime_selectgo(Select* sel) __asm__ (GOSYM_PREFIX "runtime.selectgo");
+int32 runtime_selectgo(Select* sel)
+{
+  int32 ret;
+#line 675 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+
+       return selectgo(&sel);
+return ret;
+}
+
+#line 679 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+static int 
+selectgo ( Select **selp ) 
+{ 
+Select *sel; 
+uint32 o , i , j , k , done; 
+int64 t0; 
+Scase *cas , *dfl; 
+Hchan *c; 
+SudoG *sg; 
+G *gp; 
+int index; 
+G *g; 
+#line 692 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+sel = *selp; 
+if ( runtime_gcwaiting ( ) ) 
+runtime_gosched ( ) ; 
+#line 696 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+if ( debug ) 
+runtime_printf ( "select: sel=%p\n" , sel ) ; 
+#line 699 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+g = runtime_g ( ) ; 
+#line 701 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+t0 = 0; 
+if ( runtime_blockprofilerate > 0 ) { 
+t0 = runtime_cputicks ( ) ; 
+for ( i=0; i<sel->ncase; i++ ) 
+sel->scase[i].sg.releasetime = -1; 
+} 
+#line 717 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+for ( i=0; i<sel->ncase; i++ ) 
+sel->pollorder[i] = i; 
+for ( i=1; i<sel->ncase; i++ ) { 
+o = sel->pollorder[i]; 
+j = runtime_fastrand1 ( ) % ( i+1 ) ; 
+sel->pollorder[i] = sel->pollorder[j]; 
+sel->pollorder[j] = o; 
+} 
+#line 728 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+for ( i=0; i<sel->ncase; i++ ) { 
+j = i; 
+c = sel->scase[j].chan; 
+while ( j > 0 && sel->lockorder[k= ( j-1 ) /2] < c ) { 
+sel->lockorder[j] = sel->lockorder[k]; 
+j = k; 
+} 
+sel->lockorder[j] = c; 
+} 
+for ( i=sel->ncase; i-->0; ) { 
+c = sel->lockorder[i]; 
+sel->lockorder[i] = sel->lockorder[0]; 
+j = 0; 
+for ( ;; ) { 
+k = j*2+1; 
+if ( k >= i ) 
+break; 
+if ( k+1 < i && sel->lockorder[k] < sel->lockorder[k+1] ) 
+k++; 
+if ( c < sel->lockorder[k] ) { 
+sel->lockorder[j] = sel->lockorder[k]; 
+j = k; 
+continue; 
+} 
+break; 
+} 
+sel->lockorder[j] = c; 
+} 
+#line 763 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+sellock ( sel ) ; 
+#line 765 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+loop: 
+#line 767 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+dfl = nil; 
+for ( i=0; i<sel->ncase; i++ ) { 
+o = sel->pollorder[i]; 
+cas = &sel->scase[o]; 
+c = cas->chan; 
+#line 773 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+switch ( cas->kind ) { 
+case CaseRecv: 
+if ( c->dataqsiz > 0 ) { 
+if ( c->qcount > 0 ) 
+goto asyncrecv; 
+} else { 
+sg = dequeue ( &c->sendq ) ; 
+if ( sg != nil ) 
+goto syncrecv; 
+} 
+if ( c->closed ) 
+goto rclose; 
+break; 
+#line 787 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+case CaseSend: 
+if ( raceenabled ) 
+runtime_racereadpc ( c , runtime_selectgo , chansend ) ; 
+if ( c->closed ) 
+goto sclose; 
+if ( c->dataqsiz > 0 ) { 
+if ( c->qcount < c->dataqsiz ) 
+goto asyncsend; 
+} else { 
+sg = dequeue ( &c->recvq ) ; 
+if ( sg != nil ) 
+goto syncsend; 
+} 
+break; 
+#line 802 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+case CaseDefault: 
+dfl = cas; 
+break; 
+} 
+} 
+#line 808 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+if ( dfl != nil ) { 
+selunlock ( sel ) ; 
+cas = dfl; 
+goto retc; 
+} 
+#line 816 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+done = 0; 
+for ( i=0; i<sel->ncase; i++ ) { 
+o = sel->pollorder[i]; 
+cas = &sel->scase[o]; 
+c = cas->chan; 
+sg = &cas->sg; 
+sg->g = g; 
+sg->selectdone = &done; 
+#line 825 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+switch ( cas->kind ) { 
+case CaseRecv: 
+enqueue ( &c->recvq , sg ) ; 
+break; 
+#line 830 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+case CaseSend: 
+enqueue ( &c->sendq , sg ) ; 
+break; 
+} 
+} 
+#line 836 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+g->param = nil; 
+runtime_park ( selparkcommit , sel , "select" ) ; 
+#line 839 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+sellock ( sel ) ; 
+sg = g->param; 
+#line 844 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+for ( i=0; i<sel->ncase; i++ ) { 
+cas = &sel->scase[i]; 
+if ( cas != ( Scase* ) sg ) { 
+c = cas->chan; 
+if ( cas->kind == CaseSend ) 
+dequeueg ( &c->sendq ) ; 
+else 
+dequeueg ( &c->recvq ) ; 
+} 
+} 
+#line 855 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+if ( sg == nil ) 
+goto loop; 
+#line 858 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+cas = ( Scase* ) sg; 
+c = cas->chan; 
+#line 861 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+if ( c->dataqsiz > 0 ) 
+runtime_throw ( "selectgo: shouldn't happen" ) ; 
+#line 864 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+if ( debug ) 
+runtime_printf ( "wait-return: sel=%p c=%p cas=%p kind=%d\n" , 
+sel , c , cas , cas->kind ) ; 
+#line 868 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+if ( cas->kind == CaseRecv ) { 
+if ( cas->receivedp != nil ) 
+*cas->receivedp = true; 
+} 
+#line 873 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+if ( raceenabled ) { 
+if ( cas->kind == CaseRecv && cas->sg.elem != nil ) 
+runtime_racewriteobjectpc ( cas->sg.elem , c->elemtype , selectgo , chanrecv ) ; 
+else if ( cas->kind == CaseSend ) 
+runtime_racereadobjectpc ( cas->sg.elem , c->elemtype , selectgo , chansend ) ; 
+} 
+#line 880 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+selunlock ( sel ) ; 
+goto retc; 
+#line 883 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+asyncrecv: 
+#line 885 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+if ( raceenabled ) { 
+if ( cas->sg.elem != nil ) 
+runtime_racewriteobjectpc ( cas->sg.elem , c->elemtype , selectgo , chanrecv ) ; 
+runtime_raceacquire ( chanbuf ( c , c->recvx ) ) ; 
+} 
+if ( cas->receivedp != nil ) 
+*cas->receivedp = true; 
+if ( cas->sg.elem != nil ) 
+runtime_memmove ( cas->sg.elem , chanbuf ( c , c->recvx ) , c->elemsize ) ; 
+runtime_memclr ( chanbuf ( c , c->recvx ) , c->elemsize ) ; 
+if ( ++c->recvx == c->dataqsiz ) 
+c->recvx = 0; 
+c->qcount--; 
+sg = dequeue ( &c->sendq ) ; 
+if ( sg != nil ) { 
+gp = sg->g; 
+selunlock ( sel ) ; 
+if ( sg->releasetime ) 
+sg->releasetime = runtime_cputicks ( ) ; 
+runtime_ready ( gp ) ; 
+} else { 
+selunlock ( sel ) ; 
+} 
+goto retc; 
+#line 910 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+asyncsend: 
+#line 912 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+if ( raceenabled ) { 
+runtime_racerelease ( chanbuf ( c , c->sendx ) ) ; 
+runtime_racereadobjectpc ( cas->sg.elem , c->elemtype , selectgo , chansend ) ; 
+} 
+runtime_memmove ( chanbuf ( c , c->sendx ) , cas->sg.elem , c->elemsize ) ; 
+if ( ++c->sendx == c->dataqsiz ) 
+c->sendx = 0; 
+c->qcount++; 
+sg = dequeue ( &c->recvq ) ; 
+if ( sg != nil ) { 
+gp = sg->g; 
+selunlock ( sel ) ; 
+if ( sg->releasetime ) 
+sg->releasetime = runtime_cputicks ( ) ; 
+runtime_ready ( gp ) ; 
+} else { 
+selunlock ( sel ) ; 
+} 
+goto retc; 
+#line 932 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+syncrecv: 
+#line 934 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+if ( raceenabled ) { 
+if ( cas->sg.elem != nil ) 
+runtime_racewriteobjectpc ( cas->sg.elem , c->elemtype , selectgo , chanrecv ) ; 
+racesync ( c , sg ) ; 
+} 
+selunlock ( sel ) ; 
+if ( debug ) 
+runtime_printf ( "syncrecv: sel=%p c=%p o=%d\n" , sel , c , o ) ; 
+if ( cas->receivedp != nil ) 
+*cas->receivedp = true; 
+if ( cas->sg.elem != nil ) 
+runtime_memmove ( cas->sg.elem , sg->elem , c->elemsize ) ; 
+gp = sg->g; 
+gp->param = sg; 
+if ( sg->releasetime ) 
+sg->releasetime = runtime_cputicks ( ) ; 
+runtime_ready ( gp ) ; 
+goto retc; 
+#line 953 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+rclose: 
+#line 955 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+selunlock ( sel ) ; 
+if ( cas->receivedp != nil ) 
+*cas->receivedp = false; 
+if ( cas->sg.elem != nil ) 
+runtime_memclr ( cas->sg.elem , c->elemsize ) ; 
+if ( raceenabled ) 
+runtime_raceacquire ( c ) ; 
+goto retc; 
+#line 964 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+syncsend: 
+#line 966 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+if ( raceenabled ) { 
+runtime_racereadobjectpc ( cas->sg.elem , c->elemtype , selectgo , chansend ) ; 
+racesync ( c , sg ) ; 
+} 
+selunlock ( sel ) ; 
+if ( debug ) 
+runtime_printf ( "syncsend: sel=%p c=%p o=%d\n" , sel , c , o ) ; 
+if ( sg->elem != nil ) 
+runtime_memmove ( sg->elem , cas->sg.elem , c->elemsize ) ; 
+gp = sg->g; 
+gp->param = sg; 
+if ( sg->releasetime ) 
+sg->releasetime = runtime_cputicks ( ) ; 
+runtime_ready ( gp ) ; 
+#line 981 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+retc: 
+#line 983 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+index = cas->index; 
+if ( cas->sg.releasetime > 0 ) 
+runtime_blockevent ( cas->sg.releasetime - t0 , 2 ) ; 
+runtime_free ( sel ) ; 
+return index; 
+#line 989 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+sclose: 
+#line 991 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+selunlock ( sel ) ; 
+runtime_panicstring ( "send on closed channel" ) ; 
+return 0; 
+} 
+#line 997 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+typedef struct runtimeSelect runtimeSelect; 
+struct runtimeSelect 
+{ 
+uintptr dir; 
+ChanType *typ; 
+Hchan *ch; 
+byte *val; 
+} ; 
+#line 1007 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+enum SelectDir { 
+SelectSend = 1 , 
+SelectRecv , 
+SelectDefault , 
+} ; 
+struct reflect_rselect_ret {
+  intgo chosen;
+  bool recvOK;
+};
+struct reflect_rselect_ret reflect_rselect(Slice cases) __asm__ (GOSYM_PREFIX "reflect.rselect");
+struct reflect_rselect_ret reflect_rselect(Slice cases)
+{
+  intgo chosen;
+  bool recvOK;
+#line 1013 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+
+       int32 i;
+       Select *sel;
+       runtimeSelect* rcase, *rc;
+
+       chosen = -1;
+       recvOK = false;
+
+       rcase = (runtimeSelect*)cases.__values;
+
+       sel = newselect(cases.__count);
+       for(i=0; i<cases.__count; i++) {
+               rc = &rcase[i];
+               switch(rc->dir) {
+               case SelectDefault:
+                       selectdefault(sel, i);
+                       break;
+               case SelectSend:
+                       if(rc->ch == nil)
+                               break;
+                       selectsend(sel, rc->ch, i, rc->val);
+                       break;
+               case SelectRecv:
+                       if(rc->ch == nil)
+                               break;
+                       selectrecv(sel, rc->ch, i, rc->val, &recvOK);
+                       break;
+               }
+       }
+
+       chosen = (intgo)(uintptr)selectgo(&sel);
+  {
+    struct reflect_rselect_ret __ret;
+    __ret.chosen = chosen;
+    __ret.recvOK = recvOK;
+    return __ret;
+  }
+}
+
+#line 1046 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+static void closechan ( Hchan *c , void *pc ) ; 
+void runtime_closechan(Hchan* c) __asm__ (GOSYM_PREFIX "runtime.closechan");
+void runtime_closechan(Hchan* c)
+{
+#line 1048 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+
+       closechan(c, runtime_getcallerpc(&c));
+}
+void reflect_chanclose(Hchan* c) __asm__ (GOSYM_PREFIX "reflect.chanclose");
+void reflect_chanclose(Hchan* c)
+{
+#line 1052 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+
+       closechan(c, runtime_getcallerpc(&c));
+}
+
+#line 1056 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+static void 
+closechan ( Hchan *c , void *pc ) 
+{ 
+SudoG *sg; 
+G* gp; 
+#line 1062 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+if ( c == nil ) 
+runtime_panicstring ( "close of nil channel" ) ; 
+#line 1065 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+if ( runtime_gcwaiting ( ) ) 
+runtime_gosched ( ) ; 
+#line 1068 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+runtime_lock ( c ) ; 
+if ( c->closed ) { 
+runtime_unlock ( c ) ; 
+runtime_panicstring ( "close of closed channel" ) ; 
+} 
+#line 1074 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+if ( raceenabled ) { 
+runtime_racewritepc ( c , pc , runtime_closechan ) ; 
+runtime_racerelease ( c ) ; 
+} 
+#line 1079 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+c->closed = true; 
+#line 1082 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+for ( ;; ) { 
+sg = dequeue ( &c->recvq ) ; 
+if ( sg == nil ) 
+break; 
+gp = sg->g; 
+gp->param = nil; 
+if ( sg->releasetime ) 
+sg->releasetime = runtime_cputicks ( ) ; 
+runtime_ready ( gp ) ; 
+} 
+#line 1094 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+for ( ;; ) { 
+sg = dequeue ( &c->sendq ) ; 
+if ( sg == nil ) 
+break; 
+gp = sg->g; 
+gp->param = nil; 
+if ( sg->releasetime ) 
+sg->releasetime = runtime_cputicks ( ) ; 
+runtime_ready ( gp ) ; 
+} 
+#line 1105 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+runtime_unlock ( c ) ; 
+} 
+#line 1108 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+void 
+__go_builtin_close ( Hchan *c ) 
+{ 
+runtime_closechan ( c ) ; 
+} 
+intgo reflect_chanlen(Hchan* c) __asm__ (GOSYM_PREFIX "reflect.chanlen");
+intgo reflect_chanlen(Hchan* c)
+{
+  intgo len;
+#line 1114 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+
+       if(c == nil)
+               len = 0;
+       else
+               len = c->qcount;
+return len;
+}
+
+#line 1121 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+intgo 
+__go_chan_len ( Hchan *c ) 
+{ 
+return reflect_chanlen ( c ) ; 
+} 
+intgo reflect_chancap(Hchan* c) __asm__ (GOSYM_PREFIX "reflect.chancap");
+intgo reflect_chancap(Hchan* c)
+{
+  intgo cap;
+#line 1127 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+
+       if(c == nil)
+               cap = 0;
+       else
+               cap = c->dataqsiz;
+return cap;
+}
+
+#line 1134 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+intgo 
+__go_chan_cap ( Hchan *c ) 
+{ 
+return reflect_chancap ( c ) ; 
+} 
+#line 1140 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+static SudoG* 
+dequeue ( WaitQ *q ) 
+{ 
+SudoG *sgp; 
+#line 1145 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+loop: 
+sgp = q->first; 
+if ( sgp == nil ) 
+return nil; 
+q->first = sgp->link; 
+#line 1152 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+if ( sgp->selectdone != nil ) { 
+#line 1154 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+if ( *sgp->selectdone != 0 || !runtime_cas ( sgp->selectdone , 0 , 1 ) ) 
+goto loop; 
+} 
+#line 1158 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+return sgp; 
+} 
+#line 1161 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+static void 
+dequeueg ( WaitQ *q ) 
+{ 
+SudoG **l , *sgp , *prevsgp; 
+G *g; 
+#line 1167 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+g = runtime_g ( ) ; 
+prevsgp = nil; 
+for ( l=&q->first; ( sgp=*l ) != nil; l=&sgp->link , prevsgp=sgp ) { 
+if ( sgp->g == g ) { 
+*l = sgp->link; 
+if ( q->last == sgp ) 
+q->last = prevsgp; 
+break; 
+} 
+} 
+} 
+#line 1179 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+static void 
+enqueue ( WaitQ *q , SudoG *sgp ) 
+{ 
+sgp->link = nil; 
+if ( q->first == nil ) { 
+q->first = sgp; 
+q->last = sgp; 
+return; 
+} 
+q->last->link = sgp; 
+q->last = sgp; 
+} 
+#line 1192 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/chan.goc"
+static void 
+racesync ( Hchan *c , SudoG *sg ) 
+{ 
+runtime_racerelease ( chanbuf ( c , 0 ) ) ; 
+runtime_raceacquireg ( sg->g , chanbuf ( c , 0 ) ) ; 
+runtime_racereleaseg ( sg->g , chanbuf ( c , 0 ) ) ; 
+runtime_raceacquire ( chanbuf ( c , 0 ) ) ; 
+} 
\ No newline at end of file
index ebe0493856f4ef6067d5493ced5dd80af65cad63..b8038d41c26a2374aa62d337324eba623f941da9 100644 (file)
@@ -183,8 +183,10 @@ asynch:
                goto asynch;
        }
 
-       if(raceenabled)
+       if(raceenabled) {
+               runtime_raceacquire(chanbuf(c, c->sendx));
                runtime_racerelease(chanbuf(c, c->sendx));
+       }
 
        runtime_memmove(chanbuf(c, c->sendx), ep, c->elemsize);
        if(++c->sendx == c->dataqsiz)
@@ -317,8 +319,10 @@ asynch:
                goto asynch;
        }
 
-       if(raceenabled)
+       if(raceenabled) {
                runtime_raceacquire(chanbuf(c, c->recvx));
+               runtime_racerelease(chanbuf(c, c->recvx));
+       }
 
        if(ep != nil)
                runtime_memmove(ep, chanbuf(c, c->recvx), c->elemsize);
@@ -886,6 +890,7 @@ asyncrecv:
                if(cas->sg.elem != nil)
                        runtime_racewriteobjectpc(cas->sg.elem, c->elemtype, selectgo, chanrecv);
                runtime_raceacquire(chanbuf(c, c->recvx));
+               runtime_racerelease(chanbuf(c, c->recvx));
        }
        if(cas->receivedp != nil)
                *cas->receivedp = true;
@@ -910,6 +915,7 @@ asyncrecv:
 asyncsend:
        // can send to buffer
        if(raceenabled) {
+               runtime_raceacquire(chanbuf(c, c->sendx));
                runtime_racerelease(chanbuf(c, c->sendx));
                runtime_racereadobjectpc(cas->sg.elem, c->elemtype, selectgo, chansend);
        }
diff --git a/libgo/runtime/cpuprof.c b/libgo/runtime/cpuprof.c
new file mode 100644 (file)
index 0000000..3398a92
--- /dev/null
@@ -0,0 +1,350 @@
+// AUTO-GENERATED by autogen.sh; DO NOT EDIT
+
+#include "runtime.h"
+#include "arch.h"
+#include "malloc.h"
+#include "array.h"
+
+#line 57 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/cpuprof.goc"
+typedef struct __go_open_array Slice; 
+#define array __values 
+#define len __count 
+#define cap __capacity 
+#line 62 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/cpuprof.goc"
+enum 
+{ 
+HashSize = 1<<10 , 
+LogSize = 1<<17 , 
+Assoc = 4 , 
+MaxStack = 64 , 
+} ; 
+#line 70 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/cpuprof.goc"
+typedef struct Profile Profile; 
+typedef struct Bucket Bucket; 
+typedef struct Entry Entry; 
+#line 74 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/cpuprof.goc"
+struct Entry { 
+uintptr count; 
+uintptr depth; 
+uintptr stack[MaxStack]; 
+} ; 
+#line 80 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/cpuprof.goc"
+struct Bucket { 
+Entry entry[Assoc]; 
+} ; 
+#line 84 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/cpuprof.goc"
+struct Profile { 
+bool on; 
+Note wait; 
+uintptr count; 
+uintptr evicts; 
+uintptr lost; 
+uintptr totallost; 
+#line 93 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/cpuprof.goc"
+Bucket hash[HashSize]; 
+#line 98 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/cpuprof.goc"
+uintptr log[2][LogSize/2]; 
+uintptr nlog; 
+int32 toggle; 
+uint32 handoff; 
+#line 106 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/cpuprof.goc"
+uint32 wtoggle; 
+bool wholding; 
+bool flushing; 
+bool eod_sent; 
+} ; 
+#line 112 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/cpuprof.goc"
+static Lock lk; 
+static Profile *prof; 
+#line 115 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/cpuprof.goc"
+static void tick ( uintptr* , int32 ) ; 
+static void add ( Profile* , uintptr* , int32 ) ; 
+static bool evict ( Profile* , Entry* ) ; 
+static bool flushlog ( Profile* ) ; 
+#line 120 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/cpuprof.goc"
+static uintptr eod[3] = { 0 , 1 , 0 } ; 
+#line 125 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/cpuprof.goc"
+static void 
+LostProfileData ( void ) 
+{ 
+} 
+#line 130 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/cpuprof.goc"
+extern void runtime_SetCPUProfileRate ( intgo ) 
+__asm__ ( GOSYM_PREFIX "runtime.SetCPUProfileRate" ) ; 
+#line 135 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/cpuprof.goc"
+void 
+runtime_SetCPUProfileRate ( intgo hz ) 
+{ 
+uintptr *p; 
+uintptr n; 
+#line 142 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/cpuprof.goc"
+if ( hz < 0 ) 
+hz = 0; 
+if ( hz > 1000000 ) 
+hz = 1000000; 
+#line 147 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/cpuprof.goc"
+runtime_lock ( &lk ) ; 
+if ( hz > 0 ) { 
+if ( prof == nil ) { 
+prof = runtime_SysAlloc ( sizeof *prof , &mstats.other_sys ) ; 
+if ( prof == nil ) { 
+runtime_printf ( "runtime: cpu profiling cannot allocate memory\n" ) ; 
+runtime_unlock ( &lk ) ; 
+return; 
+} 
+} 
+if ( prof->on || prof->handoff != 0 ) { 
+runtime_printf ( "runtime: cannot set cpu profile rate until previous profile has finished.\n" ) ; 
+runtime_unlock ( &lk ) ; 
+return; 
+} 
+#line 163 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/cpuprof.goc"
+prof->on = true; 
+p = prof->log[0]; 
+#line 167 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/cpuprof.goc"
+*p++ = 0; 
+*p++ = 3; 
+*p++ = 0; 
+*p++ = 1000000 / hz; 
+*p++ = 0; 
+prof->nlog = p - prof->log[0]; 
+prof->toggle = 0; 
+prof->wholding = false; 
+prof->wtoggle = 0; 
+prof->flushing = false; 
+prof->eod_sent = false; 
+runtime_noteclear ( &prof->wait ) ; 
+#line 180 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/cpuprof.goc"
+runtime_setcpuprofilerate ( tick , hz ) ; 
+} else if ( prof != nil && prof->on ) { 
+runtime_setcpuprofilerate ( nil , 0 ) ; 
+prof->on = false; 
+#line 187 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/cpuprof.goc"
+for ( ;; ) { 
+n = prof->handoff; 
+if ( n&0x80000000 ) 
+runtime_printf ( "runtime: setcpuprofile(off) twice" ) ; 
+if ( runtime_cas ( &prof->handoff , n , n|0x80000000 ) ) 
+break; 
+} 
+if ( n == 0 ) { 
+#line 196 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/cpuprof.goc"
+runtime_notewakeup ( &prof->wait ) ; 
+} 
+} 
+runtime_unlock ( &lk ) ; 
+} 
+#line 202 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/cpuprof.goc"
+static void 
+tick ( uintptr *pc , int32 n ) 
+{ 
+add ( prof , pc , n ) ; 
+} 
+#line 213 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/cpuprof.goc"
+static void 
+add ( Profile *p , uintptr *pc , int32 n ) 
+{ 
+int32 i , j; 
+uintptr h , x; 
+Bucket *b; 
+Entry *e; 
+#line 221 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/cpuprof.goc"
+if ( n > MaxStack ) 
+n = MaxStack; 
+#line 225 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/cpuprof.goc"
+h = 0; 
+for ( i=0; i<n; i++ ) { 
+h = h<<8 | ( h>> ( 8* ( sizeof ( h ) -1 ) ) ) ; 
+x = pc[i]; 
+h += x*31 + x*7 + x*3; 
+} 
+p->count++; 
+#line 234 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/cpuprof.goc"
+b = &p->hash[h%HashSize]; 
+for ( i=0; i<Assoc; i++ ) { 
+e = &b->entry[i]; 
+if ( e->depth != ( uintptr ) n ) 
+continue; 
+for ( j=0; j<n; j++ ) 
+if ( e->stack[j] != pc[j] ) 
+goto ContinueAssoc; 
+e->count++; 
+return; 
+ContinueAssoc:; 
+} 
+#line 248 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/cpuprof.goc"
+e = &b->entry[0]; 
+for ( i=1; i<Assoc; i++ ) 
+if ( b->entry[i].count < e->count ) 
+e = &b->entry[i]; 
+if ( e->count > 0 ) { 
+if ( !evict ( p , e ) ) { 
+#line 255 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/cpuprof.goc"
+p->lost++; 
+p->totallost++; 
+return; 
+} 
+p->evicts++; 
+} 
+#line 263 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/cpuprof.goc"
+e->depth = n; 
+e->count = 1; 
+for ( i=0; i<n; i++ ) 
+e->stack[i] = pc[i]; 
+} 
+#line 275 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/cpuprof.goc"
+static bool 
+evict ( Profile *p , Entry *e ) 
+{ 
+int32 i , d , nslot; 
+uintptr *log , *q; 
+#line 281 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/cpuprof.goc"
+d = e->depth; 
+nslot = d+2; 
+log = p->log[p->toggle]; 
+if ( p->nlog+nslot > nelem ( p->log[0] ) ) { 
+if ( !flushlog ( p ) ) 
+return false; 
+log = p->log[p->toggle]; 
+} 
+#line 290 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/cpuprof.goc"
+q = log+p->nlog; 
+*q++ = e->count; 
+*q++ = d; 
+for ( i=0; i<d; i++ ) 
+*q++ = e->stack[i]; 
+p->nlog = q - log; 
+e->count = 0; 
+return true; 
+} 
+#line 304 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/cpuprof.goc"
+static bool 
+flushlog ( Profile *p ) 
+{ 
+uintptr *log , *q; 
+#line 309 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/cpuprof.goc"
+if ( !runtime_cas ( &p->handoff , 0 , p->nlog ) ) 
+return false; 
+runtime_notewakeup ( &p->wait ) ; 
+#line 313 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/cpuprof.goc"
+p->toggle = 1 - p->toggle; 
+log = p->log[p->toggle]; 
+q = log; 
+if ( p->lost > 0 ) { 
+*q++ = p->lost; 
+*q++ = 1; 
+*q++ = ( uintptr ) LostProfileData; 
+} 
+p->nlog = q - log; 
+return true; 
+} 
+#line 327 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/cpuprof.goc"
+Slice 
+getprofile ( Profile *p ) 
+{ 
+uint32 i , j , n; 
+Slice ret; 
+Bucket *b; 
+Entry *e; 
+#line 335 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/cpuprof.goc"
+ret.array = nil; 
+ret.len = 0; 
+ret.cap = 0; 
+#line 339 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/cpuprof.goc"
+if ( p == nil ) 
+return ret; 
+#line 342 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/cpuprof.goc"
+if ( p->wholding ) { 
+#line 345 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/cpuprof.goc"
+for ( ;; ) { 
+n = p->handoff; 
+if ( n == 0 ) { 
+runtime_printf ( "runtime: phase error during cpu profile handoff\n" ) ; 
+return ret; 
+} 
+if ( n & 0x80000000 ) { 
+p->wtoggle = 1 - p->wtoggle; 
+p->wholding = false; 
+p->flushing = true; 
+goto flush; 
+} 
+if ( runtime_cas ( &p->handoff , n , 0 ) ) 
+break; 
+} 
+p->wtoggle = 1 - p->wtoggle; 
+p->wholding = false; 
+} 
+#line 364 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/cpuprof.goc"
+if ( p->flushing ) 
+goto flush; 
+#line 367 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/cpuprof.goc"
+if ( !p->on && p->handoff == 0 ) 
+return ret; 
+#line 371 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/cpuprof.goc"
+runtime_notetsleepg ( &p->wait , -1 ) ; 
+runtime_noteclear ( &p->wait ) ; 
+#line 374 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/cpuprof.goc"
+n = p->handoff; 
+if ( n == 0 ) { 
+runtime_printf ( "runtime: phase error during cpu profile wait\n" ) ; 
+return ret; 
+} 
+if ( n == 0x80000000 ) { 
+p->flushing = true; 
+goto flush; 
+} 
+n &= ~0x80000000; 
+#line 386 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/cpuprof.goc"
+p->wholding = true; 
+#line 388 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/cpuprof.goc"
+ret.array = ( byte* ) p->log[p->wtoggle]; 
+ret.len = n*sizeof ( uintptr ) ; 
+ret.cap = ret.len; 
+return ret; 
+#line 393 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/cpuprof.goc"
+flush: 
+#line 398 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/cpuprof.goc"
+for ( i=0; i<HashSize; i++ ) { 
+b = &p->hash[i]; 
+for ( j=0; j<Assoc; j++ ) { 
+e = &b->entry[j]; 
+if ( e->count > 0 && !evict ( p , e ) ) { 
+#line 404 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/cpuprof.goc"
+goto breakflush; 
+} 
+} 
+} 
+breakflush: 
+#line 411 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/cpuprof.goc"
+if ( p->nlog > 0 ) { 
+#line 414 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/cpuprof.goc"
+ret.array = ( byte* ) p->log[p->toggle]; 
+ret.len = p->nlog*sizeof ( uintptr ) ; 
+ret.cap = ret.len; 
+p->nlog = 0; 
+return ret; 
+} 
+#line 422 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/cpuprof.goc"
+if ( !p->eod_sent ) { 
+#line 425 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/cpuprof.goc"
+p->eod_sent = true; 
+ret.array = ( byte* ) eod; 
+ret.len = sizeof eod; 
+ret.cap = ret.len; 
+return ret; 
+} 
+#line 433 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/cpuprof.goc"
+p->flushing = false; 
+if ( !runtime_cas ( &p->handoff , p->handoff , 0 ) ) 
+runtime_printf ( "runtime: profile flush racing with something\n" ) ; 
+return ret; 
+} 
+Slice runtime_CPUProfile() __asm__ (GOSYM_PREFIX "runtime.CPUProfile");
+Slice runtime_CPUProfile()
+{
+  Slice ret;
+#line 441 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/cpuprof.goc"
+
+       ret = getprofile(prof);
+return ret;
+}
index 28ae9bbfa184707041ed2497d7d07f6a8da46f34..7d27bc6a43aac785c6307d5b02ce0cbce482e9e9 100644 (file)
@@ -87,7 +87,6 @@ struct Profile {
        uintptr count;          // tick count
        uintptr evicts;         // eviction count
        uintptr lost;           // lost ticks that need to be logged
-       uintptr totallost;      // total lost ticks
 
        // Active recent stack traces.
        Bucket hash[HashSize];
@@ -253,7 +252,6 @@ add(Profile *p, uintptr *pc, int32 n)
                if(!evict(p, e)) {
                        // Could not evict entry.  Record lost stack.
                        p->lost++;
-                       p->totallost++;
                        return;
                }
                p->evicts++;
@@ -317,6 +315,7 @@ flushlog(Profile *p)
                *q++ = p->lost;
                *q++ = 1;
                *q++ = (uintptr)LostProfileData;
+               p->lost = 0;
        }
        p->nlog = q - log;
        return true;
index 93f90f54118a49928f77df1a1c16884a66c20052..ff4bf0c5b1fd7db6d0c11f741c7f7aaf87c8d134 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.
 
-// +build darwin dragonfly freebsd linux netbsd openbsd solaris windows
+// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows
 
 #include "runtime.h"
 #include "array.h"
index 73cc1602df78ba333a405e152c3d68a1fc4324be..ea8d070e9f3514f3fcaffde50c83a27169086918 100644 (file)
 
 // Return current time.  This is the implementation of time.now().
 
-struct time_now_ret
-{
-  int64_t sec;
-  int32_t nsec;
-};
-
-struct time_now_ret now()
-  __asm__ (GOSYM_PREFIX "time.now")
-  __attribute__ ((no_split_stack));
-
 struct time_now_ret
 now()
 {
index 1624122dace8c193a697c33b6a08ecb07f4cd1aa..4a1bf5636e50ec77dcf307a6b66a5569bbb5ba04 100644 (file)
@@ -238,16 +238,13 @@ runtime_sighandler (int sig, Siginfo *info,
 /* The start of handling a signal which panics.  */
 
 static void
-sig_panic_leadin (int sig)
+sig_panic_leadin (G *gp)
 {
   int i;
   sigset_t clear;
 
-  if (runtime_m ()->mallocing)
-    {
-      runtime_printf ("caught signal while mallocing: %d\n", sig);
-      runtime_throw ("caught signal while mallocing");
-    }
+  if (!runtime_canpanic (gp))
+    runtime_throw ("unexpected signal during runtime execution");
 
   /* The signal handler blocked signals; unblock them.  */
   i = sigfillset (&clear);
@@ -281,13 +278,14 @@ sig_panic_info_handler (int sig, Siginfo *info, void *context)
   /* It would be nice to set g->sigpc here as the gc library does, but
      I don't know how to get it portably.  */
 
-  sig_panic_leadin (sig);
+  sig_panic_leadin (g);
 
   switch (sig)
     {
 #ifdef SIGBUS
     case SIGBUS:
-      if (info->si_code == BUS_ADRERR && (uintptr_t) info->si_addr < 0x1000)
+      if ((info->si_code == BUS_ADRERR && (uintptr_t) info->si_addr < 0x1000)
+         || g->paniconfault)
        runtime_panicstring ("invalid memory address or "
                             "nil pointer dereference");
       runtime_printf ("unexpected fault address %p\n", info->si_addr);
@@ -296,10 +294,11 @@ sig_panic_info_handler (int sig, Siginfo *info, void *context)
 
 #ifdef SIGSEGV
     case SIGSEGV:
-      if ((info->si_code == 0
-          || info->si_code == SEGV_MAPERR
-          || info->si_code == SEGV_ACCERR)
-         && (uintptr_t) info->si_addr < 0x1000)
+      if (((info->si_code == 0
+           || info->si_code == SEGV_MAPERR
+           || info->si_code == SEGV_ACCERR)
+          && (uintptr_t) info->si_addr < 0x1000)
+         || g->paniconfault)
        runtime_panicstring ("invalid memory address or "
                             "nil pointer dereference");
       runtime_printf ("unexpected fault address %p\n", info->si_addr);
@@ -342,7 +341,7 @@ sig_panic_handler (int sig)
   g->sigcode0 = 0;
   g->sigcode1 = 0;
 
-  sig_panic_leadin (sig);
+  sig_panic_leadin (g);
 
   switch (sig)
     {
diff --git a/libgo/runtime/heapdump.c b/libgo/runtime/heapdump.c
new file mode 100644 (file)
index 0000000..d0cfb01
--- /dev/null
@@ -0,0 +1,776 @@
+// Copyright 2014 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.
+
+// Implementation of runtime/debug.WriteHeapDump.  Writes all
+// objects in the heap plus additional info (roots, threads,
+// finalizers, etc.) to a file.
+
+// The format of the dumped file is described at
+// http://code.google.com/p/go-wiki/wiki/heapdump13
+
+#include "runtime.h"
+#include "arch.h"
+#include "malloc.h"
+#include "mgc0.h"
+#include "go-type.h"
+#include "go-defer.h"
+#include "go-panic.h"
+
+#define hash __hash
+#define KindNoPointers GO_NO_POINTERS
+
+enum {
+       FieldKindEol = 0,
+       FieldKindPtr = 1,
+       FieldKindString = 2,
+       FieldKindSlice = 3,
+       FieldKindIface = 4,
+       FieldKindEface = 5,
+
+       TagEOF = 0,
+       TagObject = 1,
+       TagOtherRoot = 2,
+       TagType = 3,
+       TagGoRoutine = 4,
+       TagStackFrame = 5,
+       TagParams = 6,
+       TagFinalizer = 7,
+       TagItab = 8,
+       TagOSThread = 9,
+       TagMemStats = 10,
+       TagQueuedFinalizer = 11,
+       TagData = 12,
+       TagBss = 13,
+       TagDefer = 14,
+       TagPanic = 15,
+       TagMemProf = 16,
+       TagAllocSample = 17,
+
+       TypeInfo_Conservative = 127,
+};
+
+// static uintptr* playgcprog(uintptr offset, uintptr *prog, void (*callback)(void*,uintptr,uintptr), void *arg);
+// static void dumpfields(uintptr *prog);
+static void dumpefacetypes(void *obj, uintptr size, const Type *type, uintptr kind);
+
+// fd to write the dump to.
+static uintptr dumpfd;
+
+// buffer of pending write data
+enum {
+       BufSize = 4096,
+};
+static byte buf[BufSize];
+static uintptr nbuf;
+
+static void
+hwrite(const byte *data, uintptr len)
+{
+       if(len + nbuf <= BufSize) {
+               runtime_memmove(buf + nbuf, data, len);
+               nbuf += len;
+               return;
+       }
+       runtime_write(dumpfd, buf, nbuf);
+       if(len >= BufSize) {
+               runtime_write(dumpfd, data, len);
+               nbuf = 0;
+       } else {
+               runtime_memmove(buf, data, len);
+               nbuf = len;
+       }
+}
+
+static void
+flush(void)
+{
+       runtime_write(dumpfd, buf, nbuf);
+       nbuf = 0;
+}
+
+// Cache of types that have been serialized already.
+// We use a type's hash field to pick a bucket.
+// Inside a bucket, we keep a list of types that
+// have been serialized so far, most recently used first.
+// Note: when a bucket overflows we may end up
+// serializing a type more than once.  That's ok.
+enum {
+       TypeCacheBuckets = 256, // must be a power of 2
+       TypeCacheAssoc = 4,
+};
+typedef struct TypeCacheBucket TypeCacheBucket;
+struct TypeCacheBucket {
+       const Type *t[TypeCacheAssoc];
+};
+static TypeCacheBucket typecache[TypeCacheBuckets];
+
+// dump a uint64 in a varint format parseable by encoding/binary
+static void
+dumpint(uint64 v)
+{
+       byte buf[10];
+       int32 n;
+       n = 0;
+       while(v >= 0x80) {
+               buf[n++] = v | 0x80;
+               v >>= 7;
+       }
+       buf[n++] = v;
+       hwrite(buf, n);
+}
+
+static void
+dumpbool(bool b)
+{
+       dumpint(b ? 1 : 0);
+}
+
+// dump varint uint64 length followed by memory contents
+static void
+dumpmemrange(const byte *data, uintptr len)
+{
+       dumpint(len);
+       hwrite(data, len);
+}
+
+static void
+dumpstr(String s)
+{
+       dumpmemrange(s.str, s.len);
+}
+
+static void
+dumpcstr(const int8 *c)
+{
+       dumpmemrange((const byte*)c, runtime_findnull((const byte*)c));
+}
+
+// dump information for a type
+static void
+dumptype(const Type *t)
+{
+       TypeCacheBucket *b;
+       int32 i, j;
+
+       if(t == nil) {
+               return;
+       }
+
+       // If we've definitely serialized the type before,
+       // no need to do it again.
+       b = &typecache[t->hash & (TypeCacheBuckets-1)];
+       if(t == b->t[0]) return;
+       for(i = 1; i < TypeCacheAssoc; i++) {
+               if(t == b->t[i]) {
+                       // Move-to-front
+                       for(j = i; j > 0; j--) {
+                               b->t[j] = b->t[j-1];
+                       }
+                       b->t[0] = t;
+                       return;
+               }
+       }
+       // Might not have been dumped yet.  Dump it and
+       // remember we did so.
+       for(j = TypeCacheAssoc-1; j > 0; j--) {
+               b->t[j] = b->t[j-1];
+       }
+       b->t[0] = t;
+       
+       // dump the type
+       dumpint(TagType);
+       dumpint((uintptr)t);
+       dumpint(t->__size);
+       if(t->__uncommon == nil || t->__uncommon->__pkg_path == nil || t->__uncommon->__name == nil) {
+               dumpstr(*t->__reflection);
+       } else {
+               dumpint(t->__uncommon->__pkg_path->len + 1 + t->__uncommon->__name->len);
+               hwrite(t->__uncommon->__pkg_path->str, t->__uncommon->__pkg_path->len);
+               hwrite((const byte*)".", 1);
+               hwrite(t->__uncommon->__name->str, t->__uncommon->__name->len);
+       }
+       dumpbool(t->__size > PtrSize || (t->__code & KindNoPointers) == 0);
+       // dumpfields((uintptr*)t->gc + 1);
+}
+
+// returns true if object is scannable
+static bool
+scannable(byte *obj)
+{
+       uintptr *b, off, shift;
+
+       off = (uintptr*)obj - (uintptr*)runtime_mheap.arena_start;  // word offset
+       b = (uintptr*)runtime_mheap.arena_start - off/wordsPerBitmapWord - 1;
+       shift = off % wordsPerBitmapWord;
+       return ((*b >> shift) & bitScan) != 0;
+}
+
+// dump an object
+static void
+dumpobj(byte *obj, uintptr size, const Type *type, uintptr kind)
+{
+       if(type != nil) {
+               dumptype(type);
+               dumpefacetypes(obj, size, type, kind);
+       }
+
+       dumpint(TagObject);
+       dumpint((uintptr)obj);
+       dumpint((uintptr)type);
+       dumpint(kind);
+       dumpmemrange(obj, size);
+}
+
+static void
+dumpotherroot(const char *description, byte *to)
+{
+       dumpint(TagOtherRoot);
+       dumpcstr((const int8 *)description);
+       dumpint((uintptr)to);
+}
+
+static void
+dumpfinalizer(byte *obj, FuncVal *fn, const FuncType* ft, const PtrType *ot)
+{
+       dumpint(TagFinalizer);
+       dumpint((uintptr)obj);
+       dumpint((uintptr)fn);
+       dumpint((uintptr)fn->fn);
+       dumpint((uintptr)ft);
+       dumpint((uintptr)ot);
+}
+
+typedef struct ChildInfo ChildInfo;
+struct ChildInfo {
+       // Information passed up from the callee frame about
+       // the layout of the outargs region.
+       uintptr argoff;     // where the arguments start in the frame
+       uintptr arglen;     // size of args region
+       BitVector args;    // if args.n >= 0, pointer map of args region
+
+       byte *sp;           // callee sp
+       uintptr depth;      // depth in call stack (0 == most recent)
+};
+
+static void
+dumpgoroutine(G *gp)
+{
+       // ChildInfo child;
+       Defer *d;
+       Panic *p;
+
+       dumpint(TagGoRoutine);
+       dumpint((uintptr)gp);
+       dumpint((uintptr)0);
+       dumpint(gp->goid);
+       dumpint(gp->gopc);
+       dumpint(gp->status);
+       dumpbool(gp->issystem);
+       dumpbool(gp->isbackground);
+       dumpint(gp->waitsince);
+       dumpcstr((const int8 *)gp->waitreason);
+       dumpint((uintptr)0);
+       dumpint((uintptr)gp->m);
+       dumpint((uintptr)gp->defer);
+       dumpint((uintptr)gp->panic);
+
+       // dump stack
+       // child.args.n = -1;
+       // child.arglen = 0;
+       // child.sp = nil;
+       // child.depth = 0;
+       // if(!ScanStackByFrames)
+       //      runtime_throw("need frame info to dump stacks");
+       // runtime_gentraceback(pc, sp, lr, gp, 0, nil, 0x7fffffff, dumpframe, &child, false);
+
+       // dump defer & panic records
+       for(d = gp->defer; d != nil; d = d->__next) {
+               dumpint(TagDefer);
+               dumpint((uintptr)d);
+               dumpint((uintptr)gp);
+               dumpint((uintptr)d->__arg);
+               dumpint((uintptr)d->__frame);
+               dumpint((uintptr)d->__pfn);
+               dumpint((uintptr)0);
+               dumpint((uintptr)d->__next);
+       }
+       for (p = gp->panic; p != nil; p = p->__next) {
+               dumpint(TagPanic);
+               dumpint((uintptr)p);
+               dumpint((uintptr)gp);
+               dumpint((uintptr)p->__arg.__type_descriptor);
+               dumpint((uintptr)p->__arg.__object);
+               dumpint((uintptr)0);
+               dumpint((uintptr)p->__next);
+       }
+}
+
+static void
+dumpgs(void)
+{
+       G *gp;
+       uint32 i;
+
+       // goroutines & stacks
+       for(i = 0; i < runtime_allglen; i++) {
+               gp = runtime_allg[i];
+               switch(gp->status){
+               default:
+                       runtime_printf("unexpected G.status %d\n", gp->status);
+                       runtime_throw("mark - bad status");
+               case Gdead:
+                       break;
+               case Grunnable:
+               case Gsyscall:
+               case Gwaiting:
+                       dumpgoroutine(gp);
+                       break;
+               }
+       }
+}
+
+static void
+finq_callback(FuncVal *fn, void *obj, const FuncType *ft, const PtrType *ot)
+{
+       dumpint(TagQueuedFinalizer);
+       dumpint((uintptr)obj);
+       dumpint((uintptr)fn);
+       dumpint((uintptr)fn->fn);
+       dumpint((uintptr)ft);
+       dumpint((uintptr)ot);
+}
+
+
+static void
+dumproots(void)
+{
+       MSpan *s, **allspans;
+       uint32 spanidx;
+       Special *sp;
+       SpecialFinalizer *spf;
+       byte *p;
+
+       // data segment
+       // dumpint(TagData);
+       // dumpint((uintptr)data);
+       // dumpmemrange(data, edata - data);
+       // dumpfields((uintptr*)gcdata + 1);
+
+       // bss segment
+       // dumpint(TagBss);
+       // dumpint((uintptr)bss);
+       // dumpmemrange(bss, ebss - bss);
+       // dumpfields((uintptr*)gcbss + 1);
+       
+       // MSpan.types
+       allspans = runtime_mheap.allspans;
+       for(spanidx=0; spanidx<runtime_mheap.nspan; spanidx++) {
+               s = allspans[spanidx];
+               if(s->state == MSpanInUse) {
+                       // The garbage collector ignores type pointers stored in MSpan.types:
+                       //  - Compiler-generated types are stored outside of heap.
+                       //  - The reflect package has runtime-generated types cached in its data structures.
+                       //    The garbage collector relies on finding the references via that cache.
+                       switch(s->types.compression) {
+                       case MTypes_Empty:
+                       case MTypes_Single:
+                               break;
+                       case MTypes_Words:
+                       case MTypes_Bytes:
+                               dumpotherroot("runtime type info", (byte*)s->types.data);
+                               break;
+                       }
+
+                       // Finalizers
+                       for(sp = s->specials; sp != nil; sp = sp->next) {
+                               if(sp->kind != KindSpecialFinalizer)
+                                       continue;
+                               spf = (SpecialFinalizer*)sp;
+                               p = (byte*)((s->start << PageShift) + spf->offset);
+                               dumpfinalizer(p, spf->fn, spf->ft, spf->ot);
+                       }
+               }
+       }
+
+       // Finalizer queue
+       runtime_iterate_finq(finq_callback);
+}
+
+// Bit vector of free marks.
+// Needs to be as big as the largest number of objects per span.
+static byte hfree[PageSize/8];
+
+static void
+dumpobjs(void)
+{
+       uintptr i, j, size, n, off, shift, *bitp, bits, ti, kind;
+       MSpan *s;
+       MLink *l;
+       byte *p;
+       const Type *t;
+
+       for(i = 0; i < runtime_mheap.nspan; i++) {
+               s = runtime_mheap.allspans[i];
+               if(s->state != MSpanInUse)
+                       continue;
+               p = (byte*)(s->start << PageShift);
+               size = s->elemsize;
+               n = (s->npages << PageShift) / size;
+               if(n > PageSize/8)
+                       runtime_throw("free array doesn't have enough entries");
+               for(l = s->freelist; l != nil; l = l->next) {
+                       hfree[((byte*)l - p) / size] = true;
+               }
+               for(j = 0; j < n; j++, p += size) {
+                       if(hfree[j]) {
+                               hfree[j] = false;
+                               continue;
+                       }
+                       off = (uintptr*)p - (uintptr*)runtime_mheap.arena_start;
+                       bitp = (uintptr*)runtime_mheap.arena_start - off/wordsPerBitmapWord - 1;
+                       shift = off % wordsPerBitmapWord;
+                       bits = *bitp >> shift;
+
+                       // Skip FlagNoGC allocations (stacks)
+                       if((bits & bitAllocated) == 0)
+                               continue;
+
+                       // extract type and kind
+                       ti = runtime_gettype(p);
+                       t = (Type*)(ti & ~(uintptr)(PtrSize-1));
+                       kind = ti & (PtrSize-1);
+                       
+                       // dump it
+                       if(kind == TypeInfo_Chan)
+                               t = ((const ChanType*)t)->__element_type; // use element type for chan encoding
+                       if(t == nil && scannable(p))
+                               kind = TypeInfo_Conservative; // special kind for conservatively scanned objects
+                       dumpobj(p, size, t, kind);
+               }
+       }
+}
+
+static void
+dumpparams(void)
+{
+       byte *x;
+
+       dumpint(TagParams);
+       x = (byte*)1;
+       if(*(byte*)&x == 1)
+               dumpbool(false); // little-endian ptrs
+       else
+               dumpbool(true); // big-endian ptrs
+       dumpint(PtrSize);
+       dumpint(runtime_Hchansize);
+       dumpint((uintptr)runtime_mheap.arena_start);
+       dumpint((uintptr)runtime_mheap.arena_used);
+       dumpint(0);
+       dumpcstr((const int8 *)"");
+       dumpint(runtime_ncpu);
+}
+
+static void
+dumpms(void)
+{
+       M *mp;
+
+       for(mp = runtime_allm; mp != nil; mp = mp->alllink) {
+               dumpint(TagOSThread);
+               dumpint((uintptr)mp);
+               dumpint(mp->id);
+               dumpint(0);
+       }
+}
+
+static void
+dumpmemstats(void)
+{
+       int32 i;
+
+       dumpint(TagMemStats);
+       dumpint(mstats.alloc);
+       dumpint(mstats.total_alloc);
+       dumpint(mstats.sys);
+       dumpint(mstats.nlookup);
+       dumpint(mstats.nmalloc);
+       dumpint(mstats.nfree);
+       dumpint(mstats.heap_alloc);
+       dumpint(mstats.heap_sys);
+       dumpint(mstats.heap_idle);
+       dumpint(mstats.heap_inuse);
+       dumpint(mstats.heap_released);
+       dumpint(mstats.heap_objects);
+       dumpint(mstats.stacks_inuse);
+       dumpint(mstats.stacks_sys);
+       dumpint(mstats.mspan_inuse);
+       dumpint(mstats.mspan_sys);
+       dumpint(mstats.mcache_inuse);
+       dumpint(mstats.mcache_sys);
+       dumpint(mstats.buckhash_sys);
+       dumpint(mstats.gc_sys);
+       dumpint(mstats.other_sys);
+       dumpint(mstats.next_gc);
+       dumpint(mstats.last_gc);
+       dumpint(mstats.pause_total_ns);
+       for(i = 0; i < 256; i++)
+               dumpint(mstats.pause_ns[i]);
+       dumpint(mstats.numgc);
+}
+
+static void
+dumpmemprof_callback(Bucket *b, uintptr nstk, Location *stk, uintptr size, uintptr allocs, uintptr frees)
+{
+       uintptr i, pc;
+       byte buf[20];
+
+       dumpint(TagMemProf);
+       dumpint((uintptr)b);
+       dumpint(size);
+       dumpint(nstk);
+       for(i = 0; i < nstk; i++) {
+               pc = stk[i].pc;
+               if(stk[i].function.len == 0) {
+                       runtime_snprintf(buf, sizeof(buf), "%X", (uint64)pc);
+                       dumpcstr((int8*)buf);
+                       dumpcstr((const int8*)"?");
+                       dumpint(0);
+               } else {
+                       dumpstr(stk[i].function);
+                       dumpstr(stk[i].filename);
+                       dumpint(stk[i].lineno);
+               }
+       }
+       dumpint(allocs);
+       dumpint(frees);
+}
+
+static void
+dumpmemprof(void)
+{
+       MSpan *s, **allspans;
+       uint32 spanidx;
+       Special *sp;
+       SpecialProfile *spp;
+       byte *p;
+
+       runtime_iterate_memprof(dumpmemprof_callback);
+
+       allspans = runtime_mheap.allspans;
+       for(spanidx=0; spanidx<runtime_mheap.nspan; spanidx++) {
+               s = allspans[spanidx];
+               if(s->state != MSpanInUse)
+                       continue;
+               for(sp = s->specials; sp != nil; sp = sp->next) {
+                       if(sp->kind != KindSpecialProfile)
+                               continue;
+                       spp = (SpecialProfile*)sp;
+                       p = (byte*)((s->start << PageShift) + spp->offset);
+                       dumpint(TagAllocSample);
+                       dumpint((uintptr)p);
+                       dumpint((uintptr)spp->b);
+               }
+       }
+}
+
+static void
+mdump(G *gp)
+{
+       const byte *hdr;
+       uintptr i;
+       MSpan *s;
+
+       // make sure we're done sweeping
+       for(i = 0; i < runtime_mheap.nspan; i++) {
+               s = runtime_mheap.allspans[i];
+               if(s->state == MSpanInUse)
+                       runtime_MSpan_EnsureSwept(s);
+       }
+
+       runtime_memclr((byte*)&typecache[0], sizeof(typecache));
+       hdr = (const byte*)"go1.3 heap dump\n";
+       hwrite(hdr, runtime_findnull(hdr));
+       dumpparams();
+       dumpobjs();
+       dumpgs();
+       dumpms();
+       dumproots();
+       dumpmemstats();
+       dumpmemprof();
+       dumpint(TagEOF);
+       flush();
+
+       gp->param = nil;
+       gp->status = Grunning;
+       runtime_gogo(gp);
+}
+
+void runtime_debug_WriteHeapDump(uintptr)
+  __asm__(GOSYM_PREFIX "runtime_debug.WriteHeapDump");
+
+void
+runtime_debug_WriteHeapDump(uintptr fd)
+{
+       M *m;
+       G *g;
+
+       // Stop the world.
+       runtime_semacquire(&runtime_worldsema, false);
+       m = runtime_m();
+       m->gcing = 1;
+       m->locks++;
+       runtime_stoptheworld();
+
+       // Update stats so we can dump them.
+       // As a side effect, flushes all the MCaches so the MSpan.freelist
+       // lists contain all the free objects.
+       runtime_updatememstats(nil);
+
+       // Set dump file.
+       dumpfd = fd;
+
+       // Call dump routine on M stack.
+       g = runtime_g();
+       g->status = Gwaiting;
+       g->waitreason = "dumping heap";
+       runtime_mcall(mdump);
+
+       // Reset dump file.
+       dumpfd = 0;
+
+       // Start up the world again.
+       m->gcing = 0;
+       runtime_semrelease(&runtime_worldsema);
+       runtime_starttheworld();
+       m->locks--;
+}
+
+// Runs the specified gc program.  Calls the callback for every
+// pointer-like field specified by the program and passes to the
+// callback the kind and offset of that field within the object.
+// offset is the offset in the object of the start of the program.
+// Returns a pointer to the opcode that ended the gc program (either
+// GC_END or GC_ARRAY_NEXT).
+/*
+static uintptr*
+playgcprog(uintptr offset, uintptr *prog, void (*callback)(void*,uintptr,uintptr), void *arg)
+{
+       uintptr len, elemsize, i, *end;
+
+       for(;;) {
+               switch(prog[0]) {
+               case GC_END:
+                       return prog;
+               case GC_PTR:
+                       callback(arg, FieldKindPtr, offset + prog[1]);
+                       prog += 3;
+                       break;
+               case GC_APTR:
+                       callback(arg, FieldKindPtr, offset + prog[1]);
+                       prog += 2;
+                       break;
+               case GC_ARRAY_START:
+                       len = prog[2];
+                       elemsize = prog[3];
+                       end = nil;
+                       for(i = 0; i < len; i++) {
+                               end = playgcprog(offset + prog[1] + i * elemsize, prog + 4, callback, arg);
+                               if(end[0] != GC_ARRAY_NEXT)
+                                       runtime_throw("GC_ARRAY_START did not have matching GC_ARRAY_NEXT");
+                       }
+                       prog = end + 1;
+                       break;
+               case GC_ARRAY_NEXT:
+                       return prog;
+               case GC_CALL:
+                       playgcprog(offset + prog[1], (uintptr*)((byte*)prog + *(int32*)&prog[2]), callback, arg);
+                       prog += 3;
+                       break;
+               case GC_CHAN_PTR:
+                       callback(arg, FieldKindPtr, offset + prog[1]);
+                       prog += 3;
+                       break;
+               case GC_STRING:
+                       callback(arg, FieldKindString, offset + prog[1]);
+                       prog += 2;
+                       break;
+               case GC_EFACE:
+                       callback(arg, FieldKindEface, offset + prog[1]);
+                       prog += 2;
+                       break;
+               case GC_IFACE:
+                       callback(arg, FieldKindIface, offset + prog[1]);
+                       prog += 2;
+                       break;
+               case GC_SLICE:
+                       callback(arg, FieldKindSlice, offset + prog[1]);
+                       prog += 3;
+                       break;
+               case GC_REGION:
+                       playgcprog(offset + prog[1], (uintptr*)prog[3] + 1, callback, arg);
+                       prog += 4;
+                       break;
+               default:
+                       runtime_printf("%D\n", (uint64)prog[0]);
+                       runtime_throw("bad gc op");
+               }
+       }
+}
+
+static void
+dump_callback(void *p, uintptr kind, uintptr offset)
+{
+       USED(&p);
+       dumpint(kind);
+       dumpint(offset);
+}
+
+// dumpint() the kind & offset of each field in an object.
+static void
+dumpfields(uintptr *prog)
+{
+       playgcprog(0, prog, dump_callback, nil);
+       dumpint(FieldKindEol);
+}
+
+static void
+dumpeface_callback(void *p, uintptr kind, uintptr offset)
+{
+       Eface *e;
+
+       if(kind != FieldKindEface)
+               return;
+       e = (Eface*)((byte*)p + offset);
+       dumptype(e->__type_descriptor);
+}
+*/
+
+// The heap dump reader needs to be able to disambiguate
+// Eface entries.  So it needs to know every type that might
+// appear in such an entry.  The following two routines accomplish
+// that.
+
+// Dump all the types that appear in the type field of
+// any Eface contained in obj.
+static void
+dumpefacetypes(void *obj __attribute__ ((unused)), uintptr size, const Type *type, uintptr kind)
+{
+       uintptr i;
+
+       switch(kind) {
+       case TypeInfo_SingleObject:
+               //playgcprog(0, (uintptr*)type->gc + 1, dumpeface_callback, obj);
+               break;
+       case TypeInfo_Array:
+               for(i = 0; i <= size - type->__size; i += type->__size)
+                       //playgcprog(i, (uintptr*)type->gc + 1, dumpeface_callback, obj);
+               break;
+       case TypeInfo_Chan:
+               if(type->__size == 0) // channels may have zero-sized objects in them
+                       break;
+               for(i = runtime_Hchansize; i <= size - type->__size; i += type->__size)
+                       //playgcprog(i, (uintptr*)type->gc + 1, dumpeface_callback, obj);
+               break;
+       }
+}
diff --git a/libgo/runtime/lfstack.c b/libgo/runtime/lfstack.c
new file mode 100644 (file)
index 0000000..cefe7b8
--- /dev/null
@@ -0,0 +1,76 @@
+// AUTO-GENERATED by autogen.sh; DO NOT EDIT
+
+#include "runtime.h"
+#include "arch.h"
+#if __SIZEOF_POINTER__ == 8
+# define PTR_BITS 47
+#else
+# define PTR_BITS 32
+#endif
+#define PTR_MASK ((1ull<<PTR_BITS)-1)
+#define CNT_MASK (0ull-1)
+#if __SIZEOF_POINTER__ == 8 && (defined(__sparc__) || (defined(__sun__) && defined(__amd64__)))
+#undef PTR_BITS
+#undef CNT_MASK
+#undef PTR_MASK
+#define PTR_BITS 0
+#define CNT_MASK 7
+#define PTR_MASK ((0ull-1)<<3)
+#endif
+
+#line 33 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/lfstack.goc"
+void 
+runtime_lfstackpush ( uint64 *head , LFNode *node ) 
+{ 
+uint64 old , new; 
+#line 38 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/lfstack.goc"
+if ( ( uintptr ) node != ( ( uintptr ) node&PTR_MASK ) ) { 
+runtime_printf ( "p=%p\n" , node ) ; 
+runtime_throw ( "runtime_lfstackpush: invalid pointer" ) ; 
+} 
+#line 43 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/lfstack.goc"
+node->pushcnt++; 
+new = ( uint64 ) ( uintptr ) node| ( ( ( uint64 ) node->pushcnt&CNT_MASK ) <<PTR_BITS ) ; 
+for ( ;; ) { 
+old = runtime_atomicload64 ( head ) ; 
+node->next = ( LFNode* ) ( uintptr ) ( old&PTR_MASK ) ; 
+if ( runtime_cas64 ( head , old , new ) ) 
+break; 
+} 
+} 
+#line 53 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/lfstack.goc"
+LFNode* 
+runtime_lfstackpop ( uint64 *head ) 
+{ 
+LFNode *node , *node2; 
+uint64 old , new; 
+#line 59 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/lfstack.goc"
+for ( ;; ) { 
+old = runtime_atomicload64 ( head ) ; 
+if ( old == 0 ) 
+return nil; 
+node = ( LFNode* ) ( uintptr ) ( old&PTR_MASK ) ; 
+node2 = runtime_atomicloadp ( &node->next ) ; 
+new = 0; 
+if ( node2 != nil ) 
+new = ( uint64 ) ( uintptr ) node2| ( ( ( uint64 ) node2->pushcnt&CNT_MASK ) <<PTR_BITS ) ; 
+if ( runtime_cas64 ( head , old , new ) ) 
+return node; 
+} 
+} 
+void runtime_lfstackpush_go(uint64* head, LFNode* node) __asm__ (GOSYM_PREFIX "runtime.lfstackpush_go");
+void runtime_lfstackpush_go(uint64* head, LFNode* node)
+{
+#line 73 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/lfstack.goc"
+
+       runtime_lfstackpush(head, node);
+}
+LFNode* runtime_lfstackpop_go(uint64* head) __asm__ (GOSYM_PREFIX "runtime.lfstackpop_go");
+LFNode* runtime_lfstackpop_go(uint64* head)
+{
+  LFNode* node;
+#line 77 "../../../trunk/libgo/runtime/../../../trunk/libgo/runtime/lfstack.goc"
+
+       node = runtime_lfstackpop(head);
+return node;
+}
index d0d551de874ad0add64453e0d27e97404754284e..ef611fb36ad5b68bea63e2ec3e49631821cc85a3 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.
 
-// +build darwin netbsd openbsd plan9 solaris windows
+// +build darwin nacl netbsd openbsd plan9 solaris windows
 
 #include "runtime.h"
 
index 9c8b8c1c74cd7a2cf7525f10306ce0ea1455089c..028872259d944195a18b3dc952f5639339148ad8 100644 (file)
@@ -63,8 +63,9 @@ extern MStats mstats; // defined in zruntime_def_$GOOS_$GOARCH.go
 extern volatile intgo runtime_MemProfileRate
   __asm__ (GOSYM_PREFIX "runtime.MemProfileRate");
 
-static void* largealloc(uint32, uintptr*);
-static void profilealloc(void *v, uintptr size, uintptr typ);
+static MSpan* largealloc(uint32, uintptr*);
+static void profilealloc(void *v, uintptr size);
+static void settype(MSpan *s, void *v, uintptr typ);
 
 // Allocate an object of at least size bytes.
 // Small objects are allocated from the per-thread cache's free lists.
@@ -79,7 +80,7 @@ runtime_mallocgc(uintptr size, uintptr typ, uint32 flag)
        uintptr tinysize, size1;
        intgo rate;
        MCache *c;
-       MCacheList *l;
+       MSpan *s;
        MLink *v, *next;
        byte *tiny;
        bool incallback;
@@ -113,8 +114,8 @@ runtime_mallocgc(uintptr size, uintptr typ, uint32 flag)
        }
        if(m->mallocing)
                runtime_throw("malloc/free - deadlock");
-       // Disable preemption during settype_flush.
-       // We can not use m->mallocing for this, because settype_flush calls mallocgc.
+       // Disable preemption during settype.
+       // We can not use m->mallocing for this, because settype calls mallocgc.
        m->locks++;
        m->mallocing = 1;
 
@@ -178,15 +179,15 @@ runtime_mallocgc(uintptr size, uintptr typ, uint32 flag)
                                }
                        }
                        // Allocate a new TinySize block.
-                       l = &c->list[TinySizeClass];
-                       if(l->list == nil)
-                               runtime_MCache_Refill(c, TinySizeClass);
-                       v = l->list;
+                       s = c->alloc[TinySizeClass];
+                       if(s->freelist == nil)
+                               s = runtime_MCache_Refill(c, TinySizeClass);
+                       v = s->freelist;
                        next = v->next;
+                       s->freelist = next;
+                       s->ref++;
                        if(next != nil)  // prefetching nil leads to a DTLB miss
                                PREFETCH(next);
-                       l->list = next;
-                       l->nlist--;
                        ((uint64*)v)[0] = 0;
                        ((uint64*)v)[1] = 0;
                        // See if we need to replace the existing tiny block with the new one
@@ -205,15 +206,15 @@ runtime_mallocgc(uintptr size, uintptr typ, uint32 flag)
                else
                        sizeclass = runtime_size_to_class128[(size-1024+127) >> 7];
                size = runtime_class_to_size[sizeclass];
-               l = &c->list[sizeclass];
-               if(l->list == nil)
-                       runtime_MCache_Refill(c, sizeclass);
-               v = l->list;
+               s = c->alloc[sizeclass];
+               if(s->freelist == nil)
+                       s = runtime_MCache_Refill(c, sizeclass);
+               v = s->freelist;
                next = v->next;
+               s->freelist = next;
+               s->ref++;
                if(next != nil)  // prefetching nil leads to a DTLB miss
                        PREFETCH(next);
-               l->list = next;
-               l->nlist--;
                if(!(flag & FlagNoZero)) {
                        v->next = nil;
                        // block is zeroed iff second word is zero ...
@@ -224,7 +225,8 @@ runtime_mallocgc(uintptr size, uintptr typ, uint32 flag)
                c->local_cachealloc += size;
        } else {
                // Allocate directly from heap.
-               v = largealloc(flag, &size);
+               s = largealloc(flag, &size);
+               v = (void*)(s->start << PageShift);
        }
 
        if(flag & FlagNoGC)
@@ -235,34 +237,23 @@ runtime_mallocgc(uintptr size, uintptr typ, uint32 flag)
        if(DebugTypeAtBlockEnd)
                *(uintptr*)((uintptr)v+size-sizeof(uintptr)) = typ;
 
+       m->mallocing = 0;
        // TODO: save type even if FlagNoScan?  Potentially expensive but might help
        // heap profiling/tracing.
-       if(UseSpanType && !(flag & FlagNoScan) && typ != 0) {
-               uintptr *buf, i;
-
-               buf = m->settype_buf;
-               i = m->settype_bufsize;
-               buf[i++] = (uintptr)v;
-               buf[i++] = typ;
-               m->settype_bufsize = i;
-       }
+       if(UseSpanType && !(flag & FlagNoScan) && typ != 0)
+               settype(s, v, typ);
 
-       m->mallocing = 0;
-       if(UseSpanType && !(flag & FlagNoScan) && typ != 0 && m->settype_bufsize == nelem(m->settype_buf))
-               runtime_settype_flush(m);
        if(raceenabled)
                runtime_racemalloc(v, size);
 
        if(runtime_debug.allocfreetrace)
-               goto profile;
+               runtime_tracealloc(v, size, typ);
 
        if(!(flag & FlagNoProfiling) && (rate = runtime_MemProfileRate) > 0) {
                if(size < (uintptr)rate && size < (uintptr)(uint32)c->next_sample)
                        c->next_sample -= size;
-               else {
-               profile:
-                       profilealloc(v, size, typ);
-               }
+               else
+                       profilealloc(v, size);
        }
 
        m->locks--;
@@ -276,7 +267,7 @@ runtime_mallocgc(uintptr size, uintptr typ, uint32 flag)
        return v;
 }
 
-static void*
+static MSpan*
 largealloc(uint32 flag, uintptr *sizep)
 {
        uintptr npages, size;
@@ -298,11 +289,11 @@ largealloc(uint32 flag, uintptr *sizep)
        v = (void*)(s->start << PageShift);
        // setup for mark sweep
        runtime_markspan(v, 0, 0, true);
-       return v;
+       return s;
 }
 
 static void
-profilealloc(void *v, uintptr size, uintptr typ)
+profilealloc(void *v, uintptr size)
 {
        uintptr rate;
        int32 next;
@@ -324,7 +315,7 @@ profilealloc(void *v, uintptr size, uintptr typ)
                        next = 0;
                c->next_sample = next;
        }
-       runtime_MProf_Malloc(v, size, typ);
+       runtime_MProf_Malloc(v, size);
 }
 
 void*
@@ -365,8 +356,8 @@ __go_free(void *v)
        if(size < TinySize)
                runtime_throw("freeing too small block");
 
-       if(raceenabled)
-               runtime_racefree(v);
+       if(runtime_debug.allocfreetrace)
+               runtime_tracefree(v, size);
 
        // Ensure that the span is swept.
        // If we free into an unswept span, we will corrupt GC bitmaps.
@@ -381,10 +372,24 @@ __go_free(void *v)
                s->needzero = 1;
                // Must mark v freed before calling unmarkspan and MHeap_Free:
                // they might coalesce v into other spans and change the bitmap further.
-               runtime_markfreed(v, size);
+               runtime_markfreed(v);
                runtime_unmarkspan(v, 1<<PageShift);
+               // NOTE(rsc,dvyukov): The original implementation of efence
+               // in CL 22060046 used SysFree instead of SysFault, so that
+               // the operating system would eventually give the memory
+               // back to us again, so that an efence program could run
+               // longer without running out of memory. Unfortunately,
+               // calling SysFree here without any kind of adjustment of the
+               // heap data structures means that when the memory does
+               // come back to us, we have the wrong metadata for it, either in
+               // the MSpan structures or in the garbage collection bitmap.
+               // Using SysFault here means that the program will run out of
+               // memory fairly quickly in efence mode, but at least it won't
+               // have mysterious crashes due to confused memory reuse.
+               // It should be possible to switch back to SysFree if we also 
+               // implement and then call some kind of MHeap_DeleteSpan.
                if(runtime_debug.efence)
-                       runtime_SysFree((void*)(s->start<<PageShift), size, &mstats.heap_sys);
+                       runtime_SysFault((void*)(s->start<<PageShift), size);
                else
                        runtime_MHeap_Free(&runtime_mheap, s, 1);
                c->local_nlargefree++;
@@ -398,9 +403,18 @@ __go_free(void *v)
                // Must mark v freed before calling MCache_Free:
                // it might coalesce v and other blocks into a bigger span
                // and change the bitmap further.
-               runtime_markfreed(v, size);
                c->local_nsmallfree[sizeclass]++;
-               runtime_MCache_Free(c, v, sizeclass, size);
+               c->local_cachealloc -= size;
+               if(c->alloc[sizeclass] == s) {
+                       // We own the span, so we can just add v to the freelist
+                       runtime_markfreed(v);
+                       ((MLink*)v)->next = s->freelist;
+                       s->freelist = v;
+                       s->ref--;
+               } else {
+                       // Someone else owns this span.  Add to free queue.
+                       runtime_MCache_Free(c, v, sizeclass, size);
+               }
        }
        m->mallocing = 0;
 }
@@ -456,37 +470,6 @@ runtime_mlookup(void *v, byte **base, uintptr *size, MSpan **sp)
        return 1;
 }
 
-MCache*
-runtime_allocmcache(void)
-{
-       intgo rate;
-       MCache *c;
-
-       runtime_lock(&runtime_mheap);
-       c = runtime_FixAlloc_Alloc(&runtime_mheap.cachealloc);
-       runtime_unlock(&runtime_mheap);
-       runtime_memclr((byte*)c, sizeof(*c));
-
-       // Set first allocation sample size.
-       rate = runtime_MemProfileRate;
-       if(rate > 0x3fffffff)   // make 2*rate not overflow
-               rate = 0x3fffffff;
-       if(rate != 0)
-               c->next_sample = runtime_fastrand1() % (2*rate);
-
-       return c;
-}
-
-void
-runtime_freemcache(MCache *c)
-{
-       runtime_MCache_ReleaseAll(c);
-       runtime_lock(&runtime_mheap);
-       runtime_purgecachedstats(c);
-       runtime_FixAlloc_Free(&runtime_mheap.cachealloc, c);
-       runtime_unlock(&runtime_mheap);
-}
-
 void
 runtime_purgecachedstats(MCache *c)
 {
@@ -523,21 +506,25 @@ extern uintptr runtime_sizeof_C_MStats
 void
 runtime_mallocinit(void)
 {
-       byte *p;
-       uintptr arena_size, bitmap_size, spans_size;
+       byte *p, *p1;
+       uintptr arena_size, bitmap_size, spans_size, p_size;
        extern byte _end[];
        uintptr limit;
        uint64 i;
+       bool reserved;
 
        runtime_sizeof_C_MStats = sizeof(MStats) - (NumSizeClasses - 61) * sizeof(mstats.by_size[0]);
 
        p = nil;
+       p_size = 0;
        arena_size = 0;
        bitmap_size = 0;
        spans_size = 0;
+       reserved = false;
 
        // for 64-bit build
        USED(p);
+       USED(p_size);
        USED(arena_size);
        USED(bitmap_size);
        USED(spans_size);
@@ -585,7 +572,9 @@ runtime_mallocinit(void)
                spans_size = arena_size / PageSize * sizeof(runtime_mheap.spans[0]);
                spans_size = ROUND(spans_size, PageSize);
                for(i = 0; i < HeapBaseOptions; i++) {
-                       p = runtime_SysReserve(HeapBase(i), bitmap_size + spans_size + arena_size + PageSize);
+                       p = HeapBase(i);
+                       p_size = bitmap_size + spans_size + arena_size + PageSize;
+                       p = runtime_SysReserve(p, p_size, &reserved);
                        if(p != nil)
                                break;
                }
@@ -628,7 +617,8 @@ runtime_mallocinit(void)
                // away from the running binary image and then round up
                // to a MB boundary.
                p = (byte*)ROUND((uintptr)_end + (1<<18), 1<<20);
-               p = runtime_SysReserve(p, bitmap_size + spans_size + arena_size + PageSize);
+               p_size = bitmap_size + spans_size + arena_size + PageSize;
+               p = runtime_SysReserve(p, p_size, &reserved);
                if(p == nil)
                        runtime_throw("runtime: cannot reserve arena virtual address space");
        }
@@ -636,13 +626,17 @@ runtime_mallocinit(void)
        // PageSize can be larger than OS definition of page size,
        // so SysReserve can give us a PageSize-unaligned pointer.
        // To overcome this we ask for PageSize more and round up the pointer.
-       p = (byte*)ROUND((uintptr)p, PageSize);
+       p1 = (byte*)ROUND((uintptr)p, PageSize);
 
-       runtime_mheap.spans = (MSpan**)p;
-       runtime_mheap.bitmap = p + spans_size;
-       runtime_mheap.arena_start = p + spans_size + bitmap_size;
+       runtime_mheap.spans = (MSpan**)p1;
+       runtime_mheap.bitmap = p1 + spans_size;
+       runtime_mheap.arena_start = p1 + spans_size + bitmap_size;
        runtime_mheap.arena_used = runtime_mheap.arena_start;
-       runtime_mheap.arena_end = runtime_mheap.arena_start + arena_size;
+       runtime_mheap.arena_end = p + p_size;
+       runtime_mheap.arena_reserved = reserved;
+
+       if(((uintptr)runtime_mheap.arena_start & (PageSize-1)) != 0)
+               runtime_throw("misrounded allocation in mallocinit");
 
        // Initialize the rest of the allocator.        
        runtime_MHeap_Init(&runtime_mheap);
@@ -655,64 +649,87 @@ runtime_mallocinit(void)
 void*
 runtime_MHeap_SysAlloc(MHeap *h, uintptr n)
 {
-       byte *p;
+       byte *p, *p_end;
+       uintptr p_size;
+       bool reserved;
 
 
        if(n > (uintptr)(h->arena_end - h->arena_used)) {
                // We are in 32-bit mode, maybe we didn't use all possible address space yet.
                // Reserve some more space.
                byte *new_end;
-               uintptr needed;
 
-               needed = (uintptr)h->arena_used + n - (uintptr)h->arena_end;
-               needed = ROUND(needed, 256<<20);
-               new_end = h->arena_end + needed;
+               p_size = ROUND(n + PageSize, 256<<20);
+               new_end = h->arena_end + p_size;
                if(new_end <= h->arena_start + MaxArena32) {
-                       p = runtime_SysReserve(h->arena_end, new_end - h->arena_end);
-                       if(p == h->arena_end)
+                       // TODO: It would be bad if part of the arena
+                       // is reserved and part is not.
+                       p = runtime_SysReserve(h->arena_end, p_size, &reserved);
+                       if(p == h->arena_end) {
                                h->arena_end = new_end;
+                               h->arena_reserved = reserved;
+                       }
+                       else if(p+p_size <= h->arena_start + MaxArena32) {
+                               // Keep everything page-aligned.
+                               // Our pages are bigger than hardware pages.
+                               h->arena_end = p+p_size;
+                               h->arena_used = p + (-(uintptr)p&(PageSize-1));
+                               h->arena_reserved = reserved;
+                       } else {
+                               uint64 stat;
+                               stat = 0;
+                               runtime_SysFree(p, p_size, &stat);
+                       }
                }
        }
        if(n <= (uintptr)(h->arena_end - h->arena_used)) {
                // Keep taking from our reservation.
                p = h->arena_used;
-               runtime_SysMap(p, n, &mstats.heap_sys);
+               runtime_SysMap(p, n, h->arena_reserved, &mstats.heap_sys);
                h->arena_used += n;
                runtime_MHeap_MapBits(h);
                runtime_MHeap_MapSpans(h);
                if(raceenabled)
                        runtime_racemapshadow(p, n);
+               
+               if(((uintptr)p & (PageSize-1)) != 0)
+                       runtime_throw("misrounded allocation in MHeap_SysAlloc");
                return p;
        }
        
        // If using 64-bit, our reservation is all we have.
-       if(sizeof(void*) == 8 && (uintptr)h->bitmap >= 0xffffffffU)
+       if((uintptr)(h->arena_end - h->arena_start) >= MaxArena32)
                return nil;
 
        // On 32-bit, once the reservation is gone we can
        // try to get memory at a location chosen by the OS
        // and hope that it is in the range we allocated bitmap for.
-       p = runtime_SysAlloc(n, &mstats.heap_sys);
+       p_size = ROUND(n, PageSize) + PageSize;
+       p = runtime_SysAlloc(p_size, &mstats.heap_sys);
        if(p == nil)
                return nil;
 
-       if(p < h->arena_start || (uintptr)(p+n - h->arena_start) >= MaxArena32) {
+       if(p < h->arena_start || (uintptr)(p+p_size - h->arena_start) >= MaxArena32) {
                runtime_printf("runtime: memory allocated by OS (%p) not in usable range [%p,%p)\n",
                        p, h->arena_start, h->arena_start+MaxArena32);
-               runtime_SysFree(p, n, &mstats.heap_sys);
+               runtime_SysFree(p, p_size, &mstats.heap_sys);
                return nil;
        }
-
+       
+       p_end = p + p_size;
+       p += -(uintptr)p & (PageSize-1);
        if(p+n > h->arena_used) {
                h->arena_used = p+n;
-               if(h->arena_used > h->arena_end)
-                       h->arena_end = h->arena_used;
+               if(p_end > h->arena_end)
+                       h->arena_end = p_end;
                runtime_MHeap_MapBits(h);
                runtime_MHeap_MapSpans(h);
                if(raceenabled)
                        runtime_racemapshadow(p, n);
        }
        
+       if(((uintptr)p & (PageSize-1)) != 0)
+               runtime_throw("misrounded allocation in MHeap_SysAlloc");
        return p;
 }
 
@@ -740,7 +757,7 @@ runtime_persistentalloc(uintptr size, uintptr align, uint64 *stat)
 
        if(align != 0) {
                if(align&(align-1))
-                       runtime_throw("persistentalloc: align is now a power of 2");
+                       runtime_throw("persistentalloc: align is not a power of 2");
                if(align > PageSize)
                        runtime_throw("persistentalloc: align is too large");
        } else
@@ -768,94 +785,67 @@ runtime_persistentalloc(uintptr size, uintptr align, uint64 *stat)
        return p;
 }
 
-static Lock settype_lock;
-
-void
-runtime_settype_flush(M *mp)
+static void
+settype(MSpan *s, void *v, uintptr typ)
 {
-       uintptr *buf, *endbuf;
        uintptr size, ofs, j, t;
        uintptr ntypes, nbytes2, nbytes3;
        uintptr *data2;
        byte *data3;
-       void *v;
-       uintptr typ, p;
-       MSpan *s;
 
-       buf = mp->settype_buf;
-       endbuf = buf + mp->settype_bufsize;
-
-       runtime_lock(&settype_lock);
-       while(buf < endbuf) {
-               v = (void*)*buf;
-               *buf = 0;
-               buf++;
-               typ = *buf;
-               buf++;
-
-               // (Manually inlined copy of runtime_MHeap_Lookup)
-               p = (uintptr)v>>PageShift;
-               p -= (uintptr)runtime_mheap.arena_start >> PageShift;
-               s = runtime_mheap.spans[p];
-
-               if(s->sizeclass == 0) {
-                       s->types.compression = MTypes_Single;
-                       s->types.data = typ;
-                       continue;
+       if(s->sizeclass == 0) {
+               s->types.compression = MTypes_Single;
+               s->types.data = typ;
+               return;
+       }
+       size = s->elemsize;
+       ofs = ((uintptr)v - (s->start<<PageShift)) / size;
+
+       switch(s->types.compression) {
+       case MTypes_Empty:
+               ntypes = (s->npages << PageShift) / size;
+               nbytes3 = 8*sizeof(uintptr) + 1*ntypes;
+               data3 = runtime_mallocgc(nbytes3, 0, FlagNoProfiling|FlagNoScan|FlagNoInvokeGC);
+               s->types.compression = MTypes_Bytes;
+               s->types.data = (uintptr)data3;
+               ((uintptr*)data3)[1] = typ;
+               data3[8*sizeof(uintptr) + ofs] = 1;
+               break;
+               
+       case MTypes_Words:
+               ((uintptr*)s->types.data)[ofs] = typ;
+               break;
+               
+       case MTypes_Bytes:
+               data3 = (byte*)s->types.data;
+               for(j=1; j<8; j++) {
+                       if(((uintptr*)data3)[j] == typ) {
+                               break;
+                       }
+                       if(((uintptr*)data3)[j] == 0) {
+                               ((uintptr*)data3)[j] = typ;
+                               break;
+                       }
                }
-
-               size = s->elemsize;
-               ofs = ((uintptr)v - (s->start<<PageShift)) / size;
-
-               switch(s->types.compression) {
-               case MTypes_Empty:
+               if(j < 8) {
+                       data3[8*sizeof(uintptr) + ofs] = j;
+               } else {
                        ntypes = (s->npages << PageShift) / size;
-                       nbytes3 = 8*sizeof(uintptr) + 1*ntypes;
-                       data3 = runtime_mallocgc(nbytes3, 0, FlagNoProfiling|FlagNoScan|FlagNoInvokeGC);
-                       s->types.compression = MTypes_Bytes;
-                       s->types.data = (uintptr)data3;
-                       ((uintptr*)data3)[1] = typ;
-                       data3[8*sizeof(uintptr) + ofs] = 1;
-                       break;
-
-               case MTypes_Words:
-                       ((uintptr*)s->types.data)[ofs] = typ;
-                       break;
-
-               case MTypes_Bytes:
-                       data3 = (byte*)s->types.data;
-                       for(j=1; j<8; j++) {
-                               if(((uintptr*)data3)[j] == typ) {
-                                       break;
-                               }
-                               if(((uintptr*)data3)[j] == 0) {
-                                       ((uintptr*)data3)[j] = typ;
-                                       break;
-                               }
-                       }
-                       if(j < 8) {
-                               data3[8*sizeof(uintptr) + ofs] = j;
-                       } else {
-                               ntypes = (s->npages << PageShift) / size;
-                               nbytes2 = ntypes * sizeof(uintptr);
-                               data2 = runtime_mallocgc(nbytes2, 0, FlagNoProfiling|FlagNoScan|FlagNoInvokeGC);
-                               s->types.compression = MTypes_Words;
-                               s->types.data = (uintptr)data2;
-
-                               // Move the contents of data3 to data2. Then deallocate data3.
-                               for(j=0; j<ntypes; j++) {
-                                       t = data3[8*sizeof(uintptr) + j];
-                                       t = ((uintptr*)data3)[t];
-                                       data2[j] = t;
-                               }
-                               data2[ofs] = typ;
+                       nbytes2 = ntypes * sizeof(uintptr);
+                       data2 = runtime_mallocgc(nbytes2, 0, FlagNoProfiling|FlagNoScan|FlagNoInvokeGC);
+                       s->types.compression = MTypes_Words;
+                       s->types.data = (uintptr)data2;
+                       
+                       // Move the contents of data3 to data2. Then deallocate data3.
+                       for(j=0; j<ntypes; j++) {
+                               t = data3[8*sizeof(uintptr) + j];
+                               t = ((uintptr*)data3)[t];
+                               data2[j] = t;
                        }
-                       break;
+                       data2[ofs] = typ;
                }
+               break;
        }
-       runtime_unlock(&settype_lock);
-
-       mp->settype_bufsize = 0;
 }
 
 uintptr
@@ -888,9 +878,7 @@ runtime_gettype(void *v)
                        runtime_throw("runtime_gettype: invalid compression kind");
                }
                if(0) {
-                       runtime_lock(&settype_lock);
                        runtime_printf("%p -> %d,%X\n", v, (int32)s->types.compression, (int64)t);
-                       runtime_unlock(&settype_lock);
                }
                return t;
        }
@@ -933,7 +921,7 @@ runtime_cnewarray(const Type *typ, intgo n)
 }
 
 func GC() {
-       runtime_gc(1);
+       runtime_gc(2);  // force GC and do eager sweep
 }
 
 func SetFinalizer(obj Eface, finalizer Eface) {
@@ -956,15 +944,25 @@ func SetFinalizer(obj Eface, finalizer Eface) {
        // because we use &runtime_zerobase for all such allocations.
        if(ot->__element_type != nil && ot->__element_type->__size == 0)
                return;
+       // The following check is required for cases when a user passes a pointer to composite literal,
+       // but compiler makes it a pointer to global. For example:
+       //      var Foo = &Object{}
+       //      func main() {
+       //              runtime.SetFinalizer(Foo, nil)
+       //      }
+       // See issue 7656.
+       if((byte*)obj.__object < runtime_mheap.arena_start || runtime_mheap.arena_used <= (byte*)obj.__object)
+               return;
        if(!runtime_mlookup(obj.__object, &base, &size, nil) || obj.__object != base) {
                // As an implementation detail we allow to set finalizers for an inner byte
                // of an object if it could come from tiny alloc (see mallocgc for details).
-               if(ot->__element_type == nil || (ot->__element_type->__code&GO_NO_POINTERS) == 0 || ot->__element_type->__size >= TinySize) {
-                       runtime_printf("runtime.SetFinalizer: pointer not at beginning of allocated block\n");
+               if(ot->__element_type == nil || (ot->__element_type->__code&KindNoPointers) == 0 || ot->__element_type->__size >= TinySize) {
+                       runtime_printf("runtime.SetFinalizer: pointer not at beginning of allocated block (%p)\n", obj.__object);
                        goto throw;
                }
        }
        if(finalizer.__type_descriptor != nil) {
+               runtime_createfing();
                if(finalizer.__type_descriptor->__code != GO_FUNC)
                        goto badfunc;
                ft = (const FuncType*)finalizer.__type_descriptor;
index 30fbb64c27523d85cf9c2990012c903dbf95962d..86b9fccf9046f2b405fff3c2636079a6e5d09333 100644 (file)
@@ -20,7 +20,7 @@
 //     MHeap: the malloc heap, managed at page (4096-byte) granularity.
 //     MSpan: a run of pages managed by the MHeap.
 //     MCentral: a shared free list for a given size class.
-//     MCache: a per-thread (in Go, per-M) cache for small objects.
+//     MCache: a per-thread (in Go, per-P) cache for small objects.
 //     MStats: allocation statistics.
 //
 // Allocating a small object proceeds up a hierarchy of caches:
@@ -158,6 +158,9 @@ struct MLink
 // SysAlloc obtains a large chunk of zeroed memory from the
 // operating system, typically on the order of a hundred kilobytes
 // or a megabyte.
+// NOTE: SysAlloc returns OS-aligned memory, but the heap allocator
+// may use larger alignment, so the caller must be careful to realign the
+// memory obtained by SysAlloc.
 //
 // SysUnused notifies the operating system that the contents
 // of the memory region are no longer needed and can be reused
@@ -172,16 +175,29 @@ struct MLink
 // SysReserve reserves address space without allocating memory.
 // If the pointer passed to it is non-nil, the caller wants the
 // reservation there, but SysReserve can still choose another
-// location if that one is unavailable.
+// location if that one is unavailable.  On some systems and in some
+// cases SysReserve will simply check that the address space is
+// available and not actually reserve it.  If SysReserve returns
+// non-nil, it sets *reserved to true if the address space is
+// reserved, false if it has merely been checked.
+// NOTE: SysReserve returns OS-aligned memory, but the heap allocator
+// may use larger alignment, so the caller must be careful to realign the
+// memory obtained by SysAlloc.
 //
 // SysMap maps previously reserved address space for use.
+// The reserved argument is true if the address space was really
+// reserved, not merely checked.
+//
+// SysFault marks a (already SysAlloc'd) region to fault
+// if accessed.  Used only for debugging the runtime.
 
 void*  runtime_SysAlloc(uintptr nbytes, uint64 *stat);
 void   runtime_SysFree(void *v, uintptr nbytes, uint64 *stat);
 void   runtime_SysUnused(void *v, uintptr nbytes);
 void   runtime_SysUsed(void *v, uintptr nbytes);
-void   runtime_SysMap(void *v, uintptr nbytes, uint64 *stat);
-void*  runtime_SysReserve(void *v, uintptr nbytes);
+void   runtime_SysMap(void *v, uintptr nbytes, bool reserved, uint64 *stat);
+void*  runtime_SysReserve(void *v, uintptr nbytes, bool *reserved);
+void   runtime_SysFault(void *v, uintptr nbytes);
 
 // FixAlloc is a simple free-list allocator for fixed size objects.
 // Malloc uses a FixAlloc wrapped around SysAlloc to manages its
@@ -261,6 +277,7 @@ struct MStats
 
 extern MStats mstats
   __asm__ (GOSYM_PREFIX "runtime.memStats");
+void   runtime_updatememstats(GCStats *stats);
 
 // Size classes.  Computed and initialized by InitSizes.
 //
@@ -281,8 +298,6 @@ extern      int8    runtime_size_to_class128[(MaxSmallSize-1024)/128 + 1];
 extern void    runtime_InitSizes(void);
 
 
-// Per-thread (in Go, per-M) cache for small objects.
-// No locking needed because it is per-thread (per-M).
 typedef struct MCacheList MCacheList;
 struct MCacheList
 {
@@ -290,6 +305,8 @@ struct MCacheList
        uint32 nlist;
 };
 
+// Per-thread (in Go, per-P) cache for small objects.
+// No locking needed because it is per-thread (per-P).
 struct MCache
 {
        // The following members are accessed on every malloc,
@@ -301,7 +318,8 @@ struct MCache
        byte*   tiny;
        uintptr tinysize;
        // The rest is not accessed on every malloc.
-       MCacheList list[NumSizeClasses];
+       MSpan*  alloc[NumSizeClasses];  // spans to allocate from
+       MCacheList free[NumSizeClasses];// lists of explicitly freed objects
        // Local allocator stats, flushed during GC.
        uintptr local_nlookup;          // number of pointer lookups
        uintptr local_largefree;        // bytes freed for large objects (>MaxSmallSize)
@@ -309,8 +327,8 @@ struct MCache
        uintptr local_nsmallfree[NumSizeClasses];       // number of frees for small objects (<=MaxSmallSize)
 };
 
-void   runtime_MCache_Refill(MCache *c, int32 sizeclass);
-void   runtime_MCache_Free(MCache *c, void *p, int32 sizeclass, uintptr size);
+MSpan* runtime_MCache_Refill(MCache *c, int32 sizeclass);
+void   runtime_MCache_Free(MCache *c, MLink *p, int32 sizeclass, uintptr size);
 void   runtime_MCache_ReleaseAll(MCache *c);
 
 // MTypes describes the types of blocks allocated within a span.
@@ -408,8 +426,9 @@ struct MSpan
        // if sweepgen == h->sweepgen, the span is swept and ready to use
        // h->sweepgen is incremented by 2 after every GC
        uint32  sweepgen;
-       uint16  ref;            // number of allocated objects in this span
+       uint16  ref;            // capacity - number of objects in freelist
        uint8   sizeclass;      // size class
+       bool    incache;        // being used by an MCache
        uint8   state;          // MSpanInUse etc
        uint8   needzero;       // needs to be zeroed before allocation
        uintptr elemsize;       // computed from sizeclass or from npages
@@ -417,8 +436,9 @@ struct MSpan
        uintptr npreleased;     // number of pages released to the OS
        byte    *limit;         // end of data in span
        MTypes  types;          // types of allocated objects in this span
-       Lock    specialLock;    // TODO: use to protect types also (instead of settype_lock)
+       Lock    specialLock;    // guards specials list
        Special *specials;      // linked list of special records sorted by offset.
+       MLink   *freebuf;       // objects freed explicitly, not incorporated into freelist yet
 };
 
 void   runtime_MSpan_Init(MSpan *span, PageID start, uintptr npages);
@@ -440,15 +460,16 @@ struct MCentral
 {
        Lock;
        int32 sizeclass;
-       MSpan nonempty;
-       MSpan empty;
-       int32 nfree;
+       MSpan nonempty; // list of spans with a free object
+       MSpan empty;    // list of spans with no free objects (or cached in an MCache)
+       int32 nfree;    // # of objects available in nonempty spans
 };
 
 void   runtime_MCentral_Init(MCentral *c, int32 sizeclass);
-int32  runtime_MCentral_AllocList(MCentral *c, MLink **first);
-void   runtime_MCentral_FreeList(MCentral *c, MLink *first);
+MSpan* runtime_MCentral_CacheSpan(MCentral *c);
+void   runtime_MCentral_UncacheSpan(MCentral *c, MSpan *s);
 bool   runtime_MCentral_FreeSpan(MCentral *c, MSpan *s, int32 n, MLink *start, MLink *end);
+void   runtime_MCentral_FreeList(MCentral *c, MLink *start); // TODO: need this?
 
 // Main malloc heap.
 // The heap itself is the "free[]" and "large" arrays,
@@ -477,6 +498,7 @@ struct MHeap
        byte *arena_start;
        byte *arena_used;
        byte *arena_end;
+       bool arena_reserved;
 
        // central free lists for small size classes.
        // the padding makes sure that the MCentrals are
@@ -510,6 +532,7 @@ void*       runtime_MHeap_SysAlloc(MHeap *h, uintptr n);
 void   runtime_MHeap_MapBits(MHeap *h);
 void   runtime_MHeap_MapSpans(MHeap *h);
 void   runtime_MHeap_Scavenger(void*);
+void   runtime_MHeap_SplitSpan(MHeap *h, MSpan *s);
 
 void*  runtime_mallocgc(uintptr size, uintptr typ, uint32 flag);
 void*  runtime_persistentalloc(uintptr size, uintptr align, uint64 *stat);
@@ -519,7 +542,7 @@ uintptr     runtime_sweepone(void);
 void   runtime_markscan(void *v);
 void   runtime_marknogc(void *v);
 void   runtime_checkallocated(void *v, uintptr n);
-void   runtime_markfreed(void *v, uintptr n);
+void   runtime_markfreed(void *v);
 void   runtime_checkfreed(void *v, uintptr n);
 extern int32   runtime_checking;
 void   runtime_markspan(void *v, uintptr size, uintptr n, bool leftover);
@@ -527,9 +550,10 @@ void       runtime_unmarkspan(void *v, uintptr size);
 void   runtime_purgecachedstats(MCache*);
 void*  runtime_cnew(const Type*);
 void*  runtime_cnewarray(const Type*, intgo);
+void   runtime_tracealloc(void*, uintptr, uintptr);
+void   runtime_tracefree(void*, uintptr);
+void   runtime_tracegc(void);
 
-void   runtime_settype_flush(M*);
-void   runtime_settype_sysfree(MSpan*);
 uintptr        runtime_gettype(void*);
 
 enum
@@ -550,15 +574,17 @@ struct Obj
        uintptr ti;     // type info
 };
 
-void   runtime_MProf_Malloc(void*, uintptr, uintptr);
-void   runtime_MProf_Free(Bucket*, void*, uintptr, bool);
+void   runtime_MProf_Malloc(void*, uintptr);
+void   runtime_MProf_Free(Bucket*, uintptr, bool);
 void   runtime_MProf_GC(void);
-void   runtime_MProf_TraceGC(void);
-struct Workbuf;
-void   runtime_MProf_Mark(struct Workbuf**, void (*)(struct Workbuf**, Obj));
+void   runtime_iterate_memprof(void (*callback)(Bucket*, uintptr, Location*, uintptr, uintptr, uintptr));
 int32  runtime_gcprocs(void);
 void   runtime_helpgc(int32 nproc);
 void   runtime_gchelper(void);
+void   runtime_createfing(void);
+G*     runtime_wakefing(void);
+extern bool    runtime_fingwait;
+extern bool    runtime_fingwake;
 
 void   runtime_setprofilebucket(void *p, Bucket *b);
 
@@ -581,13 +607,52 @@ enum
        DebugTypeAtBlockEnd = 0,
 };
 
+// Information from the compiler about the layout of stack frames.
+typedef struct BitVector BitVector;
+struct BitVector
+{
+       int32 n; // # of bits
+       uint32 *data;
+};
+typedef struct StackMap StackMap;
+struct StackMap
+{
+       int32 n; // number of bitmaps
+       int32 nbit; // number of bits in each bitmap
+       uint32 data[];
+};
+enum {
+       // Pointer map
+       BitsPerPointer = 2,
+       BitsDead = 0,
+       BitsScalar = 1,
+       BitsPointer = 2,
+       BitsMultiWord = 3,
+       // BitsMultiWord will be set for the first word of a multi-word item.
+       // When it is set, one of the following will be set for the second word.
+       BitsString = 0,
+       BitsSlice = 1,
+       BitsIface = 2,
+       BitsEface = 3,
+};
+// Returns pointer map data for the given stackmap index
+// (the index is encoded in PCDATA_StackMapIndex).
+BitVector      runtime_stackmapdata(StackMap *stackmap, int32 n);
+
 // defined in mgc0.go
 void   runtime_gc_m_ptr(Eface*);
+void   runtime_gc_g_ptr(Eface*);
 void   runtime_gc_itab_ptr(Eface*);
 
 void   runtime_memorydump(void);
 int32  runtime_setgcpercent(int32);
 
+// Value we use to mark dead pointers when GODEBUG=gcdead=1.
+#define PoisonGC ((uintptr)0xf969696969696969ULL)
+#define PoisonStack ((uintptr)0x6868686868686868ULL)
+
+struct Workbuf;
+void   runtime_MProf_Mark(struct Workbuf**, void (*)(struct Workbuf**, Obj));
 void   runtime_proc_scan(struct Workbuf**, void (*)(struct Workbuf**, Obj));
 void   runtime_time_scan(struct Workbuf**, void (*)(struct Workbuf**, Obj));
 void   runtime_netpoll_scan(struct Workbuf**, void (*)(struct Workbuf**, Obj));
index 38f824a139b5c5297d5db967869112020d8f4c70..746711a0d3bdba2adcce4077a3512350a4cfa1a8 100644 (file)
 #include "arch.h"
 #include "malloc.h"
 
+extern volatile intgo runtime_MemProfileRate
+  __asm__ (GOSYM_PREFIX "runtime.MemProfileRate");
+
+// dummy MSpan that contains no free objects.
+static MSpan emptymspan;
+
+MCache*
+runtime_allocmcache(void)
+{
+       intgo rate;
+       MCache *c;
+       int32 i;
+
+       runtime_lock(&runtime_mheap);
+       c = runtime_FixAlloc_Alloc(&runtime_mheap.cachealloc);
+       runtime_unlock(&runtime_mheap);
+       runtime_memclr((byte*)c, sizeof(*c));
+       for(i = 0; i < NumSizeClasses; i++)
+               c->alloc[i] = &emptymspan;
+
+       // Set first allocation sample size.
+       rate = runtime_MemProfileRate;
+       if(rate > 0x3fffffff)   // make 2*rate not overflow
+               rate = 0x3fffffff;
+       if(rate != 0)
+               c->next_sample = runtime_fastrand1() % (2*rate);
+
+       return c;
+}
+
 void
+runtime_freemcache(MCache *c)
+{
+       runtime_MCache_ReleaseAll(c);
+       runtime_lock(&runtime_mheap);
+       runtime_purgecachedstats(c);
+       runtime_FixAlloc_Free(&runtime_mheap.cachealloc, c);
+       runtime_unlock(&runtime_mheap);
+}
+
+// Gets a span that has a free object in it and assigns it
+// to be the cached span for the given sizeclass.  Returns this span.
+MSpan*
 runtime_MCache_Refill(MCache *c, int32 sizeclass)
 {
        MCacheList *l;
+       MSpan *s;
 
-       // Replenish using central lists.
-       l = &c->list[sizeclass];
-       if(l->list)
-               runtime_throw("MCache_Refill: the list is not empty");
-       l->nlist = runtime_MCentral_AllocList(&runtime_mheap.central[sizeclass], &l->list);
-       if(l->list == nil)
-               runtime_throw("out of memory");
-}
+       runtime_m()->locks++;
+       // Return the current cached span to the central lists.
+       s = c->alloc[sizeclass];
+       if(s->freelist != nil)
+               runtime_throw("refill on a nonempty span");
+       if(s != &emptymspan)
+               runtime_MCentral_UncacheSpan(&runtime_mheap.central[sizeclass], s);
 
-// Take n elements off l and return them to the central free list.
-static void
-ReleaseN(MCacheList *l, int32 n, int32 sizeclass)
-{
-       MLink *first, **lp;
-       int32 i;
+       // Push any explicitly freed objects to the central lists.
+       // Not required, but it seems like a good time to do it.
+       l = &c->free[sizeclass];
+       if(l->nlist > 0) {
+               runtime_MCentral_FreeList(&runtime_mheap.central[sizeclass], l->list);
+               l->list = nil;
+               l->nlist = 0;
+       }
 
-       // Cut off first n elements.
-       first = l->list;
-       lp = &l->list;
-       for(i=0; i<n; i++)
-               lp = &(*lp)->next;
-       l->list = *lp;
-       *lp = nil;
-       l->nlist -= n;
-
-       // Return them to central free list.
-       runtime_MCentral_FreeList(&runtime_mheap.central[sizeclass], first);
+       // Get a new cached span from the central lists.
+       s = runtime_MCentral_CacheSpan(&runtime_mheap.central[sizeclass]);
+       if(s == nil)
+               runtime_throw("out of memory");
+       if(s->freelist == nil) {
+               runtime_printf("%d %d\n", s->ref, (int32)((s->npages << PageShift) / s->elemsize));
+               runtime_throw("empty span");
+       }
+       c->alloc[sizeclass] = s;
+       runtime_m()->locks--;
+       return s;
 }
 
 void
-runtime_MCache_Free(MCache *c, void *v, int32 sizeclass, uintptr size)
+runtime_MCache_Free(MCache *c, MLink *p, int32 sizeclass, uintptr size)
 {
        MCacheList *l;
-       MLink *p;
 
-       // Put back on list.
-       l = &c->list[sizeclass];
-       p = v;
+       // Put on free list.
+       l = &c->free[sizeclass];
        p->next = l->list;
        l->list = p;
        l->nlist++;
-       c->local_cachealloc -= size;
 
-       // We transfer span at a time from MCentral to MCache,
-       // if we have 2 times more than that, release a half back.
-       if(l->nlist >= 2*(runtime_class_to_allocnpages[sizeclass]<<PageShift)/size)
-               ReleaseN(l, l->nlist/2, sizeclass);
+       // We transfer a span at a time from MCentral to MCache,
+       // so we'll do the same in the other direction.
+       if(l->nlist >= (runtime_class_to_allocnpages[sizeclass]<<PageShift)/size) {
+               runtime_MCentral_FreeList(&runtime_mheap.central[sizeclass], l->list);
+               l->list = nil;
+               l->nlist = 0;
+       }
 }
 
 void
 runtime_MCache_ReleaseAll(MCache *c)
 {
        int32 i;
+       MSpan *s;
        MCacheList *l;
 
        for(i=0; i<NumSizeClasses; i++) {
-               l = &c->list[i];
-               if(l->list) {
+               s = c->alloc[i];
+               if(s != &emptymspan) {
+                       runtime_MCentral_UncacheSpan(&runtime_mheap.central[i], s);
+                       c->alloc[i] = &emptymspan;
+               }
+               l = &c->free[i];
+               if(l->nlist > 0) {
                        runtime_MCentral_FreeList(&runtime_mheap.central[i], l->list);
                        l->list = nil;
                        l->nlist = 0;
index 12853367c961806ff7634daa2af6196e4f3da848..e41a83fbf03383d9ad25caedbe63e239f321ec3a 100644 (file)
@@ -19,7 +19,8 @@
 #include "malloc.h"
 
 static bool MCentral_Grow(MCentral *c);
-static void MCentral_Free(MCentral *c, void *v);
+static void MCentral_Free(MCentral *c, MLink *v);
+static void MCentral_ReturnToHeap(MCentral *c, MSpan *s);
 
 // Initialize a single central free list.
 void
@@ -30,12 +31,9 @@ runtime_MCentral_Init(MCentral *c, int32 sizeclass)
        runtime_MSpanList_Init(&c->empty);
 }
 
-// Allocate a list of objects from the central free list.
-// Return the number of objects allocated.
-// The objects are linked together by their first words.
-// On return, *pfirst points at the first object.
-int32
-runtime_MCentral_AllocList(MCentral *c, MLink **pfirst)
+// Allocate a span to use in an MCache.
+MSpan*
+runtime_MCentral_CacheSpan(MCentral *c)
 {
        MSpan *s;
        int32 cap, n;
@@ -85,25 +83,63 @@ retry:
        // Replenish central list if empty.
        if(!MCentral_Grow(c)) {
                runtime_unlock(c);
-               *pfirst = nil;
-               return 0;
+               return nil;
        }
-       s = c->nonempty.next;
+       goto retry;
 
 havespan:
        cap = (s->npages << PageShift) / s->elemsize;
        n = cap - s->ref;
-       *pfirst = s->freelist;
-       s->freelist = nil;
-       s->ref += n;
+       if(n == 0)
+               runtime_throw("empty span");
+       if(s->freelist == nil)
+               runtime_throw("freelist empty");
        c->nfree -= n;
        runtime_MSpanList_Remove(s);
        runtime_MSpanList_InsertBack(&c->empty, s);
+       s->incache = true;
+       runtime_unlock(c);
+       return s;
+}
+
+// Return span from an MCache.
+void
+runtime_MCentral_UncacheSpan(MCentral *c, MSpan *s)
+{
+       MLink *v;
+       int32 cap, n;
+
+       runtime_lock(c);
+
+       s->incache = false;
+
+       // Move any explicitly freed items from the freebuf to the freelist.
+       while((v = s->freebuf) != nil) {
+               s->freebuf = v->next;
+               runtime_markfreed(v);
+               v->next = s->freelist;
+               s->freelist = v;
+               s->ref--;
+       }
+
+       if(s->ref == 0) {
+               // Free back to heap.  Unlikely, but possible.
+               MCentral_ReturnToHeap(c, s); // unlocks c
+               return;
+       }
+       
+       cap = (s->npages << PageShift) / s->elemsize;
+       n = cap - s->ref;
+       if(n > 0) {
+               c->nfree += n;
+               runtime_MSpanList_Remove(s);
+               runtime_MSpanList_Insert(&c->nonempty, s);
+       }
        runtime_unlock(c);
-       return n;
 }
 
-// Free the list of objects back into the central free list.
+// Free the list of objects back into the central free list c.
+// Called from runtime_free.
 void
 runtime_MCentral_FreeList(MCentral *c, MLink *start)
 {
@@ -118,52 +154,58 @@ runtime_MCentral_FreeList(MCentral *c, MLink *start)
 }
 
 // Helper: free one object back into the central free list.
+// Caller must hold lock on c on entry.  Holds lock on exit.
 static void
-MCentral_Free(MCentral *c, void *v)
+MCentral_Free(MCentral *c, MLink *v)
 {
        MSpan *s;
-       MLink *p;
-       int32 size;
 
        // Find span for v.
        s = runtime_MHeap_Lookup(&runtime_mheap, v);
        if(s == nil || s->ref == 0)
                runtime_throw("invalid free");
+       if(s->sweepgen != runtime_mheap.sweepgen)
+               runtime_throw("free into unswept span");
+       
+       // If the span is currently being used unsynchronized by an MCache,
+       // we can't modify the freelist.  Add to the freebuf instead.  The
+       // items will get moved to the freelist when the span is returned
+       // by the MCache.
+       if(s->incache) {
+               v->next = s->freebuf;
+               s->freebuf = v;
+               return;
+       }
 
-       // Move to nonempty if necessary.
+       // Move span to nonempty if necessary.
        if(s->freelist == nil) {
                runtime_MSpanList_Remove(s);
                runtime_MSpanList_Insert(&c->nonempty, s);
        }
 
-       // Add v back to s's free list.
-       p = v;
-       p->next = s->freelist;
-       s->freelist = p;
+       // Add the object to span's free list.
+       runtime_markfreed(v);
+       v->next = s->freelist;
+       s->freelist = v;
+       s->ref--;
        c->nfree++;
 
        // If s is completely freed, return it to the heap.
-       if(--s->ref == 0) {
-               size = runtime_class_to_size[c->sizeclass];
-               runtime_MSpanList_Remove(s);
-               runtime_unmarkspan((byte*)(s->start<<PageShift), s->npages<<PageShift);
-               s->needzero = 1;
-               s->freelist = nil;
-               c->nfree -= (s->npages << PageShift) / size;
-               runtime_unlock(c);
-               runtime_MHeap_Free(&runtime_mheap, s, 0);
+       if(s->ref == 0) {
+               MCentral_ReturnToHeap(c, s); // unlocks c
                runtime_lock(c);
        }
 }
 
 // Free n objects from a span s back into the central free list c.
 // Called during sweep.
-// Returns true if the span was returned to heap.
+// Returns true if the span was returned to heap.  Sets sweepgen to
+// the latest generation.
 bool
 runtime_MCentral_FreeSpan(MCentral *c, MSpan *s, int32 n, MLink *start, MLink *end)
 {
-       int32 size;
-
+       if(s->incache)
+               runtime_throw("freespan into cached span");
        runtime_lock(c);
 
        // Move to nonempty if necessary.
@@ -177,6 +219,12 @@ runtime_MCentral_FreeSpan(MCentral *c, MSpan *s, int32 n, MLink *start, MLink *e
        s->freelist = start;
        s->ref -= n;
        c->nfree += n;
+       
+       // delay updating sweepgen until here.  This is the signal that
+       // the span may be used in an MCache, so it must come after the
+       // linked list operations above (actually, just after the
+       // lock of c above.)
+       runtime_atomicstore(&s->sweepgen, runtime_mheap.sweepgen);
 
        if(s->ref != 0) {
                runtime_unlock(c);
@@ -184,14 +232,7 @@ runtime_MCentral_FreeSpan(MCentral *c, MSpan *s, int32 n, MLink *start, MLink *e
        }
 
        // s is completely freed, return it to the heap.
-       size = runtime_class_to_size[c->sizeclass];
-       runtime_MSpanList_Remove(s);
-       s->needzero = 1;
-       s->freelist = nil;
-       c->nfree -= (s->npages << PageShift) / size;
-       runtime_unlock(c);
-       runtime_unmarkspan((byte*)(s->start<<PageShift), s->npages<<PageShift);
-       runtime_MHeap_Free(&runtime_mheap, s, 0);
+       MCentral_ReturnToHeap(c, s); // unlocks c
        return true;
 }
 
@@ -246,3 +287,21 @@ MCentral_Grow(MCentral *c)
        runtime_MSpanList_Insert(&c->nonempty, s);
        return true;
 }
+
+// Return s to the heap.  s must be unused (s->ref == 0).  Unlocks c.
+static void
+MCentral_ReturnToHeap(MCentral *c, MSpan *s)
+{
+       int32 size;
+
+       size = runtime_class_to_size[c->sizeclass];
+       runtime_MSpanList_Remove(s);
+       s->needzero = 1;
+       s->freelist = nil;
+       if(s->ref != 0)
+               runtime_throw("ref wrong");
+       c->nfree -= (s->npages << PageShift) / size;
+       runtime_unlock(c);
+       runtime_unmarkspan((byte*)(s->start<<PageShift), s->npages<<PageShift);
+       runtime_MHeap_Free(&runtime_mheap, s, 0);
+}
index 78f7c51faf266345321ce236dae83502e643505b..8e374863b8e4509efc7d5f3d79e0efec2c020774 100644 (file)
 static int dev_zero = -1;
 #endif
 
-static _Bool
+static int32
 addrspace_free(void *v __attribute__ ((unused)), uintptr n __attribute__ ((unused)))
 {
 #ifdef HAVE_MINCORE
        size_t page_size = getpagesize();
-       size_t off;
-       char one_byte;
+       int32 errval;
+       uintptr chunk;
+       uintptr off;
+       
+       // NOTE: vec must be just 1 byte long here.
+       // Mincore returns ENOMEM if any of the pages are unmapped,
+       // but we want to know that all of the pages are unmapped.
+       // To make these the same, we can only ask about one page
+       // at a time. See golang.org/issue/7476.
+       static byte vec[1];
 
        errno = 0;
-       for(off = 0; off < n; off += page_size)
-               if(mincore((char *)v + off, page_size, (void *)&one_byte) != -1
-                  || errno != ENOMEM)
+       for(off = 0; off < n; off += chunk) {
+               chunk = page_size * sizeof vec;
+               if(chunk > (n - off))
+                       chunk = n - off;
+               errval = mincore((int8*)v + off, chunk, vec);
+               // ENOMEM means unmapped, which is what we want.
+               // Anything else we assume means the pages are mapped.
+               if(errval == 0 || errno != ENOMEM)
                        return 0;
+       }
 #endif
        return 1;
 }
@@ -115,8 +129,27 @@ runtime_SysFree(void *v, uintptr n, uint64 *stat)
        runtime_munmap(v, n);
 }
 
+void
+runtime_SysFault(void *v, uintptr n)
+{
+       int fd = -1;
+
+#ifdef USE_DEV_ZERO
+       if (dev_zero == -1) {
+               dev_zero = open("/dev/zero", O_RDONLY);
+               if (dev_zero < 0) {
+                       runtime_printf("open /dev/zero: errno=%d\n", errno);
+                       exit(2);
+               }
+       }
+       fd = dev_zero;
+#endif
+
+       runtime_mmap(v, n, PROT_NONE, MAP_ANON|MAP_PRIVATE|MAP_FIXED, fd, 0);
+}
+
 void*
-runtime_SysReserve(void *v, uintptr n)
+runtime_SysReserve(void *v, uintptr n, bool *reserved)
 {
        int fd = -1;
        void *p;
@@ -136,13 +169,14 @@ runtime_SysReserve(void *v, uintptr n)
        // much address space.  Instead, assume that the reservation is okay
        // if we can reserve at least 64K and check the assumption in SysMap.
        // Only user-mode Linux (UML) rejects these requests.
-       if(sizeof(void*) == 8 && (uintptr)v >= 0xffffffffU) {
+       if(sizeof(void*) == 8 && (n >> 16) > 1LLU<<16) {
                p = mmap_fixed(v, 64<<10, PROT_NONE, MAP_ANON|MAP_PRIVATE, fd, 0);
                if (p != v) {
                        runtime_munmap(p, 64<<10);
                        return nil;
                }
                runtime_munmap(p, 64<<10);
+               *reserved = false;
                return v;
        }
        
@@ -153,11 +187,12 @@ runtime_SysReserve(void *v, uintptr n)
        p = runtime_mmap(v, n, PROT_NONE, MAP_ANON|MAP_PRIVATE|MAP_NORESERVE, fd, 0);
        if(p == MAP_FAILED)
                return nil;
+       *reserved = true;
        return p;
 }
 
 void
-runtime_SysMap(void *v, uintptr n, uint64 *stat)
+runtime_SysMap(void *v, uintptr n, bool reserved, uint64 *stat)
 {
        void *p;
        int fd = -1;
@@ -176,7 +211,7 @@ runtime_SysMap(void *v, uintptr n, uint64 *stat)
 #endif
 
        // On 64-bit, we don't actually have v reserved, so tread carefully.
-       if(sizeof(void*) == 8 && (uintptr)v >= 0xffffffffU) {
+       if(!reserved) {
                p = mmap_fixed(v, n, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, fd, 0);
                if(p == MAP_FAILED && errno == ENOMEM)
                        runtime_throw("runtime: out of memory");
index e67c5b983fb9b6ace32858ce121df43892603bc3..4b78f3bda56a2d37651ce28a2dc03116dfb79285 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright 2009 The Go Authors. All rights reserved.
+
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
@@ -56,6 +56,7 @@
 #include "arch.h"
 #include "malloc.h"
 #include "mgc0.h"
+#include "chan.h"
 #include "race.h"
 #include "go-type.h"
 
@@ -87,16 +88,9 @@ extern void * __splitstack_find_context (void *context[10], size_t *, void **,
 enum {
        Debug = 0,
        CollectStats = 0,
-       ScanStackByFrames = 1,
-       IgnorePreciseGC = 0,
        ConcurrentSweep = 1,
 
-       // Four bits per word (see #defines below).
-       wordsPerBitmapWord = sizeof(void*)*8/4,
-       bitShift = sizeof(void*)*8/4,
-
        WorkbufSize     = 16*1024,
-       RootBlockSize   = 4*1024,
        FinBlockSize    = 4*1024,
 
        handoffThreshold = 4,
@@ -107,13 +101,6 @@ enum {
        LOOP = 2,
        PC_BITS = PRECISE | LOOP,
 
-       // Pointer map
-       BitsPerPointer = 2,
-       BitsNoPointer = 0,
-       BitsPointer = 1,
-       BitsIface = 2,
-       BitsEface = 3,
-
        RootData        = 0,
        RootBss         = 1,
        RootFinalizers  = 2,
@@ -127,44 +114,28 @@ enum {
 // Initialized from $GOGC.  GOGC=off means no gc.
 static int32 gcpercent = GcpercentUnknown;
 
-static struct
-{
-       Lock;  
-       void* head;
-} pools;
+static FuncVal* poolcleanup;
 
-void sync_runtime_registerPool(void **)
-  __asm__ (GOSYM_PREFIX "sync.runtime_registerPool");
+void sync_runtime_registerPoolCleanup(FuncVal*)
+  __asm__ (GOSYM_PREFIX "sync.runtime_registerPoolCleanup");
 
 void
-sync_runtime_registerPool(void **p)
+sync_runtime_registerPoolCleanup(FuncVal *f)
 {
-       runtime_lock(&pools);
-       p[0] = pools.head;
-       pools.head = p;
-       runtime_unlock(&pools);
+       poolcleanup = f;
 }
 
 static void
 clearpools(void)
 {
-       void **pool, **next;
        P *p, **pp;
        MCache *c;
-       uintptr off;
 
        // clear sync.Pool's
-       for(pool = pools.head; pool != nil; pool = next) {
-               next = pool[0];
-               pool[0] = nil; // next
-               pool[1] = nil; // local
-               pool[2] = nil; // localSize
-               off = (uintptr)pool[3] / sizeof(void*);
-               pool[off+0] = nil; // global slice
-               pool[off+1] = nil;
-               pool[off+2] = nil;
+       if(poolcleanup != nil) {
+               __go_set_closure(poolcleanup);
+               poolcleanup->fn();
        }
-       pools.head = nil;
 
        for(pp=runtime_allp; (p=*pp) != nil; pp++) {
                // clear tinyalloc pool
@@ -178,39 +149,6 @@ clearpools(void)
        }
 }
 
-// Bits in per-word bitmap.
-// #defines because enum might not be able to hold the values.
-//
-// Each word in the bitmap describes wordsPerBitmapWord words
-// of heap memory.  There are 4 bitmap bits dedicated to each heap word,
-// so on a 64-bit system there is one bitmap word per 16 heap words.
-// The bits in the word are packed together by type first, then by
-// heap location, so each 64-bit bitmap word consists of, from top to bottom,
-// the 16 bitSpecial bits for the corresponding heap words, then the 16 bitMarked bits,
-// then the 16 bitScan/bitBlockBoundary bits, then the 16 bitAllocated bits.
-// This layout makes it easier to iterate over the bits of a given type.
-//
-// The bitmap starts at mheap.arena_start and extends *backward* from
-// there.  On a 64-bit system the off'th word in the arena is tracked by
-// the off/16+1'th word before mheap.arena_start.  (On a 32-bit system,
-// the only difference is that the divisor is 8.)
-//
-// To pull out the bits corresponding to a given pointer p, we use:
-//
-//     off = p - (uintptr*)mheap.arena_start;  // word offset
-//     b = (uintptr*)mheap.arena_start - off/wordsPerBitmapWord - 1;
-//     shift = off % wordsPerBitmapWord
-//     bits = *b >> shift;
-//     /* then test bits & bitAllocated, bits & bitMarked, etc. */
-//
-#define bitAllocated           ((uintptr)1<<(bitShift*0))      /* block start; eligible for garbage collection */
-#define bitScan                        ((uintptr)1<<(bitShift*1))      /* when bitAllocated is set */
-#define bitMarked              ((uintptr)1<<(bitShift*2))      /* when bitAllocated is set */
-#define bitSpecial             ((uintptr)1<<(bitShift*3))      /* when bitAllocated is set - has finalizer or being profiled */
-#define bitBlockBoundary       ((uintptr)1<<(bitShift*1))      /* when bitAllocated is NOT set - mark for FlagNoGC objects */
-
-#define bitMask (bitAllocated | bitScan | bitMarked | bitSpecial)
-
 // Holding worldsema grants an M the right to try to stop the world.
 // The procedure is:
 //
@@ -256,12 +194,15 @@ struct FinBlock
        Finalizer fin[1];
 };
 
-static G       *fing;
-static FinBlock        *finq; // list of finalizers that are to be executed
-static FinBlock        *finc; // cache of free blocks
-static FinBlock        *allfin; // list of all blocks
-static int32   fingwait;
+static Lock    finlock;        // protects the following variables
+static FinBlock        *finq;          // list of finalizers that are to be executed
+static FinBlock        *finc;          // cache of free blocks
+static FinBlock        *allfin;        // list of all blocks
+bool   runtime_fingwait;
+bool   runtime_fingwake;
+
 static Lock    gclock;
+static G*      fing;
 
 static void    runfinq(void*);
 static void    bgsweep(void*);
@@ -331,7 +272,7 @@ static struct {
 // has been marked by this function, false otherwise.
 // This function doesn't append the object to any buffer.
 static bool
-markonly(void *obj)
+markonly(const void *obj)
 {
        byte *p;
        uintptr *bitp, bits, shift, x, xbits, off, j;
@@ -339,17 +280,17 @@ markonly(void *obj)
        PageID k;
 
        // Words outside the arena cannot be pointers.
-       if((byte*)obj < runtime_mheap.arena_start || (byte*)obj >= runtime_mheap.arena_used)
+       if((const byte*)obj < runtime_mheap.arena_start || (const byte*)obj >= runtime_mheap.arena_used)
                return false;
 
        // obj may be a pointer to a live object.
        // Try to find the beginning of the object.
 
        // Round down to word boundary.
-       obj = (void*)((uintptr)obj & ~((uintptr)PtrSize-1));
+       obj = (const void*)((uintptr)obj & ~((uintptr)PtrSize-1));
 
        // Find bits for this word.
-       off = (uintptr*)obj - (uintptr*)runtime_mheap.arena_start;
+       off = (const uintptr*)obj - (uintptr*)runtime_mheap.arena_start;
        bitp = (uintptr*)runtime_mheap.arena_start - off/wordsPerBitmapWord - 1;
        shift = off % wordsPerBitmapWord;
        xbits = *bitp;
@@ -380,19 +321,19 @@ markonly(void *obj)
        x = k;
        x -= (uintptr)runtime_mheap.arena_start>>PageShift;
        s = runtime_mheap.spans[x];
-       if(s == nil || k < s->start || (byte*)obj >= s->limit || s->state != MSpanInUse)
+       if(s == nil || k < s->start || (const byte*)obj >= s->limit || s->state != MSpanInUse)
                return false;
        p = (byte*)((uintptr)s->start<<PageShift);
        if(s->sizeclass == 0) {
                obj = p;
        } else {
                uintptr size = s->elemsize;
-               int32 i = ((byte*)obj - p)/size;
+               int32 i = ((const byte*)obj - p)/size;
                obj = p+i*size;
        }
 
        // Now that we know the object header, reload bits.
-       off = (uintptr*)obj - (uintptr*)runtime_mheap.arena_start;
+       off = (const uintptr*)obj - (uintptr*)runtime_mheap.arena_start;
        bitp = (uintptr*)runtime_mheap.arena_start - off/wordsPerBitmapWord - 1;
        shift = off % wordsPerBitmapWord;
        xbits = *bitp;
@@ -768,8 +709,8 @@ checkptr(void *obj, uintptr objti)
                // A simple best-effort check until first GC_END.
                for(j = 1; pc1[j] != GC_END && pc2[j] != GC_END; j++) {
                        if(pc1[j] != pc2[j]) {
-                               runtime_printf("invalid gc type info for '%s' at %p, type info %p, block info %p\n",
-                                              t->string ? (const int8*)t->string->str : (const int8*)"?", j, pc1[j], pc2[j]);
+                               runtime_printf("invalid gc type info for '%s', type info %p [%d]=%p, block info %p [%d]=%p\n",
+                                              t->string ? (const int8*)t->string->str : (const int8*)"?", pc1, (int32)j, pc1[j], pc2, (int32)j, pc2[j]);
                                runtime_throw("invalid gc type info");
                        }
                }
@@ -793,8 +734,9 @@ scanblock(Workbuf *wbuf, bool keepworking)
        uintptr *chan_ret, chancap;
 #endif
        void *obj;
-       const Type *t;
+       const Type *t, *et;
        Slice *sliceptr;
+       String *stringptr;
        Frame *stack_ptr, stack_top, stack[GC_STACK_CAPACITY+4];
        BufferList *scanbuffers;
        Scanbuf sbuf;
@@ -851,9 +793,6 @@ scanblock(Workbuf *wbuf, bool keepworking)
        for(;;) {
                // Each iteration scans the block b of length n, queueing pointers in
                // the work buffer.
-               if(Debug > 1) {
-                       runtime_printf("scanblock %p %D\n", b, (int64)n);
-               }
 
                if(CollectStats) {
                        runtime_xadd64(&gcstats.nbytes, n);
@@ -862,6 +801,9 @@ scanblock(Workbuf *wbuf, bool keepworking)
                }
 
                if(ti != 0 && false) {
+                       if(Debug > 1) {
+                               runtime_printf("scanblock %p %D ti %p\n", b, (int64)n, ti);
+                       }
                        pc = (uintptr*)(ti & ~(uintptr)PC_BITS);
                        precise_type = (ti & PRECISE);
                        stack_top.elemsize = pc[0];
@@ -918,15 +860,23 @@ scanblock(Workbuf *wbuf, bool keepworking)
                                        pc = chanProg;
                                        break;
                                default:
+                                       if(Debug > 1)
+                                               runtime_printf("scanblock %p %D type %p %S\n", b, (int64)n, type, *t->string);
                                        runtime_throw("scanblock: invalid type");
                                        return;
                                }
+                               if(Debug > 1)
+                                       runtime_printf("scanblock %p %D type %p %S pc=%p\n", b, (int64)n, type, *t->string, pc);
                        } else {
                                pc = defaultProg;
+                               if(Debug > 1)
+                                       runtime_printf("scanblock %p %D unknown type\n", b, (int64)n);
                        }
 #endif
                } else {
                        pc = defaultProg;
+                       if(Debug > 1)
+                               runtime_printf("scanblock %p %D no span types\n", b, (int64)n);
                }
 
                if(IgnorePreciseGC)
@@ -934,7 +884,6 @@ scanblock(Workbuf *wbuf, bool keepworking)
 
                pc++;
                stack_top.b = (uintptr)b;
-
                end_b = (uintptr)b + n - PtrSize;
 
        for(;;) {
@@ -947,6 +896,8 @@ scanblock(Workbuf *wbuf, bool keepworking)
                case GC_PTR:
                        obj = *(void**)(stack_top.b + pc[1]);
                        objti = pc[2];
+                       if(Debug > 2)
+                               runtime_printf("gc_ptr @%p: %p ti=%p\n", stack_top.b+pc[1], obj, objti);
                        pc += 3;
                        if(Debug)
                                checkptr(obj, objti);
@@ -954,6 +905,8 @@ scanblock(Workbuf *wbuf, bool keepworking)
 
                case GC_SLICE:
                        sliceptr = (Slice*)(stack_top.b + pc[1]);
+                       if(Debug > 2)
+                               runtime_printf("gc_slice @%p: %p/%D/%D\n", sliceptr, sliceptr->array, (int64)sliceptr->__count, (int64)sliceptr->cap);
                        if(sliceptr->cap != 0) {
                                obj = sliceptr->array;
                                // Can't use slice element type for scanning,
@@ -967,18 +920,25 @@ scanblock(Workbuf *wbuf, bool keepworking)
 
                case GC_APTR:
                        obj = *(void**)(stack_top.b + pc[1]);
+                       if(Debug > 2)
+                               runtime_printf("gc_aptr @%p: %p\n", stack_top.b+pc[1], obj);
                        pc += 2;
                        break;
 
                case GC_STRING:
-                       obj = *(void**)(stack_top.b + pc[1]);
-                       markonly(obj);
+                       stringptr = (String*)(stack_top.b + pc[1]);
+                       if(Debug > 2)
+                               runtime_printf("gc_string @%p: %p/%D\n", stack_top.b+pc[1], stringptr->str, (int64)stringptr->len);
+                       if(stringptr->len != 0)
+                               markonly(stringptr->str);
                        pc += 2;
                        continue;
 
                case GC_EFACE:
                        eface = (Eface*)(stack_top.b + pc[1]);
                        pc += 2;
+                       if(Debug > 2)
+                               runtime_printf("gc_eface @%p: %p %p\n", stack_top.b+pc[1], eface->__type_descriptor, eface->__object);
                        if(eface->__type_descriptor == nil)
                                continue;
 
@@ -999,9 +959,15 @@ scanblock(Workbuf *wbuf, bool keepworking)
                                                continue;
 
                                        obj = eface->__object;
-                                       if((t->__code & ~KindNoPointers) == KindPtr)
-                                               // objti = (uintptr)((PtrType*)t)->elem->gc;
-                                               objti = 0;
+                                       if((t->__code & ~KindNoPointers) == KindPtr) {
+                                               // Only use type information if it is a pointer-containing type.
+                                               // This matches the GC programs written by cmd/gc/reflect.c's
+                                               // dgcsym1 in case TPTR32/case TPTR64. See rationale there.
+                                               et = ((const PtrType*)t)->elem;
+                                               if(!(et->__code & KindNoPointers))
+                                                       // objti = (uintptr)((const PtrType*)t)->elem->gc;
+                                                       objti = 0;
+                                       }
                                } else {
                                        obj = eface->__object;
                                        // objti = (uintptr)t->gc;
@@ -1013,6 +979,8 @@ scanblock(Workbuf *wbuf, bool keepworking)
                case GC_IFACE:
                        iface = (Iface*)(stack_top.b + pc[1]);
                        pc += 2;
+                       if(Debug > 2)
+                               runtime_printf("gc_iface @%p: %p/%p %p\n", stack_top.b+pc[1], iface->__methods[0], nil, iface->__object);
                        if(iface->tab == nil)
                                continue;
                        
@@ -1032,9 +1000,15 @@ scanblock(Workbuf *wbuf, bool keepworking)
                                                continue;
 
                                        obj = iface->__object;
-                                       if((t->__code & ~KindNoPointers) == KindPtr)
-                                               // objti = (uintptr)((const PtrType*)t)->elem->gc;
-                                               objti = 0;
+                                       if((t->__code & ~KindNoPointers) == KindPtr) {
+                                               // Only use type information if it is a pointer-containing type.
+                                               // This matches the GC programs written by cmd/gc/reflect.c's
+                                               // dgcsym1 in case TPTR32/case TPTR64. See rationale there.
+                                               et = ((const PtrType*)t)->elem;
+                                               if(!(et->__code & KindNoPointers))
+                                                       // objti = (uintptr)((const PtrType*)t)->elem->gc;
+                                                       objti = 0;
+                                       }
                                } else {
                                        obj = iface->__object;
                                        // objti = (uintptr)t->gc;
@@ -1046,6 +1020,8 @@ scanblock(Workbuf *wbuf, bool keepworking)
                case GC_DEFAULT_PTR:
                        while(stack_top.b <= end_b) {
                                obj = *(byte**)stack_top.b;
+                               if(Debug > 2)
+                                       runtime_printf("gc_default_ptr @%p: %p\n", stack_top.b, obj);
                                stack_top.b += PtrSize;
                                if((byte*)obj >= arena_start && (byte*)obj < arena_used) {
                                        *sbuf.ptr.pos++ = (PtrTarget){obj, 0};
@@ -1125,6 +1101,8 @@ scanblock(Workbuf *wbuf, bool keepworking)
                        objti = pc[3];
                        pc += 4;
 
+                       if(Debug > 2)
+                               runtime_printf("gc_region @%p: %D %p\n", stack_top.b+pc[1], (int64)size, objti);
                        *sbuf.obj.pos++ = (Obj){obj, size, objti};
                        if(sbuf.obj.pos == sbuf.obj.end)
                                flushobjbuf(&sbuf);
@@ -1133,6 +1111,8 @@ scanblock(Workbuf *wbuf, bool keepworking)
 #if 0
                case GC_CHAN_PTR:
                        chan = *(Hchan**)(stack_top.b + pc[1]);
+                       if(Debug > 2 && chan != nil)
+                               runtime_printf("gc_chan_ptr @%p: %p/%D/%D %p\n", stack_top.b+pc[1], chan, (int64)chan->qcount, (int64)chan->dataqsiz, pc[2]);
                        if(chan == nil) {
                                pc += 3;
                                continue;
@@ -1308,6 +1288,7 @@ markroot(ParFor *desc, uint32 i)
 
        USED(&desc);
        wbuf = getempty(nil);
+       // Note: if you add a case here, please also update heapdump.c:dumproots.
        switch(i) {
        case RootData:
                // For gccgo this is both data and bss.
@@ -1597,7 +1578,7 @@ runtime_queuefinalizer(void *p, FuncVal *fn, const FuncType *ft, const PtrType *
        FinBlock *block;
        Finalizer *f;
 
-       runtime_lock(&gclock);
+       runtime_lock(&finlock);
        if(finq == nil || finq->cnt == finq->cap) {
                if(finc == nil) {
                        finc = runtime_persistentalloc(FinBlockSize, 0, &mstats.gc_sys);
@@ -1616,25 +1597,45 @@ runtime_queuefinalizer(void *p, FuncVal *fn, const FuncType *ft, const PtrType *
        f->ft = ft;
        f->ot = ot;
        f->arg = p;
-       runtime_unlock(&gclock);
+       runtime_fingwake = true;
+       runtime_unlock(&finlock);
+}
+
+void
+runtime_iterate_finq(void (*callback)(FuncVal*, void*, const FuncType*, const PtrType*))
+{
+       FinBlock *fb;
+       Finalizer *f;
+       int32 i;
+
+       for(fb = allfin; fb; fb = fb->alllink) {
+               for(i = 0; i < fb->cnt; i++) {
+                       f = &fb->fin[i];
+                       callback(f->fn, f->arg, f->ft, f->ot);
+               }
+       }
 }
 
 void
 runtime_MSpan_EnsureSwept(MSpan *s)
 {
        M *m = runtime_m();
+       G *g = runtime_g();
        uint32 sg;
 
+       // Caller must disable preemption.
+       // Otherwise when this function returns the span can become unswept again
+       // (if GC is triggered on another goroutine).
+       if(m->locks == 0 && m->mallocing == 0 && g != m->g0)
+               runtime_throw("MSpan_EnsureSwept: m is not locked");
+
        sg = runtime_mheap.sweepgen;
        if(runtime_atomicload(&s->sweepgen) == sg)
                return;
-       m->locks++;
        if(runtime_cas(&s->sweepgen, sg-2, sg-1)) {
                runtime_MSpan_Sweep(s);
-               m->locks--;
                return;
        }
-       m->locks--;
        // unfortunate condition, and we don't have efficient means to wait
        while(runtime_atomicload(&s->sweepgen) != sg)
                runtime_osyield();  
@@ -1699,7 +1700,7 @@ runtime_MSpan_Sweep(MSpan *s)
                shift = off % wordsPerBitmapWord;
                *bitp |= bitMarked<<shift;
        }
-       
+
        // Unlink & free special records for any objects we're about to free.
        specialp = &s->specials;
        special = *specialp;
@@ -1757,8 +1758,11 @@ runtime_MSpan_Sweep(MSpan *s)
                        continue;
                }
 
-               // Clear mark, scan, and special bits.
-               *bitp &= ~((bitScan|bitMarked|bitSpecial)<<shift);
+               if(runtime_debug.allocfreetrace)
+                       runtime_tracefree(p, size);
+
+               // Clear mark and scan bits.
+               *bitp &= ~((bitScan|bitMarked)<<shift);
 
                if(cl == 0) {
                        // Free large span.
@@ -1767,8 +1771,9 @@ runtime_MSpan_Sweep(MSpan *s)
                        // important to set sweepgen before returning it to heap
                        runtime_atomicstore(&s->sweepgen, sweepgen);
                        sweepgenset = true;
+                       // See note about SysFault vs SysFree in malloc.goc.
                        if(runtime_debug.efence)
-                               runtime_SysFree(p, size, &mstats.gc_sys);
+                               runtime_SysFault(p, size);
                        else
                                runtime_MHeap_Free(&runtime_mheap, s, 1);
                        c->local_nlargefree++;
@@ -1796,7 +1801,13 @@ runtime_MSpan_Sweep(MSpan *s)
                }
        }
 
-       if(!sweepgenset) {
+       // We need to set s->sweepgen = h->sweepgen only when all blocks are swept,
+       // because of the potential for a concurrent free/SetFinalizer.
+       // But we need to set it before we make the span available for allocation
+       // (return it to heap or mcentral), because allocation code assumes that a
+       // span is already swept if available for allocation.
+
+       if(!sweepgenset && nfree == 0) {
                // The span must be in our exclusive ownership until we update sweepgen,
                // check for potential races.
                if(s->state != MSpanInUse || s->sweepgen != sweepgen-1) {
@@ -1806,11 +1817,12 @@ runtime_MSpan_Sweep(MSpan *s)
                }
                runtime_atomicstore(&s->sweepgen, sweepgen);
        }
-       if(nfree) {
+       if(nfree > 0) {
                c->local_nsmallfree[cl] += nfree;
                c->local_cachealloc -= nfree * size;
                runtime_xadd64(&mstats.next_gc, -(uint64)(nfree * size * (gcpercent + 100)/100));
                res = runtime_MCentral_FreeSpan(&runtime_mheap.central[cl], s, nfree, head.next, end);
+               //MCentral_FreeSpan updates sweepgen
        }
        return res;
 }
@@ -1838,17 +1850,16 @@ bgsweep(void* dummy __attribute__ ((unused)))
                        runtime_gosched();
                }
                runtime_lock(&gclock);
-               if(finq != nil) {
-                       // kick off or wake up goroutine to run queued finalizers
-                       if(fing == nil)
-                               fing = __go_go(runfinq, nil);
-                       else if(fingwait) {
-                               fingwait = 0;
-                               runtime_ready(fing);
-                       }
+               if(!runtime_mheap.sweepdone) {
+                       // It's possible if GC has happened between sweepone has
+                       // returned -1 and gclock lock.
+                       runtime_unlock(&gclock);
+                       continue;
                }
                sweep.parked = true;
+               runtime_g()->isbackground = true;
                runtime_parkunlock(&gclock, "GC sweep wait");
+               runtime_g()->isbackground = false;
        }
 }
 
@@ -1880,6 +1891,8 @@ runtime_sweepone(void)
                }
                if(s->sweepgen != sg-2 || !runtime_cas(&s->sweepgen, sg-2, sg-1))
                        continue;
+               if(s->incache)
+                       runtime_throw("sweep of incache span");
                npages = s->npages;
                if(!runtime_MSpan_Sweep(s))
                        npages = 0;
@@ -1896,7 +1909,7 @@ dumpspan(uint32 idx)
        byte *p;
        byte *arena_start;
        MSpan *s;
-       bool allocated, special;
+       bool allocated;
 
        s = runtime_mheap.allspans[idx];
        if(s->state != MSpanInUse)
@@ -1923,7 +1936,6 @@ dumpspan(uint32 idx)
                bits = *bitp>>shift;
 
                allocated = ((bits & bitAllocated) != 0);
-               special = ((bits & bitSpecial) != 0);
 
                for(i=0; (uint32)i<size; i+=sizeof(void*)) {
                        if(column == 0) {
@@ -1931,7 +1943,6 @@ dumpspan(uint32 idx)
                        }
                        if(i == 0) {
                                runtime_printf(allocated ? "(" : "[");
-                               runtime_printf(special ? "@" : "");
                                runtime_printf("%p: ", p+i);
                        } else {
                                runtime_printf(" ");
@@ -1969,6 +1980,7 @@ runtime_gchelper(void)
 {
        uint32 nproc;
 
+       runtime_m()->traceback = 2;
        gchelperstart();
 
        // parallel mark for over gc roots
@@ -1981,6 +1993,7 @@ runtime_gchelper(void)
        nproc = work.nproc;  // work.nproc can change right after we increment work.ndone
        if(runtime_xadd(&work.ndone, +1) == nproc-1)
                runtime_notewakeup(&work.alldone);
+       runtime_m()->traceback = 0;
 }
 
 static void
@@ -2012,8 +2025,8 @@ flushallmcaches(void)
        }
 }
 
-static void
-updatememstats(GCStats *stats)
+void
+runtime_updatememstats(GCStats *stats)
 {
        M *mp;
        MSpan *s;
@@ -2099,6 +2112,7 @@ updatememstats(GCStats *stats)
 struct gc_args
 {
        int64 start_time; // start time of GC in ns (just before stoptheworld)
+       bool  eagersweep;
 };
 
 static void gc(struct gc_args *args);
@@ -2117,6 +2131,8 @@ readgogc(void)
        return runtime_atoi(p);
 }
 
+// force = 1 - do GC regardless of current heap usage
+// force = 2 - go GC and eager sweep
 void
 runtime_gc(int32 force)
 {
@@ -2159,7 +2175,7 @@ runtime_gc(int32 force)
                return;
 
        runtime_semacquire(&runtime_worldsema, false);
-       if(!force && mstats.heap_alloc < mstats.next_gc) {
+       if(force==0 && mstats.heap_alloc < mstats.next_gc) {
                // typically threads which lost the race to grab
                // worldsema exit here when gc is done.
                runtime_semrelease(&runtime_worldsema);
@@ -2168,12 +2184,10 @@ runtime_gc(int32 force)
 
        // Ok, we're doing it!  Stop everybody else
        a.start_time = runtime_nanotime();
+       a.eagersweep = force >= 2;
        m->gcing = 1;
        runtime_stoptheworld();
        
-       if(runtime_debug.allocfreetrace)
-               runtime_MProf_TraceGC();
-
        clearpools();
 
        // Run gc on the g0 stack.  We do this so that the g stack
@@ -2182,14 +2196,14 @@ runtime_gc(int32 force)
        // we don't need to scan gc's internal state).  Also an
        // enabler for copyable stacks.
        for(i = 0; i < (runtime_debug.gctrace > 1 ? 2 : 1); i++) {
+               if(i > 0)
+                       a.start_time = runtime_nanotime();
                // switch to g0, call gc(&a), then switch back
                g = runtime_g();
                g->param = &a;
                g->status = Gwaiting;
                g->waitreason = "garbage collection";
                runtime_mcall(mgc);
-               // record a new start time in case we're going around again
-               a.start_time = runtime_nanotime();
        }
 
        // all done
@@ -2201,17 +2215,6 @@ runtime_gc(int32 force)
 
        // now that gc is done, kick off finalizer thread if needed
        if(!ConcurrentSweep) {
-               if(finq != nil) {
-                       runtime_lock(&gclock);
-                       // kick off or wake up goroutine to run queued finalizers
-                       if(fing == nil)
-                               fing = __go_go(runfinq, nil);
-                       else if(fingwait) {
-                               fingwait = 0;
-                               runtime_ready(fing);
-                       }
-                       runtime_unlock(&gclock);
-               }
                // give the queued finalizers, if any, a chance to run
                runtime_gosched();
        } else {
@@ -2236,21 +2239,21 @@ gc(struct gc_args *args)
        int64 t0, t1, t2, t3, t4;
        uint64 heap0, heap1, obj, ninstr;
        GCStats stats;
-       M *mp;
        uint32 i;
        // Eface eface;
 
        m = runtime_m();
 
+       if(runtime_debug.allocfreetrace)
+               runtime_tracegc();
+
+       m->traceback = 2;
        t0 = args->start_time;
        work.tstart = args->start_time; 
 
        if(CollectStats)
                runtime_memclr((byte*)&gcstats, sizeof(gcstats));
 
-       for(mp=runtime_allm; mp; mp=mp->alllink)
-               runtime_settype_flush(mp);
-
        m->locks++;     // disable gc during mallocs in parforalloc
        if(work.markfor == nil)
                work.markfor = runtime_parforalloc(MaxGcproc);
@@ -2262,7 +2265,9 @@ gc(struct gc_args *args)
                // itabtype = ((PtrType*)eface.__type_descriptor)->elem;
        }
 
-       t1 = runtime_nanotime();
+       t1 = 0;
+       if(runtime_debug.gctrace)
+               t1 = runtime_nanotime();
 
        // Sweep what is not sweeped by bgsweep.
        while(runtime_sweepone() != (uintptr)-1)
@@ -2277,13 +2282,17 @@ gc(struct gc_args *args)
                runtime_helpgc(work.nproc);
        }
 
-       t2 = runtime_nanotime();
+       t2 = 0;
+       if(runtime_debug.gctrace)
+               t2 = runtime_nanotime();
 
        gchelperstart();
        runtime_parfordo(work.markfor);
        scanblock(nil, true);
 
-       t3 = runtime_nanotime();
+       t3 = 0;
+       if(runtime_debug.gctrace)
+               t3 = runtime_nanotime();
 
        bufferList[m->helpgc].busy = 0;
        if(work.nproc > 1)
@@ -2298,7 +2307,7 @@ gc(struct gc_args *args)
        mstats.next_gc = mstats.heap_alloc+mstats.heap_alloc*gcpercent/100;
 
        t4 = runtime_nanotime();
-       mstats.last_gc = t4;
+       mstats.last_gc = runtime_unixnanotime();  // must be Unix time to make sense to user
        mstats.pause_ns[mstats.numgc%nelem(mstats.pause_ns)] = t4 - t0;
        mstats.pause_total_ns += t4 - t0;
        mstats.numgc++;
@@ -2306,18 +2315,22 @@ gc(struct gc_args *args)
                runtime_printf("pause %D\n", t4-t0);
 
        if(runtime_debug.gctrace) {
-               updatememstats(&stats);
                heap1 = mstats.heap_alloc;
+               runtime_updatememstats(&stats);
+               if(heap1 != mstats.heap_alloc) {
+                       runtime_printf("runtime: mstats skew: heap=%D/%D\n", heap1, mstats.heap_alloc);
+                       runtime_throw("mstats skew");
+               }
                obj = mstats.nmalloc - mstats.nfree;
 
                stats.nprocyield += work.markfor->nprocyield;
                stats.nosyield += work.markfor->nosyield;
                stats.nsleep += work.markfor->nsleep;
 
-               runtime_printf("gc%d(%d): %D+%D+%D ms, %D -> %D MB, %D (%D-%D) objects,"
+               runtime_printf("gc%d(%d): %D+%D+%D+%D us, %D -> %D MB, %D (%D-%D) objects,"
                                " %d/%d/%d sweeps,"
                                " %D(%D) handoff, %D(%D) steal, %D/%D/%D yields\n",
-                       mstats.numgc, work.nproc, (t3-t2)/1000000, (t2-t1)/1000000, (t1-t0+t4-t3)/1000000,
+                       mstats.numgc, work.nproc, (t1-t0)/1000, (t2-t1)/1000, (t3-t2)/1000, (t4-t3)/1000,
                        heap0>>20, heap1>>20, obj,
                        mstats.nmalloc, mstats.nfree,
                        sweep.nspan, gcstats.nbgsweep, gcstats.npausesweep,
@@ -2368,7 +2381,7 @@ gc(struct gc_args *args)
        sweep.spanidx = 0;
 
        // Temporary disable concurrent sweep, because we see failures on builders.
-       if(ConcurrentSweep) {
+       if(ConcurrentSweep && !args->eagersweep) {
                runtime_lock(&gclock);
                if(sweep.g == nil)
                        sweep.g = __go_go(bgsweep, nil);
@@ -2384,6 +2397,7 @@ gc(struct gc_args *args)
        }
 
        runtime_MProf_GC();
+       m->traceback = 0;
 }
 
 extern uintptr runtime_sizeof_C_MStats
@@ -2405,7 +2419,7 @@ runtime_ReadMemStats(MStats *stats)
        m = runtime_m();
        m->gcing = 1;
        runtime_stoptheworld();
-       updatememstats(nil);
+       runtime_updatememstats(nil);
        // Size of the trailing by_size array differs between Go and C,
        // NumSizeClasses was changed, but we can not change Go struct because of backward compatibility.
        runtime_memmove(stats, &mstats, runtime_sizeof_C_MStats);
@@ -2488,16 +2502,35 @@ runfinq(void* dummy __attribute__ ((unused)))
        Eface ef;
        Iface iface;
 
+       // This function blocks for long periods of time, and because it is written in C
+       // we have no liveness information. Zero everything so that uninitialized pointers
+       // do not cause memory leaks.
+       f = nil;
+       fb = nil;
+       next = nil;
+       i = 0;
+       ef.__type_descriptor = nil;
+       ef.__object = nil;
+       
+       // force flush to memory
+       USED(&f);
+       USED(&fb);
+       USED(&next);
+       USED(&i);
+       USED(&ef);
+
        for(;;) {
-               runtime_lock(&gclock);
+               runtime_lock(&finlock);
                fb = finq;
                finq = nil;
                if(fb == nil) {
-                       fingwait = 1;
-                       runtime_parkunlock(&gclock, "finalizer wait");
+                       runtime_fingwait = true;
+                       runtime_g()->isbackground = true;
+                       runtime_parkunlock(&finlock, "finalizer wait");
+                       runtime_g()->isbackground = false;
                        continue;
                }
-               runtime_unlock(&gclock);
+               runtime_unlock(&finlock);
                if(raceenabled)
                        runtime_racefingo();
                for(; fb; fb=next) {
@@ -2532,94 +2565,92 @@ runfinq(void* dummy __attribute__ ((unused)))
                                f->ot = nil;
                        }
                        fb->cnt = 0;
+                       runtime_lock(&finlock);
                        fb->next = finc;
                        finc = fb;
+                       runtime_unlock(&finlock);
                }
+
+               // Zero everything that's dead, to avoid memory leaks.
+               // See comment at top of function.
+               f = nil;
+               fb = nil;
+               next = nil;
+               i = 0;
+               ef.__type_descriptor = nil;
+               ef.__object = nil;
                runtime_gc(1);  // trigger another gc to clean up the finalized objects, if possible
        }
 }
 
+void
+runtime_createfing(void)
+{
+       if(fing != nil)
+               return;
+       // Here we use gclock instead of finlock,
+       // because newproc1 can allocate, which can cause on-demand span sweep,
+       // which can queue finalizers, which would deadlock.
+       runtime_lock(&gclock);
+       if(fing == nil)
+               fing = __go_go(runfinq, nil);
+       runtime_unlock(&gclock);
+}
+
+G*
+runtime_wakefing(void)
+{
+       G *res;
+
+       res = nil;
+       runtime_lock(&finlock);
+       if(runtime_fingwait && runtime_fingwake) {
+               runtime_fingwait = false;
+               runtime_fingwake = false;
+               res = fing;
+       }
+       runtime_unlock(&finlock);
+       return res;
+}
+
 void
 runtime_marknogc(void *v)
 {
-       uintptr *b, obits, bits, off, shift;
+       uintptr *b, off, shift;
 
        off = (uintptr*)v - (uintptr*)runtime_mheap.arena_start;  // word offset
        b = (uintptr*)runtime_mheap.arena_start - off/wordsPerBitmapWord - 1;
        shift = off % wordsPerBitmapWord;
-
-       for(;;) {
-               obits = *b;
-               if((obits>>shift & bitMask) != bitAllocated)
-                       runtime_throw("bad initial state for marknogc");
-               bits = (obits & ~(bitAllocated<<shift)) | bitBlockBoundary<<shift;
-               if(runtime_gomaxprocs == 1) {
-                       *b = bits;
-                       break;
-               } else {
-                       // more than one goroutine is potentially running: use atomic op
-                       if(runtime_casp((void**)b, (void*)obits, (void*)bits))
-                               break;
-               }
-       }
+       *b = (*b & ~(bitAllocated<<shift)) | bitBlockBoundary<<shift;
 }
 
 void
 runtime_markscan(void *v)
 {
-       uintptr *b, obits, bits, off, shift;
+       uintptr *b, off, shift;
 
        off = (uintptr*)v - (uintptr*)runtime_mheap.arena_start;  // word offset
        b = (uintptr*)runtime_mheap.arena_start - off/wordsPerBitmapWord - 1;
        shift = off % wordsPerBitmapWord;
-
-       for(;;) {
-               obits = *b;
-               if((obits>>shift & bitMask) != bitAllocated)
-                       runtime_throw("bad initial state for markscan");
-               bits = obits | bitScan<<shift;
-               if(runtime_gomaxprocs == 1) {
-                       *b = bits;
-                       break;
-               } else {
-                       // more than one goroutine is potentially running: use atomic op
-                       if(runtime_casp((void**)b, (void*)obits, (void*)bits))
-                               break;
-               }
-       }
+       *b |= bitScan<<shift;
 }
 
-// mark the block at v of size n as freed.
+// mark the block at v as freed.
 void
-runtime_markfreed(void *v, uintptr n)
+runtime_markfreed(void *v)
 {
-       uintptr *b, obits, bits, off, shift;
+       uintptr *b, off, shift;
 
        if(0)
-               runtime_printf("markfreed %p+%p\n", v, n);
+               runtime_printf("markfreed %p\n", v);
 
-       if((byte*)v+n > (byte*)runtime_mheap.arena_used || (byte*)v < runtime_mheap.arena_start)
+       if((byte*)v > (byte*)runtime_mheap.arena_used || (byte*)v < runtime_mheap.arena_start)
                runtime_throw("markfreed: bad pointer");
 
        off = (uintptr*)v - (uintptr*)runtime_mheap.arena_start;  // word offset
        b = (uintptr*)runtime_mheap.arena_start - off/wordsPerBitmapWord - 1;
        shift = off % wordsPerBitmapWord;
-
-       for(;;) {
-               obits = *b;
-               // This could be a free of a gc-eligible object (bitAllocated + others) or
-               // a FlagNoGC object (bitBlockBoundary set).  In either case, we revert to
-               // a simple no-scan allocated object because it is going on a free list.
-               bits = (obits & ~(bitMask<<shift)) | (bitAllocated<<shift);
-               if(runtime_gomaxprocs == 1) {
-                       *b = bits;
-                       break;
-               } else {
-                       // more than one goroutine is potentially running: use atomic op
-                       if(runtime_casp((void**)b, (void*)obits, (void*)bits))
-                               break;
-               }
-       }
+       *b = (*b & ~(bitMask<<shift)) | (bitAllocated<<shift);
 }
 
 // check that the block at v of size n is marked freed.
@@ -2651,7 +2682,7 @@ runtime_checkfreed(void *v, uintptr n)
 void
 runtime_markspan(void *v, uintptr size, uintptr n, bool leftover)
 {
-       uintptr *b, off, shift, i;
+       uintptr *b, *b0, off, shift, i, x;
        byte *p;
 
        if((byte*)v+size*n > (byte*)runtime_mheap.arena_used || (byte*)v < runtime_mheap.arena_start)
@@ -2670,6 +2701,9 @@ runtime_markspan(void *v, uintptr size, uintptr n, bool leftover)
        p = v;
        if(leftover)    // mark a boundary just past end of last block too
                n++;
+
+       b0 = nil;
+       x = 0;
        for(; n-- > 0; p += size) {
                // Okay to use non-atomic ops here, because we control
                // the entire span, and each bitmap word has bits for only
@@ -2678,8 +2712,15 @@ runtime_markspan(void *v, uintptr size, uintptr n, bool leftover)
                off = (uintptr*)p - (uintptr*)runtime_mheap.arena_start;  // word offset
                b = (uintptr*)runtime_mheap.arena_start - off/wordsPerBitmapWord - 1;
                shift = off % wordsPerBitmapWord;
-               *b = (*b & ~(bitMask<<shift)) | (bitAllocated<<shift);
+               if(b0 != b) {
+                       if(b0 != nil)
+                               *b0 = x;
+                       b0 = b;
+                       x = 0;
+               }
+               x |= bitAllocated<<shift;
        }
+       *b0 = x;
 }
 
 // unmark the span of memory at v of length n bytes.
@@ -2723,12 +2764,12 @@ runtime_MHeap_MapBits(MHeap *h)
 
        n = (h->arena_used - h->arena_start) / wordsPerBitmapWord;
        n = ROUND(n, bitmapChunk);
+       n = ROUND(n, PageSize);
+       page_size = getpagesize();
+       n = ROUND(n, page_size);
        if(h->bitmap_mapped >= n)
                return;
 
-       page_size = getpagesize();
-       n = (n+page_size-1) & ~(page_size-1);
-
-       runtime_SysMap(h->arena_start - n, n - h->bitmap_mapped, &mstats.gc_sys);
+       runtime_SysMap(h->arena_start - n, n - h->bitmap_mapped, h->arena_reserved, &mstats.gc_sys);
        h->bitmap_mapped = n;
 }
index f8abe6c9c1c235ff71952c8c56ab330f11329617..16000d1ae2ea8b7ce613a55455e376d460bff270 100644 (file)
@@ -44,3 +44,44 @@ enum {
        //  - at most GC_STACK_CAPACITY allocations because of GC_ARRAY_START
        GC_STACK_CAPACITY = 8,  
 };
+
+enum {
+       ScanStackByFrames = 1,
+       IgnorePreciseGC = 0,
+
+       // Four bits per word (see #defines below).
+       wordsPerBitmapWord = sizeof(void*)*8/4,
+       bitShift = sizeof(void*)*8/4,
+};
+
+// Bits in per-word bitmap.
+// #defines because enum might not be able to hold the values.
+//
+// Each word in the bitmap describes wordsPerBitmapWord words
+// of heap memory.  There are 4 bitmap bits dedicated to each heap word,
+// so on a 64-bit system there is one bitmap word per 16 heap words.
+// The bits in the word are packed together by type first, then by
+// heap location, so each 64-bit bitmap word consists of, from top to bottom,
+// the 16 bitMarked bits for the corresponding heap words,
+// then the 16 bitScan/bitBlockBoundary bits, then the 16 bitAllocated bits.
+// This layout makes it easier to iterate over the bits of a given type.
+//
+// The bitmap starts at mheap.arena_start and extends *backward* from
+// there.  On a 64-bit system the off'th word in the arena is tracked by
+// the off/16+1'th word before mheap.arena_start.  (On a 32-bit system,
+// the only difference is that the divisor is 8.)
+//
+// To pull out the bits corresponding to a given pointer p, we use:
+//
+//     off = p - (uintptr*)mheap.arena_start;  // word offset
+//     b = (uintptr*)mheap.arena_start - off/wordsPerBitmapWord - 1;
+//     shift = off % wordsPerBitmapWord
+//     bits = *b >> shift;
+//     /* then test bits & bitAllocated, bits & bitMarked, etc. */
+//
+#define bitAllocated           ((uintptr)1<<(bitShift*0))      /* block start; eligible for garbage collection */
+#define bitScan                        ((uintptr)1<<(bitShift*1))      /* when bitAllocated is set */
+#define bitMarked              ((uintptr)1<<(bitShift*2))      /* when bitAllocated is set */
+#define bitBlockBoundary       ((uintptr)1<<(bitShift*1))      /* when bitAllocated is NOT set - mark for FlagNoGC objects */
+
+#define bitMask (bitAllocated | bitScan | bitMarked)
index 3a5eb15a596e7789bd4e8842b942db3e680394e5..793915ef44c6682b17a537ed479854bf3512c089 100644 (file)
@@ -88,7 +88,7 @@ runtime_MHeap_MapSpans(MHeap *h)
        n = ROUND(n, pagesize);
        if(h->spans_mapped >= n)
                return;
-       runtime_SysMap((byte*)h->spans + h->spans_mapped, n - h->spans_mapped, &mstats.other_sys);
+       runtime_SysMap((byte*)h->spans + h->spans_mapped, n - h->spans_mapped, h->arena_reserved, &mstats.other_sys);
        h->spans_mapped = n;
 }
 
@@ -522,6 +522,7 @@ runtime_MHeap_Scavenger(void* dummy)
        G *g;
        MHeap *h;
        uint64 tick, now, forcegc, limit;
+       int64 unixnow;
        uint32 k;
        Note note, *notep;
 
@@ -548,8 +549,8 @@ runtime_MHeap_Scavenger(void* dummy)
                runtime_notetsleepg(&note, tick);
 
                runtime_lock(h);
-               now = runtime_nanotime();
-               if(now - mstats.last_gc > forcegc) {
+               unixnow = runtime_unixnanotime();
+               if(unixnow - mstats.last_gc > forcegc) {
                        runtime_unlock(h);
                        // The scavenger can not block other goroutines,
                        // otherwise deadlock detector can fire spuriously.
@@ -561,8 +562,8 @@ runtime_MHeap_Scavenger(void* dummy)
                        if(runtime_debug.gctrace > 0)
                                runtime_printf("scvg%d: GC forced\n", k);
                        runtime_lock(h);
-                       now = runtime_nanotime();
                }
+               now = runtime_nanotime();
                scavenge(k, now, limit);
                runtime_unlock(h);
        }
@@ -573,7 +574,7 @@ void runtime_debug_freeOSMemory(void) __asm__("runtime_debug.freeOSMemory");
 void
 runtime_debug_freeOSMemory(void)
 {
-       runtime_gc(1);
+       runtime_gc(2);  // force GC and do eager sweep
        runtime_lock(&runtime_mheap);
        scavenge(-1, ~(uintptr)0, 0);
        runtime_unlock(&runtime_mheap);
@@ -590,6 +591,7 @@ runtime_MSpan_Init(MSpan *span, PageID start, uintptr npages)
        span->freelist = nil;
        span->ref = 0;
        span->sizeclass = 0;
+       span->incache = false;
        span->elemsize = 0;
        span->state = MSpanDead;
        span->unusedsince = 0;
@@ -598,6 +600,7 @@ runtime_MSpan_Init(MSpan *span, PageID start, uintptr npages)
        span->specialLock.key = 0;
        span->specials = nil;
        span->needzero = 0;
+       span->freebuf = nil;
 }
 
 // Initialize an empty doubly-linked list.
@@ -672,6 +675,7 @@ addspecial(void *p, Special *s)
 
        // Ensure that the span is swept.
        // GC accesses specials list w/o locks. And it's just much safer.
+       runtime_m()->locks++;
        runtime_MSpan_EnsureSwept(span);
 
        offset = (uintptr)p - (span->start << PageShift);
@@ -684,6 +688,7 @@ addspecial(void *p, Special *s)
        while((x = *t) != nil) {
                if(offset == x->offset && kind == x->kind) {
                        runtime_unlock(&span->specialLock);
+                       runtime_m()->locks--;
                        return false; // already exists
                }
                if(offset < x->offset || (offset == x->offset && kind < x->kind))
@@ -695,6 +700,7 @@ addspecial(void *p, Special *s)
        s->next = x;
        *t = s;
        runtime_unlock(&span->specialLock);
+       runtime_m()->locks--;
        return true;
 }
 
@@ -714,6 +720,7 @@ removespecial(void *p, byte kind)
 
        // Ensure that the span is swept.
        // GC accesses specials list w/o locks. And it's just much safer.
+       runtime_m()->locks++;
        runtime_MSpan_EnsureSwept(span);
 
        offset = (uintptr)p - (span->start << PageShift);
@@ -726,11 +733,13 @@ removespecial(void *p, byte kind)
                if(offset == s->offset && kind == s->kind) {
                        *t = s->next;
                        runtime_unlock(&span->specialLock);
+                       runtime_m()->locks--;
                        return s;
                }
                t = &s->next;
        }
        runtime_unlock(&span->specialLock);
+       runtime_m()->locks--;
        return nil;
 }
 
@@ -805,7 +814,7 @@ runtime_freespecial(Special *s, void *p, uintptr size, bool freed)
                return false; // don't free p until finalizer is done
        case KindSpecialProfile:
                sp = (SpecialProfile*)s;
-               runtime_MProf_Free(sp->b, p, size, freed);
+               runtime_MProf_Free(sp->b, size, freed);
                runtime_lock(&runtime_mheap.speciallock);
                runtime_FixAlloc_Free(&runtime_mheap.specialprofilealloc, sp);
                runtime_unlock(&runtime_mheap.speciallock);
@@ -823,6 +832,8 @@ runtime_freeallspecials(MSpan *span, void *p, uintptr size)
        Special *s, **t, *list;
        uintptr offset;
 
+       if(span->sweepgen != runtime_mheap.sweepgen)
+               runtime_throw("runtime: freeallspecials: unswept span");
        // first, collect all specials into the list; then, free them
        // this is required to not cause deadlock between span->specialLock and proflock
        list = nil;
@@ -848,3 +859,92 @@ runtime_freeallspecials(MSpan *span, void *p, uintptr size)
                        runtime_throw("can't explicitly free an object with a finalizer");
        }
 }
+
+// Split an allocated span into two equal parts.
+void
+runtime_MHeap_SplitSpan(MHeap *h, MSpan *s)
+{
+       MSpan *t;
+       MCentral *c;
+       uintptr i;
+       uintptr npages;
+       PageID p;
+
+       if(s->state != MSpanInUse)
+               runtime_throw("MHeap_SplitSpan on a free span");
+       if(s->sizeclass != 0 && s->ref != 1)
+               runtime_throw("MHeap_SplitSpan doesn't have an allocated object");
+       npages = s->npages;
+
+       // remove the span from whatever list it is in now
+       if(s->sizeclass > 0) {
+               // must be in h->central[x].empty
+               c = &h->central[s->sizeclass];
+               runtime_lock(c);
+               runtime_MSpanList_Remove(s);
+               runtime_unlock(c);
+               runtime_lock(h);
+       } else {
+               // must be in h->busy/busylarge
+               runtime_lock(h);
+               runtime_MSpanList_Remove(s);
+       }
+       // heap is locked now
+
+       if(npages == 1) {
+               // convert span of 1 PageSize object to a span of 2 PageSize/2 objects.
+               s->ref = 2;
+               s->sizeclass = runtime_SizeToClass(PageSize/2);
+               s->elemsize = PageSize/2;
+       } else {
+               // convert span of n>1 pages into two spans of n/2 pages each.
+               if((s->npages & 1) != 0)
+                       runtime_throw("MHeap_SplitSpan on an odd size span");
+
+               // compute position in h->spans
+               p = s->start;
+               p -= (uintptr)h->arena_start >> PageShift;
+
+               // Allocate a new span for the first half.
+               t = runtime_FixAlloc_Alloc(&h->spanalloc);
+               runtime_MSpan_Init(t, s->start, npages/2);
+               t->limit = (byte*)((t->start + npages/2) << PageShift);
+               t->state = MSpanInUse;
+               t->elemsize = npages << (PageShift - 1);
+               t->sweepgen = s->sweepgen;
+               if(t->elemsize <= MaxSmallSize) {
+                       t->sizeclass = runtime_SizeToClass(t->elemsize);
+                       t->ref = 1;
+               }
+
+               // the old span holds the second half.
+               s->start += npages/2;
+               s->npages = npages/2;
+               s->elemsize = npages << (PageShift - 1);
+               if(s->elemsize <= MaxSmallSize) {
+                       s->sizeclass = runtime_SizeToClass(s->elemsize);
+                       s->ref = 1;
+               }
+
+               // update span lookup table
+               for(i = p; i < p + npages/2; i++)
+                       h->spans[i] = t;
+       }
+
+       // place the span into a new list
+       if(s->sizeclass > 0) {
+               runtime_unlock(h);
+               c = &h->central[s->sizeclass];
+               runtime_lock(c);
+               // swept spans are at the end of the list
+               runtime_MSpanList_InsertBack(&c->empty, s);
+               runtime_unlock(c);
+       } else {
+               // Swept spans are at the end of lists.
+               if(s->npages < nelem(h->free))
+                       runtime_MSpanList_InsertBack(&h->busy[s->npages], s);
+               else
+                       runtime_MSpanList_InsertBack(&h->busylarge, s);
+               runtime_unlock(h);
+       }
+}
index 24f8fe5e3b76d2d0aa3080257cbb3b14348d5246..8bd56bae1cd7e16cd950938197633443074cb392 100644 (file)
@@ -178,68 +178,16 @@ runtime_MProf_GC(void)
        runtime_unlock(&proflock);
 }
 
-static const char*
-typeinfoname(int32 typeinfo)
-{
-       if(typeinfo == TypeInfo_SingleObject)
-               return "single object";
-       else if(typeinfo == TypeInfo_Array)
-               return "array";
-       else if(typeinfo == TypeInfo_Chan)
-               return "channel";
-       // runtime_throw("typinfoname: unknown type info");
-       return "unknown";
-}
-
-static void
-printstackframes(Location *stk, int32 nstk)
-{
-       Location *loc;
-       int32 frame;
-
-       for(frame = 0; frame < nstk; frame++) {
-               loc = &stk[frame];
-               if (loc->function.len > 0) {
-                       runtime_printf("\t#%d %p %S %S:%d\n", frame, loc->pc, loc->function, loc->filename, (int32)loc->lineno);
-               } else {
-                       runtime_printf("\t#%d %p\n", frame, loc->pc);
-               }
-       }
-}
-
-// Called by collector to report a gc in allocfreetrace mode.
-void
-runtime_MProf_TraceGC(void)
-{
-       Location stk[32];
-       int32 nstk;
-
-       nstk = runtime_callers(1, stk, nelem(stk));
-       runtime_printf("MProf_TraceGC\n");
-       printstackframes(stk, nstk);
-}
-
 // Called by malloc to record a profiled block.
 void
-runtime_MProf_Malloc(void *p, uintptr size, uintptr typ)
+runtime_MProf_Malloc(void *p, uintptr size)
 {
        Location stk[32];
        Bucket *b;
-       Type *type;
-       const char *name;
        int32 nstk;
 
        nstk = runtime_callers(1, stk, nelem(stk));
        runtime_lock(&proflock);
-       if(runtime_debug.allocfreetrace) {
-               type = (Type*)(typ & ~3);
-               name = typeinfoname(typ & 3);
-               runtime_printf("MProf_Malloc(p=%p, size=%p, type=%p <%s", p, size, type, name);
-               if(type != nil)
-                       runtime_printf(" of %S", *type->__reflection);
-               runtime_printf(">)\n");
-               printstackframes(stk, nstk);
-       }
        b = stkbucket(MProf, size, stk, nstk, true);
        b->recent_allocs++;
        b->recent_alloc_bytes += size;
@@ -254,7 +202,7 @@ runtime_MProf_Malloc(void *p, uintptr size, uintptr typ)
 
 // Called when freeing a profiled block.
 void
-runtime_MProf_Free(Bucket *b, void *p, uintptr size, bool freed)
+runtime_MProf_Free(Bucket *b, uintptr size, bool freed)
 {
        runtime_lock(&proflock);
        if(freed) {
@@ -264,10 +212,6 @@ runtime_MProf_Free(Bucket *b, void *p, uintptr size, bool freed)
                b->prev_frees++;
                b->prev_free_bytes += size;
        }
-       if(runtime_debug.allocfreetrace) {
-               runtime_printf("MProf_Free(p=%p, size=%p)\n", p, size);
-               printstackframes(b->stk, b->nstk);
-       }
        runtime_unlock(&proflock);
 }
 
@@ -384,6 +328,18 @@ runtime_MProf_Mark(struct Workbuf **wbufp, void (*enqueue1)(struct Workbuf**, Ob
        enqueue1(wbufp, (Obj){(byte*)&bbuckets, sizeof bbuckets, 0});
 }
 
+void
+runtime_iterate_memprof(void (*callback)(Bucket*, uintptr, Location*, uintptr, uintptr, uintptr))
+{
+       Bucket *b;
+
+       runtime_lock(&proflock);
+       for(b=mbuckets; b; b=b->allnext) {
+               callback(b, b->nstk, b->stk, b->size, b->allocs, b->frees);
+       }
+       runtime_unlock(&proflock);
+}
+
 // Must match BlockProfileRecord in debug.go.
 typedef struct BRecord BRecord;
 struct BRecord {
@@ -536,3 +492,73 @@ func GoroutineProfile(b Slice) (n int, ok bool) {
                runtime_starttheworld();
        }
 }
+
+// Tracing of alloc/free/gc.
+
+static Lock tracelock;
+
+static const char*
+typeinfoname(int32 typeinfo)
+{
+       if(typeinfo == TypeInfo_SingleObject)
+               return "single object";
+       else if(typeinfo == TypeInfo_Array)
+               return "array";
+       else if(typeinfo == TypeInfo_Chan)
+               return "channel";
+       runtime_throw("typinfoname: unknown type info");
+       return nil;
+}
+
+void
+runtime_tracealloc(void *p, uintptr size, uintptr typ)
+{
+       const char *name;
+       Type *type;
+
+       runtime_lock(&tracelock);
+       runtime_m()->traceback = 2;
+       type = (Type*)(typ & ~3);
+       name = typeinfoname(typ & 3);
+       if(type == nil)
+               runtime_printf("tracealloc(%p, %p, %s)\n", p, size, name);
+       else    
+               runtime_printf("tracealloc(%p, %p, %s of %S)\n", p, size, name, *type->__reflection);
+       if(runtime_m()->curg == nil || runtime_g() == runtime_m()->curg) {
+               runtime_goroutineheader(runtime_g());
+               runtime_traceback();
+       } else {
+               runtime_goroutineheader(runtime_m()->curg);
+               runtime_traceback();
+       }
+       runtime_printf("\n");
+       runtime_m()->traceback = 0;
+       runtime_unlock(&tracelock);
+}
+
+void
+runtime_tracefree(void *p, uintptr size)
+{
+       runtime_lock(&tracelock);
+       runtime_m()->traceback = 2;
+       runtime_printf("tracefree(%p, %p)\n", p, size);
+       runtime_goroutineheader(runtime_g());
+       runtime_traceback();
+       runtime_printf("\n");
+       runtime_m()->traceback = 0;
+       runtime_unlock(&tracelock);
+}
+
+void
+runtime_tracegc(void)
+{
+       runtime_lock(&tracelock);
+       runtime_m()->traceback = 2;
+       runtime_printf("tracegc()\n");
+       // running on m->g0 stack; show all non-g0 goroutines
+       runtime_tracebackothers(runtime_g());
+       runtime_printf("end tracegc\n");
+       runtime_printf("\n");
+       runtime_m()->traceback = 0;
+       runtime_unlock(&tracelock);
+}
index 15dd58c07bcb70532e50265021ac2b770ec75057..5308e01c8e9f0a946d111b3b282d68440f023cb1 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.
 
-// +build darwin dragonfly freebsd linux netbsd openbsd solaris windows
+// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris windows
 
 package net
 
@@ -63,6 +63,7 @@ struct PollDesc
        G*      wg;     // READY, WAIT, G waiting for write or nil
        Timer   wt;     // write deadline timer
        int64   wd;     // write deadline
+       void*   user;   // user settable cookie
 };
 
 static struct
@@ -88,6 +89,11 @@ static FuncVal deadlineFn    = {(void(*)(void))deadline};
 static FuncVal readDeadlineFn  = {(void(*)(void))readDeadline};
 static FuncVal writeDeadlineFn = {(void(*)(void))writeDeadline};
 
+// runtimeNano returns the current value of the runtime clock in nanoseconds.
+func runtimeNano() (ns int64) {
+       ns = runtime_nanotime();
+}
+
 func runtime_pollServerInit() {
        runtime_netpollinit();
 }
@@ -141,7 +147,7 @@ func runtime_pollWait(pd *PollDesc, mode int) (err int) {
        if(err == 0) {
                // As for now only Solaris uses level-triggered IO.
                if(Solaris)
-                       runtime_netpollarm(pd->fd, mode);
+                       runtime_netpollarm(pd, mode);
                while(!netpollblock(pd, mode, false)) {
                        err = checkerr(pd, mode);
                        if(err != 0)
@@ -256,6 +262,30 @@ runtime_netpollfd(PollDesc *pd)
        return pd->fd;
 }
 
+void**
+runtime_netpolluser(PollDesc *pd)
+{
+       return &pd->user;
+}
+
+bool
+runtime_netpollclosing(PollDesc *pd)
+{
+       return pd->closing;
+}
+
+void
+runtime_netpolllock(PollDesc *pd)
+{
+       runtime_lock(pd);
+}
+
+void
+runtime_netpollunlock(PollDesc *pd)
+{
+       runtime_unlock(pd);
+}
+
 // make pd ready, newly runnable goroutines (if any) are enqueued info gpp list
 void
 runtime_netpollready(G **gpp, PollDesc *pd, int32 mode)
index fe534c9591d061b27870a5d146187535be45e34a..1281f45b085bbc4a36c744f6ff9af543ae58af58 100644 (file)
@@ -117,9 +117,9 @@ runtime_netpollclose(uintptr fd)
 }
 
 void
-runtime_netpollarm(uintptr fd, int32 mode)
+runtime_netpollarm(PollDesc* pd, int32 mode)
 {
-       USED(fd);
+       USED(pd);
        USED(mode);
        runtime_throw("unused");
 }
index bc38644ac78ecfd9593e820a5a9e107d970a065c..5144a870fb2c744f483a731cdc4a6983469c50b8 100644 (file)
@@ -60,9 +60,9 @@ runtime_netpollclose(uintptr fd)
 }
 
 void
-runtime_netpollarm(uintptr fd, int32 mode)
+runtime_netpollarm(PollDesc* pd, int32 mode)
 {
-       USED(fd, mode);
+       USED(pd, mode);
        runtime_throw("unused");
 }
 
index 78d4dd948c6506bb0c32af26f082489a683492ca..6a5d007f03c6b4a8251124ed2a5e1dc9ba929c06 100644 (file)
@@ -154,6 +154,30 @@ runtime_dopanic(int32 unused __attribute__ ((unused)))
        runtime_exit(2);
 }
 
+bool
+runtime_canpanic(G *gp)
+{
+       M *m = runtime_m();
+       byte g;
+
+       USED(&g);  // don't use global g, it points to gsignal
+
+       // Is it okay for gp to panic instead of crashing the program?
+       // Yes, as long as it is running Go code, not runtime code,
+       // and not stuck in a system call.
+       if(gp == nil || gp != m->curg)
+               return false;
+       if(m->locks-m->softfloat != 0 || m->mallocing != 0 || m->throwing != 0 || m->gcing != 0 || m->dying != 0)
+               return false;
+       if(gp->status != Grunning)
+               return false;
+#ifdef GOOS_windows
+       if(m->libcallsp != 0)
+               return false;
+#endif
+       return true;
+}
+
 void
 runtime_throw(const char *s)
 {
@@ -182,6 +206,10 @@ runtime_panicstring(const char *s)
                runtime_printf("panic: %s\n", s);
                runtime_throw("panic during gc");
        }
+       if(runtime_m()->locks) {
+               runtime_printf("panic: %s\n", s);
+               runtime_throw("panic holding locks");
+       }
        runtime_newErrorCString(s, &err);
        runtime_panic(err);
 }
@@ -194,3 +222,9 @@ runtime_Goexit(void)
        rundefer();
        runtime_goexit();
 }
+
+void
+runtime_panicdivide(void)
+{
+       runtime_panicstring("integer divide by zero");
+}
index f602e9a671662e6e12b5540c812260a784d98bb0..ae7e7401b32c53e68dee352242a2e075d8fa1a27 100644 (file)
@@ -91,6 +91,25 @@ runtime_printf(const char *s, ...)
        va_end(va);
 }
 
+int32
+runtime_snprintf(byte *buf, int32 n, const char *s, ...)
+{
+       G *g = runtime_g();
+       va_list va;
+       int32 m;
+
+       g->writebuf = buf;
+       g->writenbuf = n-1;
+       va_start(va, s);
+       go_vprintf(s, va);
+       va_end(va);
+       *g->writebuf = '\0';
+       m = g->writebuf - buf;
+       g->writenbuf = 0;
+       g->writebuf = nil;
+       return m;
+}
+
 // Very simple printf.  Only for debugging prints.
 // Do not add to this without checking with Rob.
 static void
index 8e32f78a2a202468ef4b927b258b9a154fca83d0..363cc191197b63ece9e9e9e9e791aa088444aec6 100644 (file)
@@ -486,6 +486,7 @@ runtime_schedinit(void)
        runtime_sched.maxmcount = 10000;
        runtime_precisestack = 0;
 
+       // runtime_symtabinit();
        runtime_mallocinit();
        mcommoninit(m);
        
@@ -494,6 +495,10 @@ runtime_schedinit(void)
        // in a fault during a garbage collection, it will not
        // need to allocated memory.
        runtime_newErrorCString(0, &i);
+       
+       // Initialize the cached gotraceback value, since
+       // gotraceback calls getenv, which mallocs on Plan 9.
+       runtime_gotraceback(nil);
 
        runtime_goargs();
        runtime_goenvs();
@@ -526,6 +531,15 @@ initDone(void *arg __attribute__ ((unused))) {
 };
 
 // The main goroutine.
+// Note: C frames in general are not copyable during stack growth, for two reasons:
+//   1) We don't know where in a frame to find pointers to other stack locations.
+//   2) There's no guarantee that globals or heap values do not point into the frame.
+//
+// The C frame for runtime.main is copyable, because:
+//   1) There are no pointers to other stack locations in the frame
+//      (d.fn points at a global, d.link is nil, d.argp is -1).
+//   2) The only pointer into this frame is from the defer chain,
+//      which is explicitly handled during stack copying.
 void
 runtime_main(void* dummy __attribute__((unused)))
 {
@@ -1094,6 +1108,22 @@ runtime_allocm(P *p, int32 stacksize, byte** ret_g0_stack, size_t* ret_g0_stacks
        return mp;
 }
 
+static G*
+allocg(void)
+{
+       G *gp;
+       // static Type *gtype;
+       
+       // if(gtype == nil) {
+       //      Eface e;
+       //      runtime_gc_g_ptr(&e);
+       //      gtype = ((PtrType*)e.__type_descriptor)->__element_type;
+       // }
+       // gp = runtime_cnew(gtype);
+       gp = runtime_malloc(sizeof(G));
+       return gp;
+}
+
 static M* lockextra(bool nilokay);
 static void unlockextra(M*);
 
@@ -1587,6 +1617,8 @@ top:
                gcstopm();
                goto top;
        }
+       if(runtime_fingwait && runtime_fingwake && (gp = runtime_wakefing()) != nil)
+               runtime_ready(gp);
        // local runq
        gp = runqget(m->p);
        if(gp)
@@ -1783,6 +1815,8 @@ top:
 void
 runtime_park(bool(*unlockf)(G*, void*), void *lock, const char *reason)
 {
+       if(g->status != Grunning)
+               runtime_throw("bad g status");
        m->waitlock = lock;
        m->waitunlockf = unlockf;
        g->waitreason = reason;
@@ -1834,6 +1868,8 @@ park0(G *gp)
 void
 runtime_gosched(void)
 {
+       if(g->status != Grunning)
+               runtime_throw("bad g status");
        runtime_mcall(runtime_gosched0);
 }
 
@@ -1861,6 +1897,8 @@ runtime_gosched0(G *gp)
 void
 runtime_goexit(void)
 {
+       if(g->status != Grunning)
+               runtime_throw("bad g status");
        if(raceenabled)
                runtime_racegoend();
        runtime_mcall(goexit0);
@@ -1874,6 +1912,13 @@ goexit0(G *gp)
        gp->entry = nil;
        gp->m = nil;
        gp->lockedm = nil;
+       gp->paniconfault = 0;
+       gp->defer = nil; // should be true already but just in case.
+       gp->panic = nil; // non-nil for Goexit during panic. points at stack-allocated data.
+       gp->writenbuf = 0;
+       gp->writebuf = nil;
+       gp->waitreason = nil;
+       gp->param = nil;
        m->curg = nil;
        m->lockedg = nil;
        if(m->locked & ~LockExternal) {
@@ -2122,8 +2167,8 @@ syscall_runtime_BeforeFork(void)
 {
        // Fork can hang if preempted with signals frequently enough (see issue 5517).
        // Ensure that we stay on the same M where we disable profiling.
-       m->locks++;
-       if(m->profilehz != 0)
+       runtime_m()->locks++;
+       if(runtime_m()->profilehz != 0)
                runtime_resetcpuprofiler(0);
 }
 
@@ -2138,7 +2183,7 @@ syscall_runtime_AfterFork(void)
        hz = runtime_sched.profilehz;
        if(hz != 0)
                runtime_resetcpuprofiler(hz);
-       m->locks--;
+       runtime_m()->locks--;
 }
 
 // Allocate a new g, with a stack big enough for stacksize bytes.
@@ -2147,7 +2192,7 @@ runtime_malg(int32 stacksize, byte** ret_stack, size_t* ret_stacksize)
 {
        G *newg;
 
-       newg = runtime_malloc(sizeof(G));
+       newg = allocg();
        if(stacksize >= 0) {
 #if USING_SPLIT_STACK
                int dont_block_signals = 0;
@@ -2204,6 +2249,10 @@ __go_go(void (*fn)(void*), void* arg)
        P *p;
 
 //runtime_printf("newproc1 %p %p narg=%d nret=%d\n", fn->fn, argp, narg, nret);
+       if(fn == nil) {
+               m->throwing = -1;  // do not dump full stacks
+               runtime_throw("go of nil func value");
+       }
        m->locks++;  // disable preemption because it can be holding p in a local var
 
        p = m->p;
@@ -2510,14 +2559,14 @@ runtime_sigprof()
        if(mp == nil)
                return;
 
+       // Profiling runs concurrently with GC, so it must not allocate.
+       mp->mallocing++;
+
        traceback = true;
 
        if(mp->mcache == nil)
                traceback = false;
 
-       // Profiling runs concurrently with GC, so it must not allocate.
-       mp->mallocing++;
-
        runtime_lock(&prof);
        if(prof.fn == nil) {
                runtime_unlock(&prof);
@@ -2765,7 +2814,7 @@ checkdead(void)
        }
        runtime_unlock(&allglock);
        if(grunning == 0)  // possible if main goroutine calls runtime_Goexit()
-               runtime_exit(0);
+               runtime_throw("no goroutines (main called runtime.Goexit) - deadlock!");
        m->throwing = -1;  // do not dump full stacks
        runtime_throw("all goroutines are asleep - deadlock!");
 }
index e84c5d4ec9127a458efd3412bb2ef2e80e2aa4a4..0f4718aa8aaf7d51e8c9394feb0bb62ec06eff3f 100644 (file)
@@ -17,7 +17,6 @@ void  runtime_racefini(void);
 
 void   runtime_racemapshadow(void *addr, uintptr size);
 void   runtime_racemalloc(void *p, uintptr sz);
-void   runtime_racefree(void *p);
 uintptr        runtime_racegostart(void *pc);
 void   runtime_racegoend(void);
 void   runtime_racewritepc(void *addr, void *callpc, void *pc);
index 230e8fa9e9e636eaa3c9f5b6ebaadc203b9314e2..63eb4dd4572ec327a43c8b3e28bd394f023396b0 100644 (file)
@@ -19,3 +19,8 @@ func setGCPercent(in int) (out int) {
 func setMaxThreads(in int) (out int) {
        out = runtime_setmaxthreads(in);
 }
+
+func SetPanicOnFault(enabled bool) (old bool) {
+       old = runtime_g()->paniconfault;
+       runtime_g()->paniconfault = enabled;
+}
index 1678a45d40a5ba38eb1af7f6a085c6970c2eb296..33fe1e7ee600046b47996d6148c6ab302363de69 100644 (file)
@@ -8,8 +8,20 @@
 #include "config.h"
 
 #include "runtime.h"
+#include "arch.h"
 #include "array.h"
 
+enum {
+       maxround = sizeof(uintptr),
+};
+
+// Keep a cached value to make gotraceback fast,
+// since we call it on every call to gentraceback.
+// The cached value is a uint32 in which the low bit
+// is the "crash" setting and the top 31 bits are the
+// gotraceback value.
+static uint32 traceback_cache = ~(uint32)0;
+
 // The GOTRACEBACK environment variable controls the
 // behavior of a Go program that is crashing and exiting.
 //     GOTRACEBACK=0   suppress all tracebacks
@@ -20,18 +32,28 @@ int32
 runtime_gotraceback(bool *crash)
 {
        const byte *p;
+       uint32 x;
 
        if(crash != nil)
                *crash = false;
-       p = runtime_getenv("GOTRACEBACK");
-       if(p == nil || p[0] == '\0')
-               return 1;       // default is on
-       if(runtime_strcmp((const char *)p, "crash") == 0) {
-               if(crash != nil)
-                       *crash = true;
-               return 2;       // extra information
+       if(runtime_m()->traceback != 0)
+               return runtime_m()->traceback;
+       x = runtime_atomicload(&traceback_cache);
+       if(x == ~(uint32)0) {
+               p = runtime_getenv("GOTRACEBACK");
+               if(p == nil)
+                       p = (const byte*)"";
+               if(p[0] == '\0')
+                       x = 1<<1;
+               else if(runtime_strcmp((const char *)p, "crash") == 0)
+                       x = (2<<1) | 1;
+               else
+                       x = runtime_atoi(p)<<1; 
+               runtime_atomicstore(&traceback_cache, x);
        }
-       return runtime_atoi(p);
+       if(crash != nil)
+               *crash = x&1;
+       return x>>1;
 }
 
 static int32   argc;
@@ -90,6 +112,8 @@ runtime_goenvs_unix(void)
        syscall_Envs.__values = (void*)s;
        syscall_Envs.__count = n;
        syscall_Envs.__capacity = n;
+
+       traceback_cache = ~(uint32)0;
 }
 
 int32
@@ -275,6 +299,7 @@ static struct {
        {"allocfreetrace", &runtime_debug.allocfreetrace},
        {"efence", &runtime_debug.efence},
        {"gctrace", &runtime_debug.gctrace},
+       {"gcdead", &runtime_debug.gcdead},
        {"scheddetail", &runtime_debug.scheddetail},
        {"schedtrace", &runtime_debug.schedtrace},
 };
index 6bd53a852b40709d58cca59fbd94cb72b7a326ae..8fc10ff81f158c024c75ab6c48410ed93fd72f4e 100644 (file)
@@ -49,6 +49,8 @@ typedef unsigned int uintptr __attribute__ ((mode (pointer)));
 typedef intptr         intgo; // Go's int
 typedef uintptr                uintgo; // Go's uint
 
+typedef uintptr                uintreg;
+
 /* Defined types.  */
 
 typedef        uint8                   bool;
@@ -216,6 +218,7 @@ struct      G
        bool    ispanic;
        bool    issystem;       // do not output in stack dump
        bool    isbackground;   // ignore in deadlock detector
+       bool    paniconfault;   // panic (instead of crash) on unexpected fault address
        M*      m;              // for debuggers, but offset not hard-coded
        M*      lockedm;
        int32   sig;
@@ -251,6 +254,7 @@ struct      M
        int32   throwing;
        int32   gcing;
        int32   locks;
+       int32   softfloat;
        int32   dying;
        int32   profilehz;
        int32   helpgc;
@@ -272,15 +276,11 @@ struct    M
        uint32  waitsemacount;
        uint32  waitsemalock;
        GCStats gcstats;
-       bool    racecall;
        bool    needextram;
        bool    dropextram;     // for gccgo: drop after call is done.
+       uint8   traceback;
        bool    (*waitunlockf)(G*, void*);
        void*   waitlock;
-
-       uintptr settype_buf[1024];
-       uintptr settype_bufsize;
-
        uintptr end[];
 };
 
@@ -340,6 +340,7 @@ enum
        SigDefault = 1<<4,      // if the signal isn't explicitly requested, don't monitor it
        SigHandling = 1<<5,     // our signal handler is registered
        SigIgnored = 1<<6,      // the signal was ignored before we registered for it
+       SigGoExit = 1<<7,       // cause all runtime procs to exit (only used on Plan 9).
 };
 
 // Layout of in-memory per-function information prepared by linker
@@ -352,6 +353,16 @@ struct     Func
        uintptr entry;  // entry pc
 };
 
+#ifdef GOOS_nacl
+enum {
+   NaCl = 1,
+};
+#else
+enum {
+   NaCl = 0,
+};
+#endif
+
 #ifdef GOOS_windows
 enum {
    Windows = 1
@@ -385,6 +396,8 @@ struct      Timers
 
 // Package time knows the layout of this structure.
 // If this struct changes, adjust ../time/sleep.go:/runtimeTimer.
+// For GOOS=nacl, package syscall knows the layout of this structure.
+// If this struct changes, adjust ../syscall/net_nacl.go:/runtimeTimer.
 struct Timer
 {
        int32   i;      // heap index
@@ -441,11 +454,13 @@ struct DebugVars
        int32   allocfreetrace;
        int32   efence;
        int32   gctrace;
+       int32   gcdead;
        int32   scheddetail;
        int32   schedtrace;
 };
 
 extern bool runtime_precisestack;
+extern bool runtime_copystack;
 
 /*
  * defined macros
@@ -490,6 +505,7 @@ extern      uint32  runtime_panicking;
 extern int8*   runtime_goos;
 extern int32   runtime_ncpu;
 extern         void    (*runtime_sysargs)(int32, uint8**);
+extern uint32  runtime_Hchansize;
 extern DebugVars       runtime_debug;
 extern uintptr runtime_maxstacksize;
 
@@ -497,6 +513,7 @@ extern      uintptr runtime_maxstacksize;
  * common functions and data
  */
 #define runtime_strcmp(s1, s2) __builtin_strcmp((s1), (s2))
+#define runtime_strncmp(s1, s2, n) __builtin_strncmp((s1), (s2), (n))
 #define runtime_strstr(s1, s2) __builtin_strstr((s1), (s2))
 intgo  runtime_findnull(const byte*);
 intgo  runtime_findnullw(const uint16*);
@@ -511,8 +528,10 @@ void       runtime_goenvs(void);
 void   runtime_goenvs_unix(void);
 void   runtime_throw(const char*) __attribute__ ((noreturn));
 void   runtime_panicstring(const char*) __attribute__ ((noreturn));
+bool   runtime_canpanic(G*);
 void   runtime_prints(const char*);
 void   runtime_printf(const char*, ...);
+int32  runtime_snprintf(byte*, int32, const char*, ...);
 #define runtime_mcmp(a, b, s) __builtin_memcmp((a), (b), (s))
 #define runtime_memmove(a, b, s) __builtin_memmove((a), (b), (s))
 void*  runtime_mal(uintptr);
@@ -552,6 +571,7 @@ int32       runtime_gcount(void);
 void   runtime_mcall(void(*)(G*));
 uint32 runtime_fastrand1(void);
 int32  runtime_timediv(int64, int32, int32*);
+int32  runtime_round2(int32 x); // round x up to a power of 2.
 
 // atomic operations
 #define runtime_cas(pval, old, new) __sync_bool_compare_and_swap (pval, old, new)
@@ -590,7 +610,8 @@ G*  __go_go(void (*pfn)(void*), void*);
 void   siginit(void);
 bool   __go_sigsend(int32 sig);
 int32  runtime_callers(int32, Location*, int32);
-int64  runtime_nanotime(void);
+int64  runtime_nanotime(void); // monotonic time
+int64  runtime_unixnanotime(void); // real time, can skip
 void   runtime_dopanic(int32) __attribute__ ((noreturn));
 void   runtime_startpanic(void);
 void   runtime_freezetheworld(void);
@@ -611,12 +632,18 @@ int32     runtime_netpollopen(uintptr, PollDesc*);
 int32   runtime_netpollclose(uintptr);
 void   runtime_netpollready(G**, PollDesc*, int32);
 uintptr        runtime_netpollfd(PollDesc*);
-void   runtime_netpollarm(uintptr, int32);
+void   runtime_netpollarm(PollDesc*, int32);
+void** runtime_netpolluser(PollDesc*);
+bool   runtime_netpollclosing(PollDesc*);
+void   runtime_netpolllock(PollDesc*);
+void   runtime_netpollunlock(PollDesc*);
 void   runtime_crash(void);
 void   runtime_parsedebugvars(void);
 void   _rt0_go(void);
 void*  runtime_funcdata(Func*, int32);
 int32  runtime_setmaxthreads(int32);
+G*     runtime_timejump(void);
+void   runtime_iterate_finq(void (*callback)(FuncVal*, void*, const FuncType*, const PtrType*));
 
 void   runtime_stoptheworld(void);
 void   runtime_starttheworld(void);
@@ -814,3 +841,12 @@ bool       runtime_gcwaiting(void);
 void   runtime_badsignal(int);
 Defer* runtime_newdefer(void);
 void   runtime_freedefer(Defer*);
+
+struct time_now_ret
+{
+  int64_t sec;
+  int32_t nsec;
+};
+
+struct time_now_ret now() __asm__ (GOSYM_PREFIX "time.now")
+  __attribute__ ((no_split_stack));
index 13ce41fc8e0daf7f252f76a49813a61fabb685f5..220629be496fc79de428489e5687057225988764 100644 (file)
@@ -6,6 +6,8 @@
 
 package time
 
+#include <sys/time.h>
+
 #include "runtime.h"
 #include "defs.h"
 #include "arch.h"
@@ -20,11 +22,19 @@ static Timers timers;
 static void addtimer(Timer*);
 static void dumptimers(const char*);
 
+// nacl fake time support. 
+int64 runtime_timens;
+
 // Package time APIs.
 // Godoc uses the comments in package time, not these.
 
 // time.now is implemented in assembly.
 
+// runtimeNano returns the current value of the runtime clock in nanoseconds.
+func runtimeNano() (ns int64) {
+       ns = runtime_nanotime();
+}
+
 // Sleep puts the current goroutine to sleep for at least ns nanoseconds.
 func Sleep(ns int64) {
        runtime_tsleep(ns, "sleep");
@@ -45,6 +55,14 @@ func stopTimer(t *Timer) (stopped bool) {
 
 // C runtime.
 
+int64 runtime_unixnanotime(void)
+{
+       struct time_now_ret r;
+
+       r = now();
+       return r.sec*1000000000 + r.nsec;
+}
+
 static void timerproc(void*);
 static void siftup(int32);
 static void siftdown(int32);
@@ -187,6 +205,7 @@ timerproc(void* dummy __attribute__ ((unused)))
 {
        int64 delta, now;
        Timer *t;
+       FuncVal *fv;
        void (*f)(int64, Eface);
        Eface arg;
 
@@ -214,12 +233,13 @@ timerproc(void* dummy __attribute__ ((unused)))
                                siftdown(0);
                                t->i = -1;  // mark as removed
                        }
+                       fv = t->fv;
                        f = (void*)t->fv->fn;
                        arg = t->arg;
                        runtime_unlock(&timers);
                        if(raceenabled)
                                runtime_raceacquire(t);
-                       __go_set_closure(t->fv);
+                       __go_set_closure(fv);
                        f(now, arg);
 
                        // clear f and arg to avoid leak while sleeping for next timer
@@ -234,7 +254,9 @@ timerproc(void* dummy __attribute__ ((unused)))
                if(delta < 0) {
                        // No timers left - put goroutine to sleep.
                        timers.rescheduling = true;
+                       runtime_g()->isbackground = true;
                        runtime_parkunlock(&timers, "timer goroutine (idle)");
+                       runtime_g()->isbackground = false;
                        continue;
                }
                // At least one timer pending.  Sleep until then.